Fail2ban se instala el primero y ahí suele quedarse la cosa: el paquete está, el servicio funciona, sshd parece protegido. Medio año después resulta que la cárcel lee un archivo que no existe en este sistema, y que todo lo demás —el correo, el panel, el formulario de acceso del sitio— nunca estuvo protegido.
Veamos qué conviene activar de verdad en un VPS corriente con un sitio web, qué números poner ahí y cómo asegurarse de que una cárcel funciona en lugar de solo figurar en la configuración.
Compruebe primero que sshd captura algo
Un solo comando:
sudo fail2ban-client status sshd
En la salida están las líneas Currently failed, Total failed y Total banned. Si Total failed es cero en un servidor expuesto al menos un día, la cárcel no funciona. Contrástelo con la realidad:
sudo lastb | wc -l
Miles de intentos fallidos en lastb frente a ceros en Fail2ban significan una sola cosa: el filtro no mira donde debe.
La causa más frecuente es Debian 12. Ya no instala rsyslog por omisión, el archivo /var/log/auth.log sencillamente no existe, y la cárcel sshd que viene de fábrica está configurada para leer ese archivo. No aparece ningún mensaje de error: el servicio arranca, el estado se muestra, los contadores siguen a cero. Se resuelve pasando al diario de systemd:
[sshd]
enabled = true
backend = systemd
La otra opción es recuperar rsyslog como paquete, si un auth.log en texto le importa para otras herramientas. Ubuntu 24.04 se comporta igual.
Dónde escribir los ajustes
No se toca /etc/fail2ban/jail.conf: se sobrescribe al actualizar el paquete, y cualquier cambio hecho ahí desaparecerá un día en silencio. Sus ajustes van a /etc/fail2ban/jail.local; ese archivo se lee el último y prevalece sobre el común.
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.25
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
ignoreip con su propia dirección no es un lujo sino un seguro: bloquearse a uno mismo por una errata ocurre antes de lo que parece. Si no tiene dirección fija, mantenga una entrada de reserva además de SSH: la consola o VNC del proveedor.
La cárcel que cambia el panorama: recidive
Una cárcel corriente tiene memoria corta: cinco intentos en diez minutos, una hora de bloqueo, y una hora después todo vuelve a empezar. Un bot convive perfectamente con eso y volverá mañana y pasado. recidive cierra justamente esa brecha: no lee el registro del sistema sino el propio de Fail2ban, es decir, bloquea a quienes Fail2ban ya ha bloqueado.
[recidive]
enabled = true
bantime = 1w
findtime = 1d
maxretry = 5
Se lee así: «bloqueado cinco veces en un día, fuera durante una semana». La cárcel necesita el archivo /var/log/fail2ban.log; si ha llevado el registro propio de Fail2ban a syslog, recidive también necesitará backend = systemd.
El efecto práctico de esta sola cárcel suele notarse más que el ajuste fino de todas las demás: los visitantes habituales desaparecen y en los registros queda solo ruido de fondo reciente.
Qué más activar si el servidor aloja un sitio
Por utilidad decreciente:
nginx-http-authoapache-auth: fuerza bruta contra la autenticación básica. Necesario si un área de administración o un entorno de pruebas está tras una contraseña del servidor web;nginx-botsearch: el rastreo de rutas conocidas:/wp-login.php,/phpmyadmin,/.env. No es una intrusión sino reconocimiento, y es precisamente lo que precede a todo lo demás;nginx-limit-req: solo funciona si en el propio Nginx está definidalimit_req_zone; sin ella la cárcel está activada y es inútil;postfix-saslydovecot: imprescindibles si gestiona su propio correo. La búsqueda de contraseñas de buzones es continua y no suele vigilarla nadie.
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log
El formulario de acceso del propio sitio es historia aparte. La aplicación no tiene un registro propio de accesos fallidos, y en access.log un intento fallido parece un POST corriente con código 200 o 302, indistinguible de uno correcto con cualquier filtro genérico. Hay dos vías: hacer que la aplicación escriba los fallos en syslog (la mayoría de gestores de contenidos tiene una extensión para ello) o limitar la frecuencia de peticiones a la página de acceso. Lo segundo es un limitador, no un detector, y conviene saberlo de antemano.
Los números
bantime = 10m es el ajuste por omisión por el que a menudo se considera inútil a Fail2ban: diez minutos le bastan a un bot para volver. Una hora en el primer bloqueo más recidive durante una semana funciona mucho mejor que bloqueos permanentes, que con el tiempo se convierten en una lista de reglas interminable.
maxretry = 3 para SSH es una forma segura de bloquearse a sí mismo. Cinco intentos en diez minutos cortan la fuerza bruta igual de bien.
Una búsqueda lenta —un intento cada cinco minutos— no caerá nunca dentro de la ventana findtime. No es motivo para estirar la ventana a un día: obtendría falsos positivos contra sus propios compañeros. Contra la búsqueda lenta no actúa un umbral, sino la autenticación por contraseña desactivada.
Asegúrese de que el bloqueo llega a los paquetes
Fail2ban solo llama a un comando externo. Si banaction no coincide con lo que filtra realmente el tráfico, el registro se llenará de alegres líneas Ban 198.51.100.7 mientras los paquetes siguen llegando. Debian 12 usa nftables por omisión, y con UFW activo lo correcto es banaction = ufw. Para comprobarlo:
sudo nft list ruleset | grep -c f2b
sudo iptables -S | grep f2b
Al menos uno de los dos debe mostrar reglas. Y el filtro en sí puede probarlo sin esperar a un ataque:
sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf
Al final de la salida se indica cuántas líneas coincidieron. Cero significa que el filtro y el registro no se encuentran, y seguir configurando carece de sentido.
Lo que Fail2ban no hace
No protege de la fuerza bruta distribuida: mil direcciones con un intento cada una no alcanzarán ningún umbral con ninguna configuración. No corrige la causa: una dirección bloqueada no mejora una contraseña débil. Y no sabe nada de reputación: una dirección que ayer atacaba servidores ajenos está limpia para él hasta que se ocupe del suyo. De ahí la combinación práctica: claves en lugar de contraseñas, Fail2ban como limitador de ruido y una lista de bloqueo compartida (CrowdSec) como conocimiento de la experiencia ajena.
Un problema aparte es que todo esto hay que mirarlo de vez en cuando. Nadie teclea fail2ban-client status cárcel por cárcel durante semanas, y un aumento del número de bloqueos se advierte cuando algo ya está roto. En las páginas de demostración de abajo, los mismos datos caben en una sola página: la lista de cárceles, quién está bloqueado ahora mismo y de dónde vienen los intentos.