Saltar a contenido

Configurar Fail2ban

UFW bloquea tráfico no deseado, pero no detecta intentos de intrusión repetidos. Fail2ban vigila los logs del servidor y, cuando un IP acumula demasiados fallos de autenticación o peticiones a rutas sensibles, crea una regla temporal en UFW para bloquearla.

Cómo funciona

Fail2ban ejecuta ciclos periódicos de lectura sobre los logs que se le indican:

  • Filtros — expresiones regulares que definen qué líneas del log son sospechosas (intentos de login fallidos, peticiones a /wp-config.php, etc.).
  • Jaulas — combinan un filtro con una acción: si una IP supera maxretry veces en el intervalo de tiempo definido, se aplica el baneo.
  • Acción ufw — en vez de manipular iptables directamente, Fail2ban crea y elimina reglas de UFW, manteniendo todo integrado.

Protección activa con jaulas integradas en UFW.

Enfoque por niveles

Cada nivel añade una jaula al mismo fichero jail.local. Puedes parar en el que quieras — el nivel 1 es la base, el resto depende de los servicios que tengas.

Nivel Qué añade ¿Aplicarlo?
1 Instalar + jaula SSH ([sshd]) Obligatorio
2 Autenticación web (apache-auth / nginx-http-auth) Solo si hay servidor web
3 Rutas sensibles (*-sensitive-paths) Recomendado si hay web
4 Ajustes: ignoreip y baneo incremental Opcional

Traefik en Docker

Si tu servidor web corre detrás de Traefik en contenedores, fail2ban no lee los logs del host de forma directa — la parte de servidor web de esta guía no cubre ese caso. Necesitarás montar los logs del contenedor en el host o usar un plugin externo (por ejemplo f2b-traefik). La jaula sshd del Nivel 1 sí aplica igualmente.

Nivel 1 — Jaula SSH

Obligatorio. Es la base de la protección: los intentos de fuerza bruta sobre SSH se bloquean automáticamente tras maxretry fallos.

  1. Comprobar si ya está instalado:

    dpkg -l | grep fail2ban
    
    rpm -qa | grep fail2ban
    
  2. Instalar si hace falta:

    sudo apt install fail2ban
    
    sudo dnf install fail2ban
    
  3. Comprobar que existe la acción ufw de fail2ban:

    ls /etc/fail2ban/action.d/ufw.conf
    

    Si no existe, usa banaction = iptables-multiport en vez de ufw en el paso siguiente.

  4. Crear jail.local desde cero (no copiar jail.conf — solo va aquí lo que sobreescribes):

    sudo vim /etc/fail2ban/jail.local
    

    Contenido:

    [DEFAULT]
    banaction = ufw
    
    [sshd]
    enabled = true
    port    = 22
    maxretry = 5
    bantime  = 3600
    

     

    Fail2ban trae el filtro sshd de serie — no hace falta crearlo a mano. maxretry = 5 y bantime = 3600 son puntos de partida sensatos; se afinan en el Nivel 4.

  5. Reiniciar y comprobar el estado:

    sudo service fail2ban restart
    sudo fail2ban-client status
    sudo fail2ban-client status sshd
    

Nivel 2 — Autenticación web

Solo si hay servidor web en la máquina (Apache o nginx). Bloquea los IPs que erran al autenticarse contra el panel o áreas restringidas.

  1. Añadir al jail.local ya creado:

    [apache-auth]
    enabled = true
    
    [nginx-http-auth]
    enabled = true
    

     

    Fail2ban trae de serie los filtros nginx-http-auth y nginx-botsearch (equivalentes a apache-auth/apache-badbots), así que no hace falta crearlos a mano.

  2. Aplicar y comprobar:

    sudo service fail2ban restart
    sudo fail2ban-client status apache-auth
    sudo fail2ban-client status nginx-http-auth
    

Nivel 3 — Rutas sensibles

Recomendado si hay servidor web, con coste de mantenimiento bajo. Bloquea a quien hurga en ficheros que delatan un servidor fuera de control: wp-config.php, .env, .git/config, xmlrpc.php...

  1. Crear el filtro para rutas sensibles:

    sudo vim /etc/fail2ban/filter.d/apache-sensitive-paths.conf
    

    Contenido:

    [Definition]
    failregex = ^<HOST> .* "(GET|POST) .*(wp-config\.php|\.env|\.git/config|xmlrpc\.php|\.htpasswd).*" (403|404)
    ignoreregex =
    
    sudo vim /etc/fail2ban/filter.d/nginx-sensitive-paths.conf
    

    Contenido (mismo patrón, adaptado al log de nginx):

    [Definition]
    failregex = ^<HOST> .* "(GET|POST) .*(wp-config\.php|\.env|\.git/config|xmlrpc\.php|\.htpasswd).*" (403|404)
    ignoreregex =
    
  2. Añadir la jaula al jail.local:

    [apache-sensitive-paths]
    enabled  = true
    port     = http,https
    filter   = apache-sensitive-paths
    logpath  = /var/log/apache2/*access.log
    maxretry = 1
    bantime  = 86400
    
    [nginx-sensitive-paths]
    enabled  = true
    port     = http,https
    filter   = nginx-sensitive-paths
    logpath  = /var/log/nginx/*access.log
    maxretry = 1
    bantime  = 86400
    
  3. Aplicar y comprobar:

    sudo service fail2ban restart
    sudo fail2ban-client status nginx-sensitive-paths
    

Nivel 4 — Ajustes

Opcional. Afina el comportamiento de todas las jaulas para tu estilo de administración.

  1. Excluir tus propias IPs (no banearte a ti) en [DEFAULT] dentro de jail.local:

    ignoreip = 127.0.0.1/8 <tu-ip-o-vpn>/<mascara>
    

    Si trabajas desde casa con IP dinámica, valora excluir tu VPN en vez de una IP concreta.

  2. Endurecer el baneo ante reincidencia — el baneo crece con cada reincidencia de la misma IP (Fail2ban 0.11+):

    bantime.increment  = true
    bantime.factor     = 2
    bantime.maxtime    = 86400
    
  3. Ajustar la sensibilidad: findtime (ventana de tiempo en la que cuentan los intentos) y maxretry. Ejemplo en la jaula sshd:

    [sshd]
    maxretry = 5
    findtime = 600
    

    Criterio: un servidor muy expuesto tolera menos intentos; un uso agresivo corta accesos legítimos con scripts que reintentan mucho.

  4. Aplicar y comprobar el estado:

    sudo service fail2ban restart
    sudo fail2ban-client status
    sudo ufw status numbered
    

Referencia

Estado de jail.local al completar los cuatro niveles (con servidor web nginx):

[DEFAULT]
banaction = ufw
ignoreip = 127.0.0.1/8 <tu-ip-o-vpn>/<mascara>
bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 86400

[sshd]
enabled  = true
port     = 22
maxretry = 5
bantime  = 3600
findtime = 600

[nginx-http-auth]
enabled = true

[nginx-sensitive-paths]
enabled  = true
port     = http,https
filter   = nginx-sensitive-paths
logpath  = /var/log/nginx/*access.log
maxretry = 1
bantime  = 86400

Si usas Apache, sustituye la jaula web por [apache-auth] y [apache-sensitive-paths] (con logpath = /var/log/apache2/*access.log), y recuerda el filtro creado en el Nivel 3.

Verificar

  1. Estado general y de cada jaula:

    sudo fail2ban-client status
    sudo fail2ban-client status sshd
    sudo ufw status numbered
    

Para una prueba en vivo sin esperar a un atacante real, fuerza maxretry fallos de login contra una IP de prueba y confirma que la regla aparece en ufw status numbered con la IP bloqueada.

Y ahora qué

Fail2ban está activo y bloqueando intentos sospechosos. El siguiente paso es configurar las Actualizaciones automáticas para mantener el sistema parcheado.