Seguridad

Protege tu SSH: Guía esencial para configurar Fail2ban en Linux

TL;DR — Lo que vas a tener al final

  • Tiempo de setup: 1-2 horas para un server nuevo.
  • Requisitos: Acceso root, 2 sesiones SSH abiertas durante cambios.
  • Resultado: Login por clave ed25519, fail2ban activo, 99% de ataques bloqueados.
  • Troubleshooting: Consola del proveedor, journalctl -u sshd, fail2ban-client status.

Por qué SSH sigue siendo el principal punto de entrada

Cualquier servidor Linux con el puerto 22 abierto hacia internet recibe intentos de brute-force las 24 horas del día. En una auditoría de 24 horas sobre un servidor recién desplegado, lo normal es ver entre 800 y 4000 intentos fallidos desde rangos de IP rotativos que cambian de ASN cada pocas horas. Si el daemon SSH no autentica con clave pública, una contraseña débil o reutilizada se rompe en horas.

Fail2ban (en su rama estable 1.1, con el release 1.1.1 publicado en agosto de 2026) es la contramedida clásica: escanea /var/log/auth.log o el journal de sshd, cuenta los fallos por IP dentro de una ventana móvil, y bloquea al infractor en el firewall durante un tiempo configurable. Es una capa defensiva esencial, y esta guía cubre exactamente lo que necesitas configurar para que tu servidor esté protegido sin trampas.

Antes de empezar: hardening mínimo de SSH

Fail2ban asume que tu servidor sigue reglas mínimas de hardening. Sin esto, una contraseña filtrada alcanza antes de que el baneo surta efecto. Ajusta estos valores en /etc/ssh/sshd_config y reinicia el servicio:

Port 2222                              # evita el 90% de los bots que solo prueban el 22
PermitRootLogin no
PasswordAuthentication no              # solo claves ed25519; si necesitas password, sube MaxAuthTries
MaxAuthTries 3
MaxSessions 4
LoginGraceTime 20
AllowGroups sshusers                   # grupo dedicado, no 'users' ni 'sudo'

Si dejas PasswordAuthentication yes, Fail2ban igual ayuda, pero queda un hueco: bots con listas de contraseñas gigantes pueden acertar antes del cuarto intento. Activa las claves SSH y desactiva el password login por completo. El grupo sshusers aísla quién puede entrar vía SSH aunque el resto del sistema sea accesible por otras vías.

Instalación: apt, dnf o pacman

El paquete está en los repos oficiales de las cuatro distribuciones principales. Instala, activa y arranca:

# Debian / Ubuntu
sudo apt update && sudo apt install fail2ban
# Fedora / RHEL
sudo dnf install fail2ban
# Arch
sudo pacman -S fail2ban

Confirma que está corriendo. Fail2ban lleva un socket y un socket de archivo en /var/run/fail2ban/ que supervisan el estado de cada jail:

sudo systemctl enable --now fail2ban
sudo fail2ban-client status
# Status
# |- Number of jail: 0    ← al inicio todos los jails están desactivados
# `- ...

Si Number of jail es 0, es normal: por defecto ningún jail viene habilitado. Lo activamos a continuación.

Por qué jail.local y no jail.conf

El archivo maestro /etc/fail2ban/jail.conf se reescribe con cada apt upgrade. Si editas sobre él, tus cambios se pierden silenciosamente en la próxima actualización. Por eso las buenas prácticas (y la advertencia en el propio comentario de cabecera del archivo) dicen: nunca toques jail.conf; crea /etc/fail2ban/jail.local con tus overrides.

Cómo se interpreta: Fail2ban primero lee jail.conf, después jail.d/*.conf en orden alfabético, y al final jail.local. Las secciones del .local sobrescriben las mismas secciones del .conf, dejando el resto intacto. Es el equivalente a /etc/ssh/sshd_config sobre el sshd_config por defecto.

Configuración esencial: ignorar tu IP, endurecer el resto

Crea el archivo y aplica la configuración mínima viable. Esta es la versión que debes aplicar en cualquier servidor de producción expuesto a internet:

sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# IPs que NUNCA se bloquean: tu IP fija, rango de oficina, redes privadas
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
# Defaults globales - endurecidos respecto al original (10 min, 5 reintentos)
bantime  = 1h
findtime = 10m
maxretry = 3
# Tiempo incremental: reincidentes reciben bans cada vez más largos
bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 1w
[sshd]
enabled    = true
mode       = aggressive
filter     = sshd
backend    = systemd
port       = ssh
logpath    = %(sshd_log)s
# Jail acumulativo: hosts reincidentes reciben ban de 1 semana
[sshd-aggressive]
enabled = true
filter  = sshd
mode    = aggressive
backend = systemd
port    = ssh
logpath = %(sshd_log)s
findtime = 1d
maxretry = 10
bantime  = 1w
[recidive]
enabled  = true
filter   = recidive
logpath  = /var/log/fail2ban.log
bantime  = 1w
findtime = 1d
maxretry = 5

El parámetro ignoreip es no negociable. Un atacante que conozca tu dirección puede enviar paquetes con la IP de origen falsificada usando herramientas como hping3, logrando que tu propia IP quede baneada y te quedes fuera del servidor. La advertencia oficial del ArchWiki es tajante: “make sure to specify your IP in ignoreip“. Con tu IP en la lista blanca, el resto del endurecimiento opera sobre el tráfico real.

Los parámetros críticos y por qué se eligen así:

Parámetro Default upstream Valor endurecido Por qué
bantime 10 min (600 s) 1 h Suficiente para disuadir bots lentos, no molesta a humanos con errores tipográficos
findtime 10 min 10 min (mismo) Ventana móvil en la que cuentan los intentos fallidos
maxretry 5 3 Bots prueban rápido; un humano tarda segundos en notar que falla
bantime.increment false true El reincidente recibe bans de 2 h, 4 h, 8 h, hasta un máximo de 1 semana
mode = aggressive (no en jail.conf) aggressive Banea también al host que conecta a puertos no-SSH una vez baneado (lo aplica banaction_allports)

El jail recidive es la pieza que detecta reincidencia. Si una IP fue baneada por SSH y vuelve a aparecer baneada por cualquier otro jail dentro del día, recibe un ban de una semana directamente. Cierra el ciclo de los bots que cambian de táctica cuando notan el bloqueo.

Recuperarte si te baneaste a ti mismo

El escenario clásico: configuras Fail2ban, olvidaste poner tu IP en ignoreip, escribes mal la clave tres veces y quedas afuera. El sshd_config sigue rechazándote porque Fail2ban bloqueó tu IP en iptables/nftables, y ya no puedes entrar por SSH para corregirlo.

Tienes tres salidas según el nivel de acceso que conserves:

  1. Consola KVM/IPMI del proveedor. Lo más limpio: te conectas vía interfaz web del proveedor (Hetzner, OVH, OCI, AWS vía Session Manager) y ejecutas sudo fail2ban-client set sshd unbanip TU.IP.
  2. Otra IP que sí esté en ignoreip. Válida si tu servidor tiene un jump-host o un rango de IP de respaldo whitelistado.
  3. Acceso por consola al servidor (VNC, consola física, máquina virtual en KVM). Inicias sesión local y ejecutas el mismo comando: sudo fail2ban-client set sshd unbanip TU.IP.

Una vez dentro, edita /etc/fail2ban/jail.local, agrega tu IP a ignoreip, y recarga: sudo systemctl reload fail2ban (en realidad necesitas restart porque reload no toma cambios en esta sección; la advertencia está en el propio changelog oficial).

Cuándo Fail2ban no es la herramienta correcta

Hay tres escenarios donde Fail2ban ayuda poco o estorba:

  • Servidor que solo acepta clave pública. Si tu sshd_config tiene PasswordAuthentication no, no hay contraseñas que bruteforcear. Fail2ban queda como red de seguridad contra reconnaissance, pero no necesitas sus alarmas diarias.
  • Servidor detrás de WireGuard/Tailscale y sin 22 expuesto. El atacante nunca llega al puerto SSH. Mejor invierte el tiempo en monitorizar el endpoint VPN y desactiva el jail SSH por completo.
  • Bots distribuidos a escala de botnet. Cada IP solo prueba una o dos veces; maxretry nunca se alcanza. Aquí la herramienta correcta es CrowdSec o un WAF que correlacione a nivel de ASN, no por IP individual.

Para servidores SSH abiertos a internet sin 2FA, Fail2ban es el mínimo; combínalo con MaxAuthTries 3, claves ed25519, y un 2FA TOTP como segunda capa. La defensa en profundidad es lo que evita que un solo exploit de un componente sea catastrófico.

Verificación rápida

Después de aplicar la configuración, espera cinco minutos y comprueba:

sudo fail2ban-client status sshd
# Status for the jail: sshd
# |- Filter
# |  |- Currently failed: 0
# |  |- Total failed:     0
# |  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
# `- Actions
#    |- Currently banned: 0
#    |- Total banned:     0
#    `- Banned IP list:

Para forzar que el jail cuente un fallo y se auto-verifique:

# Desde otra máquina, intenta entrar con clave equivocada 3 veces
ssh -o NumberOfPasswordPrompts=3 -v usuario@TUSERVIDOR
# Vuelve a chequear
sudo fail2ban-client status sshd
#   `- Banned IP list:  203.0.113.45    ← la IP aparece
sudo fail2ban-client get sshd banip 203.0.113.45
# 1                          ← segundos restantes de ban

Si ves el ban y el conteo subir, todo funciona. Si Currently failed permanece en 0 después del intento fallido, revisa que el journalctl esté leyendo el servicio correcto (backend = systemd); un logpath apuntando a un archivo que no existe provoca que el filter no vea nada y nunca se active.

Resumen

La configuración esencial de Fail2ban para SSH se reduce a cuatro puntos:

  1. Edita /etc/fail2ban/jail.local, nunca jail.conf.
  2. Lista tu IP en ignoreip antes de activar nada.
  3. Activa los jails sshd, sshd-aggressive y recidive.
  4. Verifica que backend = systemd corresponda a tu distribución; en Debian/Ubuntu con journald nativo es obligatorio.

Fail2ban no es la única capa, pero es la capa que detiene el 99% del ruido automatizado sin intervención manual. Con esa configuración endurecida, tu servidor pasa de aparecer en listas de “low-hanging fruit” de cualquier botnet a ser ignorado por la mayoría. Complementa con claves SSH, 2FA, y un firewall que bloquea todo lo que no necesitas; el resto de la seguridad ya es arquitectura, no configuración.

Preguntas frecuentes

¿Por qué Protege tu SSH: Guía esencial para configurar Fail2ban en Linux es relevante para sysadmins Linux?

Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.

¿Qué necesito para empezar con Protege tu SSH: Guía esencial?

Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.

¿Cuáles son los errores más comunes con Protege tu SSH: Guía esencial?

Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.

¿Cuándo usar Protege tu SSH: Guía esencial y cuándo una alternativa?

Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.

¿Cómo migro desde una configuración previa a Protege tu SSH: Guía esencial?

Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *