Qué hace
fail2ban lee los registros. Cuando una dirección falla al iniciar sesión maxretry veces dentro de findtime, añade una regla de cortafuegos que bloquea esa dirección durante bantime. Nada más. No inspecciona el tráfico SSH y no convierte en fuerte una contraseña débil.
Instalar
sudo apt install fail2ban python3-systemd
python3-systemd le permite leer el journal de systemd. En los sistemas de la familia Red Hat el paquete está en EPEL. En Debian 13 y Ubuntu 24.04 el paquete ya activa la jail sshd, con el backend systemd y nftables, mediante /etc/fail2ban/jail.d/defaults-debian.conf.
Configurar: jail.local
No edites jail.conf, porque las actualizaciones pueden reemplazarlo. Pon tus ajustes en /etc/fail2ban/jail.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.10
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
backend = systemd
| Ajuste | Significado | Valor por defecto del paquete |
|---|---|---|
bantime | Cuánto dura un bloqueo | 10m |
findtime | La ventana en la que se suman los fallos | 10m |
maxretry | Fallos permitidos en esa ventana | 5 |
ignoreip | Direcciones que nunca se bloquean | ninguna |
backend | De dónde vienen los registros | auto |
Añade tu propia dirección fija a ignoreip, para que unos pocos errores al teclear no te dejen fuera. Si tu dirección cambia, asegúrate de tener una consola a la que recurrir.
Usa backend = systemd donde SSH registra en el journal, como en el Debian actual, que puede no tener ningún /var/log/auth.log. En un sistema que escribe ese archivo, puedes usar backend = auto y logpath.
Si sshd escucha en otro puerto, añade port = 2222 a la jail. La regla que vimos coincidía solo con el puerto indicado en el ajuste de la jail (tcp dport 22), así que un bloqueo en el puerto equivocado no bloquea nada.
Prueba el archivo y luego reinicia:
sudo fail2ban-client -t
sudo systemctl restart fail2ban
Comprueba que funciona de verdad
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 4
| `- Journal matches: _SYSTEMD_UNIT=ssh.service + _COMM=sshd
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 203.0.113.20
Haz una prueba real. Desde otra dirección, que no sea el propio servidor ni una de ignoreip, falla un acceso con contraseña y ejecuta el comando otra vez. Total failed debe subir. Si se queda en 0, la jail no está leyendo los registros correctos y el resto no sirve de nada. Compara la coincidencia del journal con el nombre de tu unidad, ssh.service o sshd.service, y comprueba con journalctl -u ssh -n 20.
Una vez bloqueado, el cliente ve esto:
ssh: connect to host 203.0.113.10 port 22: Connection refused
La regla del cortafuegos se puede ver con sudo nft list ruleset:
table inet f2b-table {
set addr-set-sshd {
type ipv4_addr
elements = { 203.0.113.20 }
}
chain f2b-chain {
type filter hook input priority filter - 1; policy accept;
tcp dport 22 ip saddr @addr-set-sshd reject with icmp port-unreachable
}
}
Comandos del día a día
| Objetivo | Comando |
|---|---|
| Listar las jails | sudo fail2ban-client status |
| Desbloquear una dirección | sudo fail2ban-client set sshd unbanip 203.0.113.20 |
| Desbloquear todo | sudo fail2ban-client unban --all |
| Bloquear a mano | sudo fail2ban-client set sshd banip 203.0.113.20 |
| Ver los bloqueos y cuándo terminan | sudo fail2ban-client get sshd banip --with-time |
| Ignorar una dirección ahora | sudo fail2ban-client set sshd addignoreip 203.0.113.10 |
| Leer los ajustes en uso | sudo fail2ban-client get sshd bantime |
| Leer su registro | sudo tail /var/log/fail2ban.log |
El cambio de addignoreip dura solo hasta la siguiente recarga. Añádelo a jail.local para conservarlo.
Qué cuenta como fallo
Probamos el modo por defecto en OpenSSH 10.0 con estos resultados:
- Una contraseña incorrecta contó, igual que una contraseña incorrecta para un usuario que no existe.
- Una desconexión por Too many authentication failures contó como una.
- Las conexiones que ofrecían una clave incorrecta y luego se rendían no contaron. Aparecen en el registro como
Connection closed by authenticating user.
Con mode = aggressive en la jail [sshd], las conexiones con clave incorrecta también se contaron. Detecta más, y también facilita bloquear a alguien con un cliente mal configurado, así que mantén ignoreip al día.
Frente a qué no protege
- Una clave robada o una contraseña filtrada. Un acceso correcto no es un fallo, así que no hay bloqueo.
- Intentos lentos o distribuidos. Muchas direcciones que hacen uno o dos intentos cada una se quedan por debajo del límite.
- Una debilidad en el propio sshd. Reacciona a líneas de registro, una vez ocurrido el hecho.
- Otros servicios, salvo que tengan sus propias jails.
También puede perjudicarte: todos los que están detrás de un NAT de oficina comparten una dirección, así que los errores de una persona pueden dejar fuera a toda la oficina.
Si una clave se filtra, hay que saber dónde está instalada y quitar la clave SSH de cada servidor.
Lo que importa más
Si el servidor solo acepta claves, adivinar contraseñas no tiene nada que adivinar, sea cual sea el número de intentos. Así que haz esto primero:
- Usa una clave y desactiva el acceso por contraseña.
- Limita quién puede llegar al puerto 22 con un cortafuegos o una VPN, si puedes.
- Revisa el resto de tus ajustes de sshd.
Después de eso, fail2ban es un extra útil que mantiene los registros más tranquilos. Además, OpenSSH 9.8 y posteriores tienen su propio PerSourcePenalties, activado por defecto, que rechaza durante un tiempo corto las direcciones que se portan mal. Es mucho más corto que un bloqueo típico de fail2ban, así que ambos funcionan bien juntos.
Preguntas
¿Cómo desbloqueo una IP en fail2ban?
Ejecuta sudo fail2ban-client set sshd unbanip seguido de la dirección. sudo fail2ban-client unban --all quita todos los bloqueos de todas las jails.
¿Cómo compruebo que fail2ban funciona?
Ejecuta sudo fail2ban-client status sshd, falla un inicio de sesión desde otra dirección y vuelve a ejecutarlo. Total failed debe subir. Si se queda en 0, la jail no está leyendo los logs de SSH.
¿Por qué un cliente bloqueado ve Connection refused?
Con la acción nftables por defecto en Debian, una dirección bloqueada recibe un rechazo, así que ssh imprime Connection refused en lugar de un tiempo de espera agotado. Una acción que descarte paquetes se vería como un timeout.
¿Cuentan como fallos las claves incorrectas?
No en el modo por defecto. En nuestra prueba, cinco conexiones con una clave incorrecta no sumaron nada al contador. mode = aggressive en la jail sí las contó.
Fuentes
- jail.conf(5), fail2ban
- Filtro sshd, fail2ban
- Documentación de Fail2Ban
- sshd_config(5), PerSourcePenalties