`table inet tawasuyu_input` con policy drop, 15 reglas de puerto, y el set `ban4` llenandose solo: a los pocos minutos, 27 IPs baneadas y paquetes tirados por el contador. El reemplazo de fail2ban funcionando sobre trafico real, dentro del kernel y sin daemon. 🧨 EL PRIMER BANEADO FUI YO, EN SEGUNDOS. Aplicada la primera version: ssh y https bien, y `http`, proxy y git-ssh muertos; al minuto la caja entera dejo de contestarme. La causa no es una regla mal escrita: **el set @ban4 es UNO SOLO para todos los servicios** y `ip saddr @ban4 drop` esta antes que todo, asi que pasarse de tasa en UN puerto te tira en TODOS — y el `nuevas_por_min: 5` del puerto de administracion es exactamente lo que me pasa a mi, porque cada comando remoto es una conexion nueva. Lo devolvio el INTERRUPTOR DE HOMBRE MUERTO (un `nft flush ruleset` programado a 180 s que sólo se desarma si la verificacion externa sale bien). Sin consola serie, esa es la diferencia entre un susto y un rescue. ⚠ Y el tipo ya lo avisaba: `PoliticaEntrada` tiene un campo `confiables` cuyo comentario describe palabra por palabra lo que me paso. Copie el .ron de EJEMPLO en vez de leer el TIPO. La cura fue una linea con el hub y el worker, que se aceptan ANTES de la logica de tasa. ⚠ `nft -c` rechazo 5 reglas antes de aplicar nada: `ct count over N` es CONFIG_NFT_CONNLIMIT y este kernel no lo trae. Quedan COMENTADAS y no borradas en el fichero que se aplica — lo que la politica declara y el kernel no puede tiene que verse. El simbolo ya entro a la receta del kernel. Y para que sobreviva al reinicio, `nftables` declara su [[service]] con `lifecycle = "oneshot"`: cargar un reglaset no es un daemon, es un acto. Va PRIMERO en el genesis — entre que la red esta y que las reglas cargan, la caja esta abierta. Probado con `nft flush ruleset` + `arjectl start`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
88 lines
5.5 KiB
TOML
88 lines
5.5 KiB
TOML
# nftables 1.1.6 — el `nft` con el que se aplica el reglaset del cortafuegos. Es la pieza que el
|
|
# grafo pedía como `wanted` desde antes de este frente, y la que destraba el §6.5 del SDD 26: el
|
|
# «modo foco» de `atuq` lo sostiene el sistema (una regla de egress por cgroup) y no una extensión
|
|
# que el propio navegador puede apagar.
|
|
#
|
|
# ⚠ LA VERSIÓN ES UNA DECISIÓN, NO UN DEFAULT. 1.1.7 ya está publicada (2026-09-01), pero el SDD del
|
|
# cortafuegos de tawasuyu dice que sus expresiones —en particular `socket cgroupv2 level N`— están
|
|
# «verificadas en metal contra nftables 1.1.6». Cambiar de versión cambia qué acepta `nft -c`, así
|
|
# que se pinea la que la otra mitad ya midió; subirla es su propia unidad de trabajo, con la
|
|
# revalidación incluida.
|
|
#
|
|
# `--without-cli`: el modo interactivo pide readline o editline, y acá nadie escribe reglas a mano
|
|
# — las genera `cortafuegos-core` y se aplican con `nft -f`. `--disable-man-doc` porque el manual
|
|
# sale de asciidoc/docbook, que no están en el corpus.
|
|
#
|
|
# Sin bison ni flex en `[deps]`: el tarball de release YA trae `src/parser_bison.c` y `src/scanner.c`
|
|
# generados (comprobado en el tarball pineado). El árbol de git no, y ésa es otra de las razones por
|
|
# las que esto entra por tarball.
|
|
#
|
|
# EL PARCHE ARREGLA DOS COSAS QUE VIENEN DE FÁBRICA, y las dos se midieron acá:
|
|
#
|
|
# 1. **un bashismo en `config.status`** — `configure.ac` emite `for ((i = 56; i >= 0; i-= 8))` para
|
|
# escribir `nftversion.h`, y el `/bin/sh` del lab es busybox: el build murió con
|
|
# `config.status: line 2041: syntax error: bad for loop variable`. Se reemplaza por un `for i in
|
|
# 56 48 ... 0` POSIX, que produce la MISMA secuencia. Se podría haber pasado `CONFIG_SHELL=bash`
|
|
# —el lab trae bash— pero eso mete una dependencia INVISIBLE: el lab no entra en `hash_inputs`
|
|
# (ADR pendiente), así que el día que un lab sin bash construya esta receta, el fallo vuelve sin
|
|
# que nada haya cambiado en el corpus. El parche sí entra en el hash.
|
|
#
|
|
# 2. **un sello de tiempo dentro del binario** — `MAKE_STAMP` es literalmente
|
|
# `$(shell date +%s)`, y `nftversion.h` lo mete byte a byte en el ejecutable. Eso es la familia
|
|
# exacta del `BuildID` de waterfox: dos builds de la misma fuente NO reproducen. nftables no mira
|
|
# `SOURCE_DATE_EPOCH`. Se fija en `0`, que además es lo correcto acá: el sello sólo se usa
|
|
# (`src/netlink.c:version_cmp`) para comparar qué `nft` creó una tabla, y en una distro
|
|
# reproducible dos builds de la MISMA versión son el mismo `nft`.
|
|
#
|
|
# `link = "dynamic"` porque no hay alternativa honesta: nft necesita libgmp y `recipes/gmp.toml`
|
|
# publica sólo `.so` (`--disable-static`). Un `link = "static"` acá sellaría un binario dinámico y
|
|
# la receta estaría mintiendo — la familia de fallos del `link=static` por libtool.
|
|
name = "nftables"
|
|
version = "1.1.6"
|
|
license = "GPL-2.0-only"
|
|
|
|
[source]
|
|
tarball = "https://www.netfilter.org/pub/nftables/nftables-1.1.6.tar.xz"
|
|
sha256 = "372931bda8556b310636a2f9020adc710f9bab66f47efe0ce90bff800ac2530c"
|
|
patches = ["nftables-sin-bashismo-ni-sello.patch"]
|
|
|
|
[build]
|
|
compiler = "zig-cc"
|
|
target = "x86_64-linux-musl"
|
|
link = "dynamic"
|
|
flags = []
|
|
|
|
[build.phases]
|
|
configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --sbindir=/usr/sbin --disable-static --enable-shared --without-cli --disable-man-doc'
|
|
compile = 'make -j"$(nproc)"'
|
|
install = "make install DESTDIR=/out && find /out -name '*.la' -delete"
|
|
|
|
[deps]
|
|
build = ["binutils", "busybox", "make", "pkgconf", "libmnl", "libnftnl", "gmp"]
|
|
|
|
# ── EL SERVICIO QUE ESTE PAQUETE TRAE (SDD 30) ──────────────────────────────────────────────────
|
|
# Fuera de `hash_inputs`: declarar esto NO re-hashea nftables.
|
|
#
|
|
# `Oneshot`: cargar un reglaset no es un daemon, es un acto. Se aplica al arrancar y termina; si
|
|
# vuelve a hacer falta, se vuelve a correr (`arjectl start cortafuegos`).
|
|
#
|
|
# ⚠ EL REGLASET ES DATO DEL SITIO y por eso NO viene en el paquete: qué puertos expone una máquina
|
|
# y a quién no banea nunca es una decisión de quien la administra. Se genera con el cortafuegos de
|
|
# tawasuyu (`cortafuegos generate-input --policy <.ron>`) y se deja en `/etc/takana/`. El servicio
|
|
# sale 78 nombrando el fichero en vez de dejar la caja ABIERTA creyendo que está protegida — que es
|
|
# el modo de fallo peligroso: un cortafuegos ausente y un cortafuegos vacío se ven igual desde
|
|
# afuera hasta que alguien prueba.
|
|
#
|
|
# ⚠⚠ Y la lección que costó la primera aplicación: el set de baneados es UNO SOLO para todos los
|
|
# servicios, así que pasarse de tasa en el puerto de ssh te tira TAMBIÉN el https, el proxy y el
|
|
# git. Sin `confiables` en la política, el primer baneado es el operador. Ver SDD 28 §6.32.
|
|
[[service]]
|
|
label = "cortafuegos"
|
|
id = "01M2EKDA00NFT4B135C0RTAF9G"
|
|
exec = "/bin/busybox"
|
|
argv = ["sh", "-c", "test -f /etc/takana/cortafuegos-entrada.nft || { echo 'cortafuegos: falta /etc/takana/cortafuegos-entrada.nft — el reglaset es dato del SITIO, se genera con `cortafuegos generate-input`. La caja arranca SIN filtrar y eso no se hace en silencio' >&2; exit 78; }; /usr/sbin/nft -c -f /etc/takana/cortafuegos-entrada.nft || { echo 'cortafuegos: el kernel RECHAZA el reglaset (¿falta alguna opción de nf_tables?) — no aplico nada' >&2; exit 78; }; exec /usr/sbin/nft -f /etc/takana/cortafuegos-entrada.nft"]
|
|
envp = [["PATH", "/usr/sbin:/usr/bin:/sbin:/bin"]]
|
|
networking = "full"
|
|
cgroup = "arje.slice/cortafuegos"
|
|
lifecycle = "oneshot"
|