Il rumore di fondo di internet
Basta esporre un server Linux con SSH o un pannello di login web e, nel giro di poche ore, i log di autenticazione iniziano a riempirsi di tentativi falliti: scanner automatici che provano credenziali comuni su SSH, bot che martellano i form di login di WordPress, richieste che cercano endpoint vulnerabili. Non è un attacco mirato: è rumore costante e automatizzato a cui ogni IP pubblico è esposto, ventiquattr’ore su ventiquattro.
Fail2ban resta la risposta più pragmatica a questo problema da oltre quindici anni. Osserva i file di log (o il journal di systemd), riconosce pattern di autenticazione fallita e, superata una soglia configurabile, banna l’IP a livello di firewall. È leggero, flessibile e presente nei repository di ogni distribuzione. In questo articolo vediamo come installarlo, configurarlo correttamente — evitando l’errore più comune, cioè modificare il file sbagliato — e alcune tecniche di tuning che fanno la differenza tra una protezione reale e un servizio che gira senza incidere davvero.
Come funziona, in tre concetti
Fail2ban si basa su tre elementi che vale la pena avere chiari prima di toccare la configurazione:
- Filter: un insieme di pattern regex che riconoscono le righe di log corrispondenti a un fallimento di autenticazione.
- Jail: combina un filtro con un percorso di log, le soglie di attivazione e l’azione da eseguire.
- Action: cosa succede al superamento della soglia — tipicamente un ban a livello di firewall, ma può includere anche una notifica email.
Fail2ban include già filtri e jail pronti per decine di servizi: SSH, Apache, Nginx, Postfix, Dovecot e altri. Nella maggior parte dei casi basta abilitare le jail che servono e regolare pochi parametri numerici.
Installazione
# Debian / Ubuntu
sudo apt update
sudo apt install fail2ban
# Fedora / RHEL 9+ / Rocky / AlmaLinux
sudo dnf install fail2ban
# Arch Linux
sudo pacman -S fail2ban
Abilitazione e avvio del servizio:
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
Se lo stato non riporta active (running), i log spiegano quasi sempre il motivo:
sudo journalctl -u fail2ban -n 50
Il modo corretto di configurare Fail2ban
Il primo errore, molto comune tra chi lo usa per la prima volta, è modificare direttamente /etc/fail2ban/jail.conf. Quel file viene sovrascritto ad ogni aggiornamento del pacchetto, e tutte le modifiche vanno perse silenziosamente al primo upgrade.
L’approccio corretto è creare un file separato nella directory jail.d:
sudo nano /etc/fail2ban/jail.d/custom.conf
In alternativa, si può copiare il file di default e modificare la copia:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Le impostazioni nei file sotto jail.d/ e in jail.local sovrascrivono quelle di default in jail.conf. Usate sempre uno di questi due metodi, mai il file originale.
La sezione [DEFAULT]: i parametri che contano
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
Vale la pena capire bene ciascun valore:
- bantime: durata del ban. Il default di molte distribuzioni è 10 minuti, decisamente troppo poco. Un’ora è un minimo ragionevole; per attaccanti persistenti si può salire a 24 ore o anche una settimana.
- findtime: la finestra temporale in cui vengono contati i fallimenti. Con i valori di esempio, 5 fallimenti in 10 minuti fanno scattare il ban.
- maxretry: numero di fallimenti prima del ban. 5 è ragionevole per SSH; si può scendere a 3 per una protezione più aggressiva.
- ignoreip: IP che non verranno mai bannati. Aggiungete sempre il vostro IP qui prima di abilitare qualsiasi jail — restare bloccati fuori dal proprio server è un problema fastidioso da risolvere da remoto.
Se il server ha anche un indirizzo IPv6 pubblico, includetelo in ignoreip: Fail2ban supporta IPv6, ma alcuni filtri più datati riconoscono solo pattern IPv4, quindi vale la pena verificare che le jail intercettino entrambi i protocolli.
ignoreip = 127.0.0.1/8 ::1 VOSTRO.IP.PUBBLICO
Nota: bantime accetta anche il valore -1 per un ban permanente. Da usare con cautela, perché un errore di configurazione può bloccare IP legittimi in modo definitivo.
Jail SSH, Apache e Nginx
La jail SSH è quella più importante per la maggior parte dei server, anche se in alcune distribuzioni va abilitata esplicitamente:
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 1h
Se SSH è stato spostato su una porta non standard (buona pratica), aggiornate la riga port:
port = 2222
Sui sistemi basati su systemd, la variabile %(sshd_log)s punta automaticamente al journal. Sui sistemi più datati che scrivono su /var/log/auth.log o /var/log/secure, Fail2ban gestisce la differenza tramite il parametro backend.
Per i server web, Apache e Nginx attirano un tipo di abuso diverso: scanner di endpoint 404, bruteforcer di login, bot che generano richieste inutili.
# Apache
[apache-auth]
enabled = true
logpath = %(apache_error_log)s
maxretry = 5
[apache-badbots]
enabled = true
logpath = %(apache_access_log)s
maxretry = 2
# Nginx
[nginx-http-auth]
enabled = true
logpath = %(nginx_error_log)s
maxretry = 3
[nginx-limit-req]
enabled = true
logpath = %(nginx_error_log)s
maxretry = 10
La jail nginx-limit-req intercetta i client che superano i limiti impostati con limit_req nella configurazione Nginx: una combinazione utile se avete già lavorato sul tuning delle performance del web server. Se Fail2ban segnala che un percorso di log non esiste, impostatelo esplicitamente, ad esempio logpath = /var/log/nginx/error.log.
Dopo ogni modifica, ricaricate la configurazione:
sudo fail2ban-client reload
Verificare lo stato delle jail e i ban attivi
sudo fail2ban-client status
Status
|- Number of jail: 3
`- Jail list: nginx-http-auth, sshd, apache-badbots
Per il dettaglio di una singola jail:
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 143
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 5
|- Total banned: 38
`- Banned IP list: 203.0.113.7 198.51.100.22 ...
Centoquaranta tentativi falliti in pochi giorni non sono un’anomalia: su un server esposto a internet è la norma, ed è proprio per questo che Fail2ban è utile.
Ban e unban manuali sono comandi da tenere a portata di mano:
sudo fail2ban-client set sshd banip 203.0.113.99
sudo fail2ban-client set sshd unbanip 203.0.113.99
La jail recidive: bloccare chi torna
Una delle funzionalità meno usate ma più efficaci è la jail recidive, che osserva il log di Fail2ban stesso e banna in modo più severo gli IP che, dopo un ban scaduto, ricominciano subito.
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
action = %(action_mwl)s
bantime = 1w
findtime = 1d
maxretry = 5
Con questa configurazione, un IP bannato 5 volte in un giorno riceve un ban di una settimana. È quanto di più vicino a una blocklist persistente di attaccanti si possa ottenere senza ricorrere a feed di threat intelligence esterni. Se il sistema non scrive su /var/log/fail2ban.log (setup solo journal), impostate backend = systemd nella jail recidive.
Testare i filtri prima di fidarsi
Prima di abilitare una jail, conviene verificare che il filtro corrisponda davvero alle righe di log presenti sul sistema:
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
L’output mostra quante righe sono state riconosciute e quali IP sono stati estratti. Un filtro che non intercetta nulla non protegge nulla — vale soprattutto quando si scrivono filtri personalizzati per applicazioni custom, che vivono in /etc/fail2ban/filter.d/:
# /etc/fail2ban/filter.d/miaapp-auth.conf
[Definition]
failregex = ^ .* "POST /login" 401
ignoreregex =
Il tag <HOST> è obbligatorio in un filtro reale: Fail2ban lo sostituisce con una regex che cattura l’indirizzo IP da bannare. Senza, il filtro non estrae nulla di utile. Tenete la regex il più specifica possibile: un pattern troppo largo rischia di bannare traffico legittimo.
nftables e firewalld: adattare il backend
Su Debian 12+ e Ubuntu 22.04+, nftables è il backend firewall predefinito. L’azione di default di Fail2ban usa ancora iptables, che sui sistemi moderni funziona tramite il layer di compatibilità iptables-nft. Su installazioni nftables “pure”, senza quel layer, va impostata esplicitamente l’azione corretta:
[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports
Su RHEL, Fedora e Rocky, dove il firewall è gestito da firewalld, serve analogamente:
[DEFAULT]
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-allports
Verificare quale sia effettivamente attivo evita ban silenziosamente inefficaci: sudo nft list ruleset per nftables, sudo systemctl status firewalld per firewalld.
Ban persistenti e notifiche email
Per default i ban vivono in memoria e un riavvio del server li cancella tutti. Per renderli persistenti:
[DEFAULT]
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 7d
Su Debian 12+, Ubuntu 22.04+ e Fedora 38+ il database SQLite è già abilitato per default. Questo stesso database alimenta anche la jail recidive, quindi il parametro conta doppio se la usate.
Per ricevere una notifica email ad ogni ban (richiede un setup di invio funzionante, ad esempio postfix o msmtp):
# Solo ban:
action = %(action_)s
# Ban + notifica email:
action = %(action_mw)s
# Ban + email con le righe di log rilevanti:
action = %(action_mwl)s
[DEFAULT]
destemail = voi@vostrodominio.it
sender = fail2ban@vostroserver.it
Una configurazione di partenza completa
[DEFAULT]
bantime = 2h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 7d
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 6h
[nginx-http-auth]
enabled = true
logpath = %(nginx_error_log)s
maxretry = 4
[nginx-limit-req]
enabled = true
logpath = %(nginx_error_log)s
maxretry = 10
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 1w
findtime = 1d
maxretry = 5
sudo fail2ban-client reload
sudo fail2ban-client status
Cosa Fail2ban non risolve
Fail2ban è reattivo, non preventivo: banna dopo che l’attacco è già in corso. Non copre attacchi brute-force distribuiti su migliaia di IP diversi (con uno o due tentativi ciascuno), exploit zero-day che non generano righe di log, o attacchi a livello applicativo che non falliscono l’autenticazione in modo riconoscibile.
Per una protezione a più livelli, Fail2ban va affiancato ad autenticazione SSH tramite chiave (disabilitando del tutto l’autenticazione a password), un firewall configurato correttamente e revisioni periodiche dei log. Vale anche la pena controllare i limiti di sistema (file descriptor) se il volume di ban è elevato, per evitare che sia Fail2ban stesso a saturare le risorse.
Riferimento rapido
sudo fail2ban-client status # elenco jail
sudo fail2ban-client status sshd # stato di una jail
sudo fail2ban-client set sshd banip 1.2.3.4 # ban manuale
sudo fail2ban-client set sshd unbanip 1.2.3.4 # unban
sudo fail2ban-client reload # ricarica config
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf # test filtro
sudo tail -f /var/log/fail2ban.log # ban in tempo reale
Conclusione
Fail2ban è uno di quegli strumenti che si guadagnano un posto fisso su ogni server Linux esposto a internet: l’installazione richiede pochi minuti e, anche con un tuning minimo, elimina una quantità enorme di rumore da scanner SSH e probe web. La differenza pratica maggiore la fanno tre accorgimenti: impostare un bantime sensato (i 10 minuti di default sono quasi inutili), aggiungere sempre il proprio IP a ignoreip prima di abilitare le jail, e attivare la jail recidive. Da soli, questi tre passaggi migliorano drasticamente l’efficacia di Fail2ban rispetto a un’installazione lasciata ai valori di default.
Fonte originale: LinuxBlog.io – Fail2ban on Linux: Protect Your Server from Brute-Force Attacks