--- title: "Fail2Ban avanzado en Ubuntu 24.04: filtros y jails propios" description: "Configura Fail2Ban en Ubuntu 24.04 con nftables, bloqueos progresivos, un filtro y una jail propios para Nginx, la jail recidive y avisos por webhook." category: "Seguridad de Red Avanzada" language: es source: https://cubepath.com/es/docs/seguridad-de-red-avanzada/configuracion-avanzada-de-fail2ban-y-jails-personalizadas updated: 2026-09-25 --- # Fail2Ban avanzado en Ubuntu 24.04: filtros y jails propios Fail2Ban lee los logs de tus servicios, detecta patrones de abuso como contraseñas fallidas o escaneos, y bloquea temporalmente las IP responsables en el cortafuegos. Con la configuración por defecto solo protege SSH, pero su verdadera utilidad está en las jails propias. En este tutorial configurarás Fail2Ban en Ubuntu 24.04 para bloquear con nftables, aplicar bloqueos progresivos a los reincidentes, proteger un formulario de login servido por Nginx con un filtro propio y enviar un aviso a un webhook cada vez que se bloquea una IP. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Nginx instalado y sirviendo un sitio con un formulario de login en `/login`, si quieres seguir el ejemplo de la jail propia. Si tu aplicación usa otra ruta, adapta la expresión regular. - La IP pública desde la que administras el servidor (`your_admin_ip`), para no bloquearte a ti mismo. ## Paso 1: Instalar Fail2Ban Instala el paquete desde los repositorios de Ubuntu: ```bash sudo apt update sudo apt install -y fail2ban ``` Habilita y arranca el servicio: ```bash sudo systemctl enable --now fail2ban ``` Comprueba que responde y qué jails tiene activas. En Ubuntu, el paquete activa la jail `sshd` por defecto: ```bash sudo fail2ban-client status ``` ```output Status |- Number of jail: 1 `- Jail list: sshd ``` ## Paso 2: Entender la estructura de configuración Nunca edites `jail.conf` ni los ficheros originales de `filter.d` y `action.d`: se sobrescriben al actualizar el paquete. Fail2Ban lee los ficheros en este orden y cada uno sobrescribe al anterior: | Fichero | Uso | |---------|-----| | `/etc/fail2ban/jail.conf` | Valores por defecto del paquete, no se toca | | `/etc/fail2ban/jail.d/*.conf` | Ajustes de la distribución (`defaults-debian.conf`) | | `/etc/fail2ban/jail.local` | Tus valores globales | | `/etc/fail2ban/jail.d/*.local` | Tus jails, una por fichero | | `/etc/fail2ban/filter.d/*.conf` | Filtros: qué líneas de log cuentan como fallo | | `/etc/fail2ban/action.d/*.conf` | Acciones: qué se hace al bloquear o desbloquear | ## Paso 3: Definir los valores globales Crea `jail.local` con los valores que se aplicarán a todas las jails: ```bash sudo nano /etc/fail2ban/jail.local ``` ```ini [DEFAULT] # No bloquear nunca estas direcciones ignoreip = 127.0.0.1/8 ::1 your_admin_ip # Ventana de detección, fallos permitidos y duración del primer bloqueo findtime = 10m maxretry = 5 bantime = 1h # Bloqueos progresivos: cada nuevo bloqueo de la misma IP dura más bantime.increment = true bantime.factor = 2 bantime.maxtime = 1w # Bloquear con nftables en lugar de iptables banaction = nftables-multiport banaction_allports = nftables-allports ``` Con `bantime.increment` activo, una IP que vuelve a ser bloqueada pasa de 1 hora a 2, luego a 4, y así hasta el máximo de una semana. Fail2Ban recuerda los bloqueos anteriores en su base de datos SQLite, que por defecto purga las entradas de más de un día. Para que el historial dure tanto como el bloqueo más largo, crea `fail2ban.local`: ```bash sudo nano /etc/fail2ban/fail2ban.local ``` ```ini [Definition] dbpurgeage = 8d ``` ## Paso 4: Endurecer la jail de SSH El filtro `sshd` incluido admite varios modos. El modo `aggressive` combina los modos `normal`, `ddos` y `extra`, y detecta también desconexiones durante la negociación y fallos de clave pública, que son típicos de los escáneres. Crea un fichero para la jail: ```bash sudo nano /etc/fail2ban/jail.d/sshd.local ``` ```ini [sshd] enabled = true mode = aggressive port = ssh maxretry = 3 ``` Si SSH escucha en otro puerto, indícalo en `port` (por ejemplo `port = 2222`), porque es el puerto que se cierra a la IP bloqueada. Valida toda la configuración antes de recargar: ```bash sudo fail2ban-client -t ``` ```output OK: configuration test is successful ``` Aplica los cambios: ```bash sudo fail2ban-client reload sudo fail2ban-client get sshd maxretry ``` ```output 3 ``` ## Paso 5: Crear un filtro propio para Nginx Un filtro es una lista de expresiones regulares (`failregex`) donde `` marca la IP que se va a bloquear. Este ejemplo detecta intentos de login fallidos en una aplicación que responde con `401` a `POST /login`. Así se ve una línea del log de acceso de Nginx en formato combinado: ```text 203.0.113.45 - - [25/Sep/2026:12:01:33 +0000] "POST /login HTTP/1.1" 401 512 "-" "Mozilla/5.0" ``` Crea el filtro: ```bash sudo nano /etc/fail2ban/filter.d/nginx-login.conf ``` ```ini [Definition] failregex = ^ \S+ \S+ \[[^\]]+\] "POST /login[^"]*" 401 ignoreregex = ``` La marca de tiempo entre corchetes la reconoce Fail2Ban automáticamente, así que no hace falta definir `datepattern`. Antes de usar el filtro, pruébalo con `fail2ban-regex`, que acepta una línea de texto o un fichero de log: ```bash sudo fail2ban-regex '203.0.113.45 - - [25/Sep/2026:12:01:33 +0000] "POST /login HTTP/1.1" 401 512 "-" "Mozilla/5.0"' /etc/fail2ban/filter.d/nginx-login.conf ``` ```output Results ======= Failregex: 1 total |- #) [# of hits] regular expression | 1) [1] ^ \S+ \S+ \[[^\]]+\] "POST /login[^"]*" 401 `- Ignoreregex: 0 total Date template hits: |- [# of hits] date format | [1] Day(?P<_sep>[-/])MON(?P=_sep)ExYear[ :]?24hour:Minute:Second(?:\.Microseconds)?(?: Zone offset)? `- Lines: 1 lines, 0 ignored, 1 matched, 0 missed ``` Pruébalo también contra el log real para asegurarte de que no coincide con peticiones legítimas: ```bash sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-login.conf ``` Revisa la línea `Lines:` del final: el número de coincidencias debe corresponder solo a logins fallidos. ## Paso 6: Crear la jail para el filtro Una jail une un filtro, un log y una acción. Crea la jail para el login: ```bash sudo nano /etc/fail2ban/jail.d/nginx-login.local ``` ```ini [nginx-login] enabled = true filter = nginx-login logpath = /var/log/nginx/access.log port = http,https maxretry = 5 findtime = 5m bantime = 30m ``` Valida y recarga: ```bash sudo fail2ban-client -t sudo fail2ban-client reload sudo fail2ban-client status nginx-login ``` ```output Status for the jail: nginx-login |- Filter | |- Currently failed: 0 | |- Total failed: 0 | `- File list: /var/log/nginx/access.log `- Actions |- Currently banned: 0 |- Total banned: 0 `- Banned IP list: ``` Para probarla sin bloquearte, lanza desde otra máquina (no desde `your_admin_ip`, que está en `ignoreip`) seis peticiones fallidas contra tu sitio y vuelve a consultar el estado. La IP de esa máquina aparecerá en `Banned IP list`. ## Paso 7: Bloquear a los reincidentes con recidive La jail `recidive` lee el propio log de Fail2Ban y bloquea en todos los puertos a las IP que han sido bloqueadas varias veces por cualquier jail. Es complementaria a `bantime.increment`: esta afecta a una jail concreta, mientras que `recidive` cierra el servidor entero. Crea el fichero: ```bash sudo nano /etc/fail2ban/jail.d/recidive.local ``` ```ini [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s findtime = 1d maxretry = 5 bantime = 1w ``` Recarga y comprueba que ya son tres jails: ```bash sudo fail2ban-client reload sudo fail2ban-client status ``` ```output Status |- Number of jail: 3 `- Jail list: nginx-login, recidive, sshd ``` ## Paso 8: Recibir avisos con una acción propia Las acciones son comandos que Fail2Ban ejecuta al arrancar, parar, bloquear o desbloquear. Esta acción envía un mensaje a un webhook compatible con Discord (el campo `content`) cada vez que se bloquea una IP. Para Slack, cambia `content` por `text`. Instala `curl` si no lo tienes y crea la acción: ```bash sudo nano /etc/fail2ban/action.d/webhook-notify.conf ``` ```ini [Definition] actionstart = actionstop = actioncheck = actionban = curl -fsS -m 10 -H "Content-Type: application/json" -d '{"content": "Fail2Ban [] ha bloqueado tras fallos en "}' "" actionunban = [Init] webhook = ``` Las etiquetas entre `<>` las sustituye Fail2Ban: `` es la jail, `` la dirección bloqueada, `` el número de fallos y `` el nombre del servidor. Para usarla en la jail de SSH sin perder el bloqueo en el cortafuegos, combínala con la acción por defecto `%(action_)s`. Edita la jail: ```bash sudo nano /etc/fail2ban/jail.d/sshd.local ``` ```ini [sshd] enabled = true mode = aggressive port = ssh maxretry = 3 action = %(action_)s webhook-notify[webhook="https://discord.com/api/webhooks/your_webhook_id/your_webhook_token"] ``` Sustituye la URL por la de tu webhook. Recarga y fuerza un bloqueo manual con una IP de documentación para probar la acción: ```bash sudo fail2ban-client reload sudo fail2ban-client set sshd banip 203.0.113.99 ``` ```output 1 ``` Deberías recibir el mensaje en tu canal. Si no llega, revisa el log de Fail2Ban con `sudo grep -i webhook /var/log/fail2ban.log`. Después, desbloquea la IP de prueba: ```bash sudo fail2ban-client set sshd unbanip 203.0.113.99 ``` ## Paso 9: Administrar los bloqueos Estos son los comandos que usarás a diario. Ver todas las IP bloqueadas, agrupadas por jail: ```bash sudo fail2ban-client banned ``` ```output [{'sshd': ['198.51.100.23', '192.0.2.77']}, {'nginx-login': []}, {'recidive': []}] ``` Desbloquear una IP en todas las jails, útil si has bloqueado a un cliente por error: ```bash sudo fail2ban-client unban 198.51.100.23 ``` Ver las reglas que Fail2Ban ha creado en nftables. Las acciones `nftables-*` usan una tabla propia, así que no interfieren con UFW: ```bash sudo nft list table inet f2b-table ``` Seguir la actividad en tiempo real: ```bash sudo tail -f /var/log/fail2ban.log ``` ```output 2026-09-25 12:14:02,118 fail2ban.filter [4021]: INFO [sshd] Found 198.51.100.23 - 2026-09-25 12:14:01 2026-09-25 12:14:05,407 fail2ban.actions [4021]: NOTICE [sshd] Ban 198.51.100.23 ``` Los bloqueos sobreviven a los reinicios de Fail2Ban y del servidor: al arrancar, Fail2Ban restaura desde su base de datos las IP cuyo bloqueo aún no ha caducado. ## Solución de problemas - **Una jail no arranca y `fail2ban-client -t` da error de `logpath`**: el fichero de log no existe. Crea el servicio o el log antes de activar la jail, o corrige la ruta. - **El filtro no detecta nada aunque hay fallos en el log**: prueba con `fail2ban-regex` usando el log real y la opción `--print-all-missed` para ver qué líneas no coinciden y por qué. - **Las IP bloqueadas siguen llegando a Nginx**: si el servidor está detrás de un proxy o CDN, todas las peticiones llegan desde la IP del proxy y el bloqueo en el cortafuegos no sirve. Configura Nginx para registrar la IP real del cliente con el módulo `realip` y bloquea en el proxy. - **Te has bloqueado a ti mismo**: entra por la consola VNC del panel de tu proveedor y ejecuta `sudo fail2ban-client unban tu_ip`. Después añade tu IP a `ignoreip`. ## Conclusión Tu servidor bloquea ahora con nftables, alarga los bloqueos de los reincidentes, protege un login web con un filtro propio probado con `fail2ban-regex` y te avisa en cada bloqueo. A partir de aquí puedes crear jails para otros servicios que registren fallos (Postfix, Dovecot, paneles de administración), combinar Fail2Ban con reglas de limitación de conexiones en nftables o centralizar sus logs en un SIEM como Wazuh.