From 4753a991f1f2f28533b33bce0f31e4928f8bcc69 Mon Sep 17 00:00:00 2001 From: Sergio Date: Tue, 15 Sep 2026 20:28:29 +0000 Subject: [PATCH] =?UTF-8?q?qdrant,=20act=5Frunner=20y=20fail2ban=20MUEREN?= =?UTF-8?q?=20=E2=80=94=20y=20el=20sustituto=20del=20=C3=BAltimo=20no=20pu?= =?UTF-8?q?ede=20correr=20todav=C3=ADa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decisión del usuario sobre lo que quedaba del censo: · `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 queda en el catálogo, que no es lo mismo que la imagen, y eso NO es deuda. · `act_runner` (:41027) — runner de CI, binario suelto que nadie provee. Si mañana hace falta CI, entra por receta y no por un binario copiado. · `fail2ban` — **tawasuyu ya tiene el sustituto y es mejor**: `shared/cortafuegos` (SDD-ENTRADA §1) 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. 🧨 PERO EL SUSTITUTO NO ARRANCA: `nft list ruleset` da «cache initialization failed: Invalid argument». `nft 1.1.6` está instalado y sellado; lo que falta está un piso más abajo, en el `.config` que publica el artefacto del kernel: CONFIG_NETFILTER=y · CONFIG_NF_CONNTRACK=y · **# CONFIG_NF_TABLES is not set** y la causa: **# CONFIG_NETFILTER_ADVANCED is not set** (NF_TABLES cuelga de ella) Es la lección del §6.4 muro 1 otra vez: la receta dice lo que se CAMBIÓ; sólo el `.config` sellado dice lo que QUEDÓ. `linux-generic` sale de `make defconfig` y defconfig apaga NETFILTER_ADVANCED. ⇒ HOY LA CAJA NO TIENE CORTAFUEGOS DE NINGUNA CLASE, con :22022, :2345, :1137, :80 y :443 públicos. Hay que decirlo entero. La cura es un kernel con NETFILTER_ADVANCED + NF_TABLES (trabajo del SDD 22) y termina en un reinicio de producción. MITIGACIÓN HECHA HOY, que no depende del kernel: el `sshd` ofrecía `password` y `keyboard-interactive` —su config eran CUATRO líneas y ninguna hablaba de autenticación, así que regía el default de OpenSSH— que es justo la puerta que fail2ban cerraba. Ahora es sólo claves, con cuatro controles: la clave entra, :22022 y :2345 contestan `Permission denied (publickey)` sin ofrecer contraseña, y el `git ls-remote` por SSH sigue andando. La lección: una defensa se muda 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. Co-Authored-By: Claude Opus 5 (1M context) --- docs/28-servidor-de-produccion.md | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) 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: