diff --git a/docs/28-servidor-de-produccion.md b/docs/28-servidor-de-produccion.md index 07bd7032..ee997f71 100644 --- a/docs/28-servidor-de-produccion.md +++ b/docs/28-servidor-de-produccion.md @@ -1733,6 +1733,51 @@ eligió es peor que una sin hora, porque nadie lo mira. consistente, o sea que el respaldo habría copiado la sqlite a lo bruto. *Un paquete declarado no es un paquete instalado: en una caja viva, lo que vale es lo que el `upgrade` proyectó.* +### 6.30 ⚰️ Tres servicios que MUEREN — y el cortafuegos que no puede correr todavía *(2026-09-15)* + +Decisión del usuario sobre lo que quedaba del censo, y las tres son «no se muda»: + +| servicio | por qué muere | +|---|---| +| **`qdrant`** (`:6333`, `:6334`) | es la base vectorial de `gioser_api`, y **ese backend es un fósil**: `api.gioser.net` da 502 permanente desde el §6.9. Una base sin consumidor no se muda. La receta `recipes/qdrant.toml` queda en el catálogo —**catálogo ≠ imagen**, y eso no es deuda—, pero no entra en ningún perfil. | +| **`act_runner`** (`:41027`) | el runner de CI del gitea, un binario suelto en `~/.local/bin` que nadie provee. Muere con la caja; si mañana hace falta CI, entra por receta y no por un binario copiado. | +| **`fail2ban-server`** | **tawasuyu ya tiene el sustituto**, y es mejor: `shared/cortafuegos` (§1 de su SDD-ENTRADA) no mira logs con un daemon, usa **dos sets dinámicos del kernel** (`ban4`/`ban6`, `flags dynamic`, `timeout`) con `limit rate over N/minute` **por IP de origen**. El ban lo aplica nftables sin proceso y sin latencia; fail2ban es un daemon de Python leyendo ficheros para hacer lo mismo peor. | + +#### 🧨 Pero el sustituto NO PUEDE CORRER: el kernel de la caja no trae `nf_tables` + + $ nft list ruleset + netlink: Error: cache initialization failed: Invalid argument + +`nft 1.1.6` **está instalado** (`nftables` es raíz de `perfil.servidor` y está sellado). Lo que falta +está un piso más abajo, en el `.config` que el artefacto publica: + + CONFIG_NETFILTER=y ← sí + CONFIG_NF_CONNTRACK=y ← sí + # CONFIG_NF_TABLES is not set ← AQUÍ + # CONFIG_NETFILTER_ADVANCED is not set ← y ésta es la causa: NF_TABLES cuelga de ella + +Es otra vez la lección del §6.4 muro 1: **la receta dice lo que se CAMBIÓ; sólo el `.config` sellado +dice lo que QUEDÓ**. `linux-generic` sale de un `make defconfig`, y defconfig deja `NETFILTER_ADVANCED` +apagado, que arrastra a `NF_TABLES` con él. + +⇒ **Hoy la caja no tiene cortafuegos de ninguna clase**: ni fail2ban (que no se muda), ni el +sustituto (que no arranca), ni una regla. Con `:22022`, `:2345`, `:1137`, `:80` y `:443` públicos, +eso hay que decirlo entero y no dejarlo implícito. La cura es un kernel con +`CONFIG_NETFILTER_ADVANCED=y` + `NF_TABLES` (y `NFT_CT`, `NFT_LIMIT`, `NFT_COUNTER`, `NFT_LOG`), +que es trabajo del SDD 22 y termina en un **reinicio de la caja de producción**. + +#### La mitigación que NO depende del kernel, hecha hoy + +El `sshd` de la caja ofrecía `password` y `keyboard-interactive` — su config eran **cuatro líneas** +y ninguna decía nada de autenticación, así que regía el default de OpenSSH. Sin cortafuegos y sin +fail2ban, eso es exactamente la puerta que aquéllos cerraban. Ahora es **sólo claves**, con los +cuatro controles corridos: la clave entra ✅, `:22022` contesta `Permission denied (publickey)` sin +ofrecer contraseña ✅, `:2345` igual ✅, y el `git ls-remote` por SSH sigue funcionando ✅. + +**Lo que enseña:** *una defensa que se muda tiene que mudarse con su capacidad, no con su nombre*. +Decidir «fail2ban muere porque tenemos algo mejor» es correcto **y** deja un hueco abierto hasta que +ese algo mejor pueda ejecutarse. Entre la decisión y la capacidad hay un kernel de por medio. + ## 7. Reusar los scripts que ya existen, y no escribir de nuevo Pedido explícito del usuario. El inventario de lo que ya hace el trabajo: