Ubuntu 24.04: Fail2ban no está bloqueando nada — flujo rápido de verificación

¿Te fue útil?

Instalaste Fail2ban. Activaste una jail para SSH. Incluso viste a alguien martillar el puerto 22 como si le debieran dinero. Y aún así: nadie es bloqueado. Ninguna entrada en el firewall. Ningún contador “banned” satisfactorio. Solo silencio.

En producción, el silencio rara vez es tranquilidad. Suele ser telemetría ausente, un backend equivocado, la familia de firewall incorrecta o una jail que nunca hizo match con una línea. La solución no es “reinstalar Fail2ban” (así pierdes una tarde). La solución es la verificación: demuestra la ruta de datos, demuestra el match, demuestra el baneo y demuestra que el baneo es aplicable.

Guía rápida de diagnóstico

Si estás de guardia, empieza aquí. Esta es la secuencia “encuentra el cuello de botella en cinco minutos”. Es opinada porque la alternativa es navegar por archivos de configuración mientras los ataques continúan.

Primero: ¿está Fail2ban vivo y observando la jail que crees?

  • Comprueba la salud del servicio y errores recientes en el journal.
  • Lista las jails habilitadas y verifica que la jail objetivo esté presente.

Segundo: ¿está viendo eventos (logs) y haciendo coincidencias (filtros)?

  • Confirma el backend (journal de systemd vs archivos de log).
  • Confirma que la fuente de logs contiene realmente las líneas de fallo que esperas.
  • Ejecuta una prueba regex contra líneas de log conocidas.

Tercero: ¿está intentando banear y los baneos se aplican?

  • Banea manualmente una IP de prueba y observa si aparecen reglas en el firewall.
  • Valida si estás en nftables o iptables y que Fail2ban esté configurado en consecuencia.
  • Valida la acción seleccionada en la jail y que la “familia” del firewall coincida con la realidad del sistema.

El modo de fallo más común en Ubuntu moderno no es Fail2ban en sí. Es una discordancia entre dónde viven los logs (systemd-journald vs archivos) y qué capa de firewall está activa (nftables vs iptables-legacy). Fail2ban no puede banear lo que no puede ver, y no puede bloquear lo que no puede programar.

Modelo mental: eventos → coincidencias → baneos → aplicación

Fail2ban es simple del mismo modo que un detector de humo es simple. Escucha. Reconoce un patrón. Toma una acción. Si lo pones en la habitación equivocada, o las pilas están muertas, “funciona” con una calma impresionante.

Piénsalo en cuatro capas:

  1. Eventos: fallos de autenticación aparecen en algún lugar (un archivo como /var/log/auth.log o el journal de systemd).
  2. Coincidencias: el regex del filtro de una jail coincide con esas líneas de evento y cuenta fallos por IP durante una ventana.
  3. Baneos: cuando se superan los umbrales (findtime/maxretry), Fail2ban decide banear una IP.
  4. Aplicación: la acción de baneo inserta reglas en un firewall (nftables o iptables) o indica a un servicio (como una ACL de una app) que bloquee.

Cuando Fail2ban “no está bloqueando nada”, no adivines qué capa falló. Pruébala. En la práctica:

  • No hay eventos: fuente de logs errónea, permisos, o el servicio registra en otro sitio (logs de contenedor, syslog deshabilitado, etc.).
  • Eventos pero sin coincidencias: filtro incorrecto, jail equivocada, formato de log distinto, o tu servicio no produce las cadenas de fallo que el filtro espera.
  • Coincidencias pero sin baneos: umbrales demasiado altos, ignoreip captura todo, configuraciones de tiempo incorrectas, o la jail no está habilitada.
  • Baneos pero sin aplicación: acción equivocada, backend de firewall erróneo, conflicto con el gestor de firewall, o el orden de reglas hace que tu “bloqueo” nunca bloquee.

Idea parafraseada (Gene Kim): “La fiabilidad viene de retroalimentación rápida y cambios pequeños y reversibles.” Ese es el propósito de este flujo: bucles de retroalimentación estrechos que te permiten dejar de adivinar.

Datos interesantes y contexto (rápido, útil, a veces nerd)

  • Fail2ban precede a la era cloud. Comenzó a principios de los 2000, cuando las fuerza bruta contra SSH y el password spraying en FTP eran el ruido de fondo diario de Internet.
  • No “detecta atacantes”. Detecta patrones en logs. Si un atacante evita generar los logs que esperas (o golpea un endpoint que no monitorizas), Fail2ban permanece cortésmente ignorante.
  • El objetivo por defecto original fue iptables. El ecosistema avanzó hacia nftables; hoy en día los firewalls Linux suelen ejecutarse mediante nftables incluso cuando escribes “iptables”.
  • systemd-journald cambió el juego de logs. En algunas configuraciones, /var/log/auth.log está presente; en otras, el journal es la fuente canónica. Fail2ban puede leer ambos, pero debes elegir correctamente.
  • “iptables” puede significar varias cosas. Existe iptables-legacy e iptables-nft. El nombre del comando es el mismo, el backend difiere, y las reglas pueden acabar en un lugar que no estás observando.
  • UFW es una capa de política. Históricamente usó iptables debajo; en sistemas más nuevos puede integrarse con nftables. Fail2ban puede coexistir, pero el orden importa.
  • IPv6 es la trampa silenciosa. Si tu servicio es accesible vía IPv6 y solo baneas IPv4, tu “baneo” será un simple obstáculo, no un bloqueo.
  • DNS y búsquedas inversas pueden retrasar los baneos. Si habilitas funciones que provocan operaciones DNS lentas dentro de la tubería de baneo, puedes crear comportamientos extraños del tipo “está baneando horas después”.

Broma #1: Fail2ban no “no funciona”. Funciona exactamente como está configurado, lo cual es una frase mucho menos reconfortante a las 03:00.

Flujo de verificación: tareas prácticas con comandos, significado, decisiones

Este es el núcleo del artículo: comprobaciones reales, en un orden sensato, con “qué significa” y “qué haces después”. Ejecútalas en el host donde está instalado Fail2ban.

Tarea 1 — Confirma que Fail2ban está realmente en ejecución (y no reiniciándose)

cr0x@server:~$ systemctl status fail2ban --no-pager
● fail2ban.service - Fail2Ban Service
     Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; preset: enabled)
     Active: active (running) since Sun 2025-12-28 09:12:19 UTC; 1h 7min ago
       Docs: man:fail2ban(1)
   Main PID: 1247 (fail2ban-server)
      Tasks: 5 (limit: 19020)
     Memory: 39.8M
        CPU: 1.421s
     CGroup: /system.slice/fail2ban.service
             └─1247 /usr/bin/python3 /usr/bin/fail2ban-server -xf start

Qué significa: “active (running)” es imprescindible. Si aparece “failed” o reiniciándose, los baneos no se mantendrán y las jails pueden no cargarse.

Decisión: Si no está estable, ve directamente a la Tarea 2 (errores del journal) antes de tocar la configuración.

Tarea 2 — Lee los últimos errores/advertencias de Fail2ban

cr0x@server:~$ journalctl -u fail2ban -n 200 --no-pager
Dec 28 09:12:19 server fail2ban-server[1247]: Server ready
Dec 28 10:01:07 server fail2ban-server[1247]: WARNING Found no accessible config files for 'filter.d/sshd'. Skipping...
Dec 28 10:01:07 server fail2ban-server[1247]: ERROR  Unable to read the filter 'sshd'

Qué significa: Esto es Fail2ban diciéndote que no puede cargar un filtro, no puede abrir un log, no puede hablar con el firewall o no puede enlazar su socket. Créelo.

Decisión: Arregla el archivo/permiso reportado primero. No “ajustes bantime” mientras no pueda cargar la jail.

Tarea 3 — Lista las jails en ejecución (las que Fail2ban realmente cargó)

cr0x@server:~$ sudo fail2ban-client status
Status
|- Number of jail:	2
`- Jail list:	sshd, nginx-http-auth

Qué significa: Si la jail esperada no aparece listada, no está en ejecución. Punto.

Decisión: Si falta una jail, valida la configuración en la Tarea 4 y revisa la habilitación en el archivo de jail. Sin jail, no hay baneo.

Tarea 4 — Valida el parseo de la configuración sin empezar una sesión de adivinanzas

cr0x@server:~$ sudo fail2ban-client -d
...snip...
Jail sshd: backend = systemd
Jail sshd: enabled = true
Jail sshd: maxretry = 5
Jail sshd: findtime = 600
Jail sshd: bantime = 3600
Jail sshd: action = nftables[type=multiport]
...snip...

Qué significa: -d vuelca la configuración computada después de incluir archivos. Aquí detectas “edité jail.conf pero jail.local lo sobrescribe” y otros clásicos.

Decisión: Si el backend/acción/rutas no son lo que esperas, detente y corrige los overrides. No depures la realidad equivocada.

Tarea 5 — Inspecciona el estado de una jail específica (es tu marcador)

cr0x@server:~$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:	1
|  |- Total failed:	44
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	0
   |- Total banned:	0
   `- Banned IP list:

Qué significa: “Total failed” en aumento significa que está leyendo logs y haciendo match. “Total banned” en cero indica que los umbrales no se cumplen, las reglas de ignore aplican o la acción está fallando.

Decisión: Si “File list” apunta a un archivo que no tienes o no usas (común en sistemas basados en journal), salta a las Tareas 6–8.

Tarea 6 — Confirma dónde se registran los fallos de autenticación en Ubuntu 24.04

cr0x@server:~$ ls -l /var/log/auth.log
-rw-r----- 1 syslog adm 148322 Dec 28 10:18 /var/log/auth.log

Qué significa: Si /var/log/auth.log existe y se está actualizando, el backend de archivos puede funcionar. Si falta o está obsoleto, probablemente necesites el backend de systemd.

Decisión: Si falta/está obsoleto, usa la Tarea 7 para confirmar que el journal tiene los eventos y luego configura backend = systemd para la jail.

Tarea 7 — Demuestra que los fallos existen en el journal

cr0x@server:~$ sudo journalctl -u ssh --since "30 min ago" --no-pager | tail -n 20
Dec 28 10:09:41 server sshd[3188]: Failed password for invalid user admin from 203.0.113.90 port 49152 ssh2
Dec 28 10:09:44 server sshd[3188]: Failed password for root from 203.0.113.90 port 49153 ssh2

Qué significa: El sistema tiene la evidencia que Fail2ban necesita. Ahora debes asegurarte de que la jail lea la misma fuente.

Decisión: Si el journal tiene entradas pero la “File list” de la jail muestra /var/log/auth.log, cambia a backend = systemd (y elimina overrides de logpath que confundan).

Tarea 8 — Comprueba el backend efectivo y el logpath de la jail

cr0x@server:~$ sudo fail2ban-client get sshd backend
systemd
cr0x@server:~$ sudo fail2ban-client get sshd logpath
/var/log/auth.log

Qué significa: Sí, puedes acabar con un backend systemd y aun así un logpath obsoleto configurado (especialmente si copiaste fragmentos antiguos). Eso confunde y es innecesario.

Decisión: Si usas backend systemd, elimina logpath de esa jail a menos que tengas un motivo específico. Mantén la configuración aburrida.

Tarea 9 — Verifica que Fail2ban pueda leer la fuente de logs (permisos y grupos)

cr0x@server:~$ ps -o user,group,comm -p $(pgrep -xo fail2ban-server)
USER     GROUP    COMMAND
root     root     fail2ban-server
cr0x@server:~$ sudo -u root test -r /var/log/auth.log && echo "readable"
readable

Qué significa: En Ubuntu, Fail2ban normalmente se ejecuta como root, por lo que los permisos raramente son el bloqueo. Pero si lo endureciste para que se ejecute sin privilegios, el acceso a logs se vuelve real.

Decisión: Si ejecutas Fail2ban como no-root, asegúrate de que pueda leer los logs y manipular el firewall. Si no puede, no funcionará.

Tarea 10 — Ejecuta una prueba regex del filtro contra datos reales (deja de esperar)

cr0x@server:~$ sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched
Running tests
=============
Use   failregex filter file : sshd, basedir: /etc/fail2ban
Use         log file : /var/log/auth.log
Results
=======
Failregex: 12 total
|-  #) [# of hits] regular expression
|   1) [12] Failed password for .* from <HOST>
Ignoreregex: 0 total

Summary
=======
Lines:  1828 lines, 0 ignored, 12 matched, 1816 missed

Qué significa: Si esto informa “0 matched”, Fail2ban no puede banear con esa combinación filtro/log. Tus logs pueden estar en el journal, o los mensajes de SSHD pueden ser diferentes (módulos PAM, localización, etc.).

Decisión: Arregla la capacidad de coincidencia antes de tocar umbrales. Si no hace match, no importa cuál sea maxretry.

Tarea 11 — Confirma que la lógica de ventanas temporales no impide los baneos

cr0x@server:~$ sudo fail2ban-client get sshd findtime
600
cr0x@server:~$ sudo fail2ban-client get sshd maxretry
5
cr0x@server:~$ sudo fail2ban-client get sshd bantime
3600

Qué significa: Baneas cuando una IP produce maxretry coincidencias dentro de findtime. Si los atacantes distribuyen intentos en el tiempo o entre usuarios con líneas de log distintas, puede que no alcances el umbral.

Decisión: Si “Total failed” sube pero no hay baneos, reduce temporalmente maxretry a 2 y mantén findtime razonable (p. ej., 10 minutos). Verifica que los baneos ocurren y luego vuelve a afinar.

Tarea 12 — Comprueba reglas de ignore (la manta de inmunidad silenciosa)

cr0x@server:~$ sudo fail2ban-client get sshd ignoreip
127.0.0.1/8 ::1 10.0.0.0/8 192.168.0.0/16

Qué significa: Si tu tráfico proviene de una VPN o rango NAT dentro de ignoreip, Fail2ban hará la vista gorda. Esto es una función y también un generador de incidencias.

Decisión: Mantén las listas de ignoreip reducidas. Añade tus IPs de administración, no todo el espacio de direcciones corporativo, a menos que disfrutes explicar por qué laptops comprometidos pueden hacer fuerza bruta libremente.

Tarea 13 — Fuerza un baneo (prueba controlada) para verificar la capa de acción

cr0x@server:~$ sudo fail2ban-client set sshd banip 203.0.113.90
1
cr0x@server:~$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	44
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	1
   |- Total banned:	1
   `- Banned IP list:	203.0.113.90

Qué significa: Esto demuestra que el plano de control de Fail2ban puede emitir baneos. No prueba que el firewall lo esté aplicando. Eso viene a continuación.

Decisión: Si el baneo aparece aquí, procede a validar el firewall. Si no aparece, tu jail/acción está rota dentro de Fail2ban (o usaste el nombre de jail equivocado).

Tarea 14 — Identifica si el sistema usa nftables o iptables para la aplicación

cr0x@server:~$ sudo update-alternatives --display iptables
iptables - auto mode
  link best version is /usr/sbin/iptables-nft
  link currently points to /usr/sbin/iptables-nft
  link iptables is /usr/sbin/iptables
/usr/sbin/iptables-nft - priority 20
/usr/sbin/iptables-legacy - priority 10

Qué significa: Si iptables apunta a iptables-nft, entonces “reglas iptables” pueden ser en realidad estructuras nftables debajo. Si usas una acción nftables en Fail2ban, debes inspeccionar nftables directamente.

Decisión: Elige una vía de aplicación y obsérvala con la herramienta correcta. No mezcles iptables-legacy con nftables a menos que intencionalmente ejecutes universos paralelos.

Tarea 15 — Verifica que nftables tenga una tabla/chain de Fail2ban (si usas acciones nftables)

cr0x@server:~$ sudo nft list ruleset | sed -n '1,120p'
table inet filter {
	chain input {
		type filter hook input priority filter; policy accept;
		ct state established,related accept
		iif "lo" accept
		tcp dport 22 accept
	}
}
table inet f2b-table {
	set f2b-sshd {
		type ipv4_addr
		elements = { 203.0.113.90 }
	}
	chain f2b-sshd {
		ip saddr @f2b-sshd drop
		return
	}
}

Qué significa: Quieres ver una tabla/chain o set f2b con tu IP baneada. Si existe, Fail2ban programó nftables.

Decisión: Si el set/chain existe pero el tráfico aún pasa, probablemente tienes problemas de orden de reglas (la chain de Fail2ban no está enganchada lo suficientemente temprano) o tu servicio es accesible vía IPv6 mientras baneaste solo IPv4.

Tarea 16 — Verifica que iptables tenga chains de Fail2ban (si usas acciones iptables)

cr0x@server:~$ sudo iptables -S | sed -n '1,80p'
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N f2b-sshd
-A INPUT -p tcp -m tcp --dport 22 -j f2b-sshd
-A f2b-sshd -s 203.0.113.90/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -j RETURN

Qué significa: La presencia de la chain f2b-sshd y un salto desde INPUT hacia ella es la prueba de la configuración de aplicación.

Decisión: Si tienes una chain pero no hay salto hacia ella, la acción solo creó la chain pero no la adjuntó. Eso es un problema de configuración de la acción.

Tarea 17 — Verifica que el baneo realmente bloquee paquetes (no confíes en la configuración; prueba el comportamiento)

cr0x@server:~$ sudo timeout 5 tcpdump -ni any 'host 203.0.113.90 and tcp port 22'
tcpdump: data link type LINUX_SLL2
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
10:21:02.112345 IP 203.0.113.90.49160 > 198.51.100.10.22: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 1 ecr 0,nop,wscale 7], length 0

Qué significa: Ves SYNs llegando. Ahora comprueba si el servidor responde (SYN-ACK) o descarta/rechaza. Si tu baneo es “drop”, deberías ver SYNs entrantes sin respuestas.

Decisión: Si todavía ves respuestas SYN-ACK saliendo, tu regla de firewall no está siendo alcanzada (chain/orden/interfaz/familia incorrecta), o el servicio está enlazado de forma diferente a lo que supones.

Tarea 18 — Desbanea tu IP de prueba y limpia

cr0x@server:~$ sudo fail2ban-client set sshd unbanip 203.0.113.90
1

Qué significa: No intentas banear permanentemente IPs de documentación (o el Wi‑Fi del hotel de tu compañero). Estás verificando la canalización.

Decisión: Si el unban no elimina el estado del firewall, la limpieza de la acción está rota, o estás mirando el backend de firewall equivocado.

Realidad del firewall en Ubuntu 24.04: nftables, iptables y lo que Fail2ban realmente toca

Ubuntu 24.04 está en la era donde nftables es la verdad subyacente, incluso cuando tu memoria muscular todavía escribe iptables. Fail2ban puede usar cualquiera de los dos, pero debes alinear:

  • Acciones nftables crean tablas/chains/sets de nft directamente. Valídalas con nft list ruleset.
  • Acciones iptables insertan reglas iptables. En sistemas con iptables-nft, esas reglas pueden traducirse a estructuras nftables, pero aún las gestionas con las herramientas de iptables.

Elige una interfaz de aplicación y obsérvala correctamente

Si configuras Fail2ban para usar acciones nftables, no valides baneos usando iptables -S y luego declares que está roto. Eso es como comprobar el nivel de aceite mirando las ruedas. Técnicamente todo es parte del coche, pero no estás midiendo lo correcto.

UFW complica, pero no prohíbe, Fail2ban

UFW es un gestor de políticas, no un motor de firewall. La acción clásica de Fail2ban con iptables suele coexistir con UFW porque UFW deja espacio para chains adicionales. Pero UFW también puede imponer políticas por defecto estrictas y ordenar sus chains antes o después de las de Fail2ban según cómo lo integres.

Regla operativa: si usas UFW, asegúrate de que el bloqueo de Fail2ban se evalúe temprano en la ruta de entrada para los puertos objetivo. Si no lo sabes, prueba con la Tarea 17 y deja de teorizar.

IPv6: el culpable de “baneado pero aún conectándose”

Si tu servicio escucha en :: (comodín IPv6) y el cliente tiene conectividad IPv6, un baneo IPv4 no influirá en esa conexión. Eso puede parecer “Fail2ban no está bloqueando” cuando en realidad sí lo hace, pero en la familia de protocolo equivocada.

Enfoque práctico: confirma qué familia de direcciones usan los clientes y configura acciones de Fail2ban que abarquen IPv4 e IPv6 cuando sea apropiado.

Filtros y regex: cuando Fail2ban “funciona” pero nunca hace match

Los filtros son donde tus suposiciones van a morir. Fail2ban no entiende “un atacante”. Entiende “una línea de log que coincide con este regex”. Eso es a la vez su potencia y su trampa.

Conoce tu formato de logs: OpenSSH no es la única voz en auth

Entre PAM, distintas configuraciones de SSHD y a veces parches personalizados, las cadenas de fallo pueden variar. Algunos entornos emiten líneas “Invalid user”, otros “Failed password”, otros campos distintos para fallos de publickey. Tu filtro debe coincidir con tu realidad.

Usa fail2ban-regex como una puerta, no como un postmortem de depuración

Si haces una cosa con disciplina: ejecuta fail2ban-regex contra la fuente exacta de log que configuraste. Si no hace match, no tienes un problema de baneos. Tienes un problema de patrón.

Los backends journald cambian cómo scopes

Al usar backend = systemd, Fail2ban usa consultas al journal en lugar de hacer tail a un archivo. Eso suele ser mejor en sistemas modernos, pero significa que la vieja sabiduría de “logpath” puede engañarte. En el mundo del journal, la pregunta se vuelve: ¿el nombre de la unidad es correcto (p. ej., ssh vs sshd) y tus permisos de journal son suficientes?

Broma #2: Regex es un lenguaje de solo escritura — hasta que el informe de incidente te exige leerlo.

Acciones: cuando hace match, pero nada se bloquea

Una vez que probaste coincidencias, la siguiente clase de fallos es “Fail2ban decidió banear, pero la aplicación no hizo nada”. Eso suele caer en uno de estos grupos:

  • Familia de firewall equivocada: usaste una acción iptables en un sistema donde solo inspeccionas nftables, o viceversa.
  • Orden de reglas: la regla de baneo existe pero nunca se alcanza porque reglas anteriores aceptan el tráfico (común con reglas ACCEPT amplias, o hooks de entrada que evitan la chain).
  • Desajuste de interfaz: tu servicio está en otro puerto/protocolo del que la acción asume, o estás baneando el puerto 22 mientras SSH corre en el 2222.
  • Desajuste IPv6: baneaste IPv4 pero el cliente se conecta vía IPv6.
  • Permisos/capacidades: Fail2ban no puede programar realmente el firewall debido a hardening o políticas del sistema.

El baneo/desbaneo manual es tu mejor “test unitario”

En sistemas reales, puedes pasar horas esperando que se dispare un umbral. No lo hagas. Usa la Tarea 13 para forzar un baneo y luego inspecciona el estado del firewall. Si el baneo manual no crea entradas en el firewall, tienes un problema de ejecución de la acción. Si sí lo hace, pero los baneos automáticos no ocurren, tienes un problema de evento/match/umbral.

Verifica la adjunción de la chain, no solo su existencia

Es posible tener una chain de Fail2ban maravillosamente poblada que nadie salte. Eso es el equivalente firewall de construir una caseta de guardia en el aparcamiento y no poner a nadie a vigilarla.

Para iptables, necesitas ver un salto desde INPUT (o la chain relevante) hacia f2b-*. Para nftables, necesitas que la chain f2b esté referenciada en el hook correcto, o un set usado por una regla que realmente se ejecuta para tráfico entrante.

Tres microhistorias corporativas desde las trincheras sin baneos

1) El incidente causado por una suposición equivocada

El equipo tenía una flota pequeña de servidores Ubuntu actualizados a una versión más nueva. La lista de verificación de migración incluía “instalar Fail2ban” y “habilitar la jail sshd”, y alguien marcó la casilla. Unos días después, sus registros de autenticación mostraron un patrón sostenido de fuerza bruta: miles de intentos fallidos, constantes como un metrónomo.

Todos asumieron que el sistema de baneos lo tenía controlado porque siempre lo había tenido. El ingeniero de guardia miró fail2ban-client status y vio la jail sshd “en ejecución”. Reconfortante. Volvió a otras tareas.

A la mañana siguiente, alguien notó picos de CPU correlacionados con las ráfagas de autenticación. Los picos no eran catastróficos, pero bastaron para degradar un servicio sensible a latencia que compartía la misma clase de VM. Cuando finalmente revisaron los detalles de la jail, “Total failed” subía y “Total banned” estaba atascado en cero.

La suposición equivocada: “Si la jail está en ejecución, debe estar baneando.” En realidad, la jail leía /var/log/auth.log, que existía pero estaba obsoleto porque los eventos de auth del sistema ahora estaban en journald. No había entrada de log en vivo. No hubo baneo.

La solución fue aburrida y rápida: cambiar la jail a backend = systemd, quitar el override de logpath que la fijaba al archivo obsoleto y luego banear forzadamente una IP de prueba para validar las reglas nftables. Una vez visible y coincidiente la chain, el ruido de la fuerza bruta bajó inmediatamente. El postmortem fue corto y algo embarazoso, que es lo mejor.

2) La optimización que salió mal

Un ingeniero con mentalidad de seguridad quiso reducir escrituras en disco y volumen de logs. Ajustó rsyslog y confió más en journald. No es intrínsecamente malo. Pero también ajustó rotación y retención de forma que algunos logs basados en archivos desaparecieron por completo. Nadie lo notó porque los servicios seguían funcionando y el journal aún tenía datos.

Fail2ban, sin embargo, seguía configurado con backends de archivo y apuntaba a rutas que solían existir. Como Fail2ban no se bloqueó, la suposición fue “está bien”. Se inició, funcionó y hasta reportó jails. Pero efectivamente estaba haciendo tail a aire vacío.

La parte sutil fue: además aumentaron findtime y bantime drásticamente para “ser más duros”. La intención era buena—banear más y por más tiempo. El efecto fue peor: cuando reactivaron el logging, Fail2ban de repente tenía una ventana mayor para evaluar, pero el filtro regex coincidió con líneas benignas adicionales debido a un formato PAM personalizado. Comenzó a banear IPs de egress internas durante una ventana de mantenimiento.

Operaciones lo vio como un problema de red intermitente. Desarrolladores lo vieron como “SSH está roto”. Seguridad lo vio como “estamos bajo ataque”. La verdad: una optimización en logging más una afinación agresiva de baneos amplificó un desajuste del filtro.

La recuperación fue disciplinada: volver a un conjunto mínimo de filtros probados, cambiar los backends explícitamente a journald, reducir umbrales a valores sensatos y estrechar ignoreip a los rangos administrativos reales. Mantuvieron journald como almacén principal, pero dejaron de pretender que logs invisibles podían ser parseados por herramientas que esperan archivos.

3) La práctica aburrida pero correcta que salvó el día

Otra organización ejecutaba un trabajo estandarizado de “verificación de controles de seguridad” después de cada ciclo de parches del SO. No era sofisticado; eran un puñado de comandos capturados en un runbook con salidas esperadas. Una de esas comprobaciones forzaba un baneo temporal a una IP de prueba segura y validaba que una regla de firewall apareciera en el subsistema correcto.

Durante un despliegue rutinario, una nueva imagen base cambió la alternativa de iptables de legacy a nft. Nada más en la pila de aplicaciones falló. El despliegue parecía limpio. Pero el trabajo de verificación falló: el baneo de prueba aparecía en el estado de Fail2ban, sin embargo la chain iptables esperada no estaba presente. No había reglas coincidentes.

Como la prueba era rutinaria, se notó inmediatamente. El equipo no esperó a que un atacante probara la brecha. Simplemente actualizaron la acción de Fail2ban a nftables y ajustaron la validación para inspeccionar nft list ruleset en lugar de grepear la salida de iptables. Diez minutos después, la canalización pasó. Pusieron la nueva baseline y siguieron.

Lo mejor: nadie tuvo que discutir en una reunión si los baneos “probablemente siguen funcionando”. Tenían evidencia. Práctica aburrida y correcta. No gana premios, pero mantiene las noches tranquilas.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: la jail está listada, pero “Total failed” se queda en 0

Causa raíz: Fuente de logs incorrecta. Estás haciendo tail a un archivo que no se escribe, o la unidad que consultas en journald es incorrecta.

Solución: Demuestra que existen fallos (Tarea 7) y alinea backend/logpath (Tareas 6–8). No adivines. Haz que la jail lea lo que tu sistema escribe.

2) Síntoma: “Total failed” aumenta, pero “Total banned” se queda en 0 para siempre

Causa raíz: Umbrales demasiado altos, ignoreip incluye el rango de origen del atacante, o tus fallos se distribuyen en el tiempo más allá de findtime.

Solución: Revisa umbrales e ignoreip (Tareas 11–12). Reduce temporalmente maxretry para validar la canalización y luego afina.

3) Síntoma: “Currently banned” muestra IPs, pero los atacantes aún se conectan

Causa raíz: Desajuste de aplicación: acción equivocada (iptables vs nftables), orden de reglas o bypass por IPv6.

Solución: Fuerza un baneo de prueba (Tarea 13), luego verifica en el subsistema de firewall correcto (Tareas 14–16) y valida el comportamiento de paquetes (Tarea 17). Añade cobertura IPv6 si hace falta.

4) Síntoma: logs de Fail2ban muestran “No such file or directory” para logpath

Causa raíz: Fragmentos antiguos de jail que apuntan a /var/log/auth.log u otros logs que ya no existen porque el logging pasó a journald o a la salida de contenedor.

Solución: Cambia a backend = systemd donde corresponda, o asegúrate de que el servicio registre en un archivo que puedas leer y rotar con seguridad.

5) Síntoma: el baneo manual funciona, los baneos automáticos nunca ocurren

Causa raíz: Filtro discordante: tu regex no coincide con las líneas reales de fallo. O la jail monitoriza el puerto/nombre de servicio equivocado.

Solución: Ejecuta fail2ban-regex (Tarea 10) contra la fuente real y ajusta el filtro o los parámetros de la jail hasta obtener coincidencias.

6) Síntoma: tras habilitar UFW, los baneos dejan de funcionar

Causa raíz: Cambio en el orden de chains; las reglas de UFW aceptan tráfico antes de que Fail2ban pueda descartarlo.

Solución: Asegura que las reglas de Fail2ban se evalúen temprano para los puertos objetivo. Valida con pruebas de comportamiento (Tarea 17), no solo por la presencia de reglas.

7) Síntoma: Fail2ban banea a tus colegas durante un despliegue

Causa raíz: NAT: muchas personas comparten una IP de salida y los fallos de múltiples hosts se acumulan bajo esa dirección.

Solución: Añade IPs de egress corporativas apropiadas a ignoreip (con precisión), o aumenta maxretry/findtime para evitar castigar el egress compartido. Considera mover el acceso administrativo detrás de una VPN con identidad por usuario.

8) Síntoma: el servicio Fail2ban se inicia, pero las jails no se cargan tras una edición

Causa raíz: Error de sintaxis en un archivo de jail o un problema de orden en includes. Fail2ban es estricto y una sección mal formada puede deshabilitar lo que creías habilitado.

Solución: Usa fail2ban-client -d (Tarea 4) y los errores del journal (Tarea 2) para identificar el archivo defectuoso. Arregla, reinicia y vuelve a comprobar la lista de jails.

Listas de verificación / plan paso a paso

Lista A — “Necesito que los baneos funcionen hoy” (30–60 minutos, disciplinado)

  1. Confirma la salud de Fail2ban: systemctl status fail2ban (Tarea 1).
  2. Lee errores de Fail2ban: journalctl -u fail2ban (Tarea 2).
  3. Verifica que tu jail está en ejecución: fail2ban-client status (Tarea 3).
  4. Inspecciona detalles de la jail: fail2ban-client status sshd (Tarea 5).
  5. Demuestra que existen fallos en la fuente de logs elegida (Tareas 6–7).
  6. Demuestra que el filtro hace match: fail2ban-regex (Tarea 10).
  7. Forza un baneo a una IP de prueba: fail2ban-client set sshd banip ... (Tarea 13).
  8. Verifica el estado del firewall con la herramienta correcta (Tareas 14–16).
  9. Valida el comportamiento con tcpdump (Tarea 17), luego desbanea (Tarea 18).

Lista B — Endurecimiento sin romper los baneos (porque “seguro” no es “no funcional”)

  1. Decide tu backend de aplicación: nftables o iptables-nft. Documenta la elección.
  2. Estandariza jails en /etc/fail2ban/jail.d/*.local en lugar de editar jail.conf.
  3. Prefiere backend = systemd en hosts donde journald es la autoridad.
  4. Mantén ignoreip mínimo; trátalo como listas de permitidos de firewall (revisadas, justificadas, acotadas).
  5. Cubre IPv6 si tus servicios son accesibles por IPv6.
  6. Añade una prueba sencilla de verificación de baneo a tus comprobaciones post-despliegue (baneo, inspección, desbaneo).

Lista C — Cuando sospechas problemas de orden de reglas

  1. Forza un baneo de prueba (Tarea 13).
  2. Encuentra la presencia de la regla y la chain (Tareas 15–16).
  3. Confirma que la chain de baneo está realmente referenciada desde la ruta de entrada (iptables: salto desde INPUT; nft: hook de chain o set referenciado).
  4. Prueba el comportamiento con tcpdump (Tarea 17).
  5. Ajusta la configuración de la acción o el orden de políticas del firewall hasta que los paquetes dejen de ser aceptados.

Preguntas frecuentes

1) ¿Por qué Fail2ban muestra “Total failed” en aumento pero aun así no banea?

Porque el baneo se activa por umbrales. Verifica maxretry, findtime y ignoreip. Luego reduce temporalmente maxretry para demostrar que la canalización funciona y ajusta de nuevo.

2) ¿Debo usar backend = systemd en Ubuntu 24.04?

Si tus fallos de autenticación viven en journald (común), sí. Usa el journal como fuente de verdad. El tail de archivos está bien cuando el archivo es real, actual y consistentemente formateado.

3) ¿Cómo sé si uso nftables o iptables?

Comprueba update-alternatives --display iptables e inspecciona las reglas activas con la herramienta correspondiente. Si Fail2ban usa acciones nftables, inspecciona con nft list ruleset.

4) Veo baneos en el estado de Fail2ban. ¿Eso garantiza que el tráfico está bloqueado?

No. Eso solo prueba el estado interno de Fail2ban. Siempre valida la aplicación comprobando reglas de firewall y el comportamiento de paquetes (tcpdump o un intento de conexión controlado).

5) ¿Por qué el baneo manual funciona pero el automático no?

El baneo manual se salta el filtrado. Los baneos automáticos dependen de coincidencias en logs. Usa fail2ban-regex contra la fuente exacta para demostrar que tu filtro coincide con la realidad.

6) ¿Puede UFW interferir con Fail2ban?

Sí, generalmente a través del orden de reglas o supuestos de política en conflicto. La solución no es “deshabilitar UFW”. La solución es asegurar que el bloqueo de Fail2ban se evalúe en la ruta relevante y verificar el comportamiento.

7) ¿Por qué a veces me baneo a mí mismo del servidor?

NAT y egress compartido. Varias personas fallando logins desde la misma IP pública acumulan reintentos bajo esa dirección. Añade IPs de egress administrativas a ignoreip con precisión, o mejora los patrones de acceso (VPN, bastión, claves).

8) ¿Necesito preocuparme por IPv6 con Fail2ban?

Si el servicio es accesible por IPv6, sí. De lo contrario, generas una situación de “el baneo funciona para la mitad de Internet”, que no es el tipo de éxito que quieres anunciar.

9) ¿Fail2ban es suficiente protección para SSH?

Es un reductor de ruido útil y un limitador por tasa mediante baneos. Pero lo fundamental sigue siendo más importante: autenticación por clave, deshabilitar autenticación por contraseña cuando sea posible, MFA en bastiones y mínima exposición en el perímetro de red.

10) ¿Cuál es la mejor comprobación de cordura cuando alguien dice “Fail2ban no funciona”?

Forzar un baneo a una IP de prueba, verificar el estado del firewall y luego verificar el comportamiento de paquetes. Eso aisla la aplicación de la detección en minutos.

Conclusión: próximos pasos que puedes hacer hoy

Si Fail2ban no está baneando en Ubuntu 24.04, trátalo como cualquier otra canalización de producción: entrada, procesamiento, salida, aplicación. Verifica cada capa con evidencia. No toques perillas a oscuras.

Haz estas tres cosas ahora:

  1. Demuestra visibilidad de logs: confirma que tus fallos existen donde la jail los lee (journal vs archivo) y ejecuta fail2ban-regex contra datos reales.
  2. Demuestra la aplicación: banea manualmente una IP de prueba y confirma que las reglas aparecen en el subsistema de firewall correcto (nft vs iptables), luego valida el comportamiento con paquetes.
  3. Hazlo repetible: añade un pequeño paso de verificación post-cambio (banear, observar, desbanear) para que las actualizaciones no desenchufen silenciosamente tus defensas.

Fail2ban no es magia. Eso es buena noticia. Significa que puedes depurarlo como un adulto: con un flujo de trabajo, no con un deseo.

← Anterior
Solucionar WordPress “There has been a critical error”: habilitar WP_DEBUG y recuperar el sitio
Siguiente →
Compresión ZFS con zstd: elegir niveles sin quemar CPU

Deja un comentario