fail2ban su un VPS: ban automatici senza restare chiusi fuori

Un VPS con la porta 22 esposta riceve tentativi di login continuamente. Non è un attacco mirato: sono scanner automatici che provano root, admin, test su interi blocchi di indirizzi. Non entrano, se l’autenticazione è a chiave, ma riempiono i log e rendono illeggibile tutto il resto.
fail2ban serve a questo. Non è un firewall e non è un antivirus: è un programma che legge i log, conta i fallimenti e scrive una regola nel firewall quando un indirizzo supera la soglia. Il file che lo governa, jail.conf, è descritto nel manuale come la “configuration for the fail2ban server” che definisce “combinations of Filters with Actions”: un filtro che riconosce la riga di log, un’azione che la trasforma in un ban.
Quello che segue è verificato sul pacchetto Debian 1.1.0-8, quello di trixie. È importante perché su Debian tre cose funzionano diversamente da come le racconta quasi tutta la documentazione in giro.
Su Debian il jail sshd è già attivo
Il jail.conf che arriva da upstream disattiva tutto. Nella sezione [DEFAULT] c’è enabled = false, e la sezione dell’SSH non ha nessuna riga enabled:
[sshd]
#mode = normal
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
Letto così, fail2ban appena installato non fa niente. Ed è esattamente il punto in cui le guide ti dicono di creare un jail.local con [sshd] e enabled = true. Su Debian non serve, perché il pacchetto installa un file suo in /etc/fail2ban/jail.d/defaults-debian.conf, che contiene per intero questo:
[DEFAULT]
banaction = nftables
banaction_allports = nftables[type=allports]
[sshd]
backend = systemd
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd
enabled = true
Il README.Debian del pacchetto lo dice in una riga: “Only handling of ssh files is enabled by default”. Da qui scendono tre conseguenze, e sono i tre punti su cui si perde tempo.
Il jail non legge /var/log/auth.log. Legge il journal di systemd, perché Debian imposta backend = systemd con il journalmatch che vedi sopra. Il file paths-debian.conf spiega la scelta: “these services will log to the journal via syslog, so use the journal by default”. Quindi ogni consiglio del tipo “metti logpath = /var/log/auth.log” qui è sbagliato, e il pacchetto dipende da python3-systemd proprio perché quel backend funzioni senza installare nulla.
Il ban non è una regola iptables. Upstream il default è banaction = iptables-multiport; Debian lo sovrascrive con nftables. Se cerchi il ban con iptables -L non trovi niente e concludi che fail2ban non funziona, mentre invece sta funzionando. Il pacchetto dichiara nftables | iptables fra i Recommends, in quest’ordine.
Non devi abilitare niente per l’SSH. Devi solo decidere i numeri.
Dove si mette la configurazione
L’intestazione di jail.conf è esplicita: “YOU SHOULD NOT MODIFY THIS FILE. It will probably be overwritten or improved in a distribution update”, e indica la strada giusta — “Provide customizations in a jail.local file or a jail.d/customisation.local”.
L’ordine di lettura, dal manuale, è questo:
jail.confjail.d/*.conf, in ordine alfabeticojail.localjail.d/*.local, in ordine alfabetico
con la regola che conta: “Settings in the file parsed later take precedence over identical entries in previously parsed files”. Nota dove cade jail.local: dopo jail.d/*.conf, quindi dopo defaults-debian.conf. Le tue impostazioni in jail.local vincono su quelle del pacchetto, e non devi toccare nessuno dei due file che l’aggiornamento riscriverà.
I tre numeri che contano
Nel [DEFAULT] del file installato ci sono tre righe:
bantime = 10m
findtime = 10m
maxretry = 5
Il manuale le definisce così: bantime è la “effective ban duration (in seconds or time abbreviation format)”, findtime il “time interval (in seconds or time abbreviation format) before the current time where failures will count towards a ban”, maxretry il “number of failures that have to occur in the last findtime seconds to ban the IP”.
Tradotto: cinque fallimenti in dieci minuti, e l’indirizzo resta fuori per dieci minuti. È una soglia prudente, pensata per non fare danni.
Attenzione al formato del tempo, che è una trappola vera: nel formato abbreviato m e mm indicano i minuti, i mesi si scrivono mo o mon. Un bantime = 1m non è un mese di ban, è un minuto. Se hai un dubbio, il client lo calcola per te: fail2ban-client --str2sec 1d12h.
Nel file installato esistono anche le righe del ban progressivo, ma sono commentate — cioè non attive:
#bantime.increment = true
#bantime.multipliers = 1 2 4 8 16 32 64
Vale la pena saperlo prima di dare per scontato che il ban si allunghi da solo a ogni recidiva: di default non lo fa.
Non bannare te stesso
Questa è la parte da sistemare per prima, perché l’errore si paga subito. Nel file installato la riga degli indirizzi esentati è commentata:
#ignoreip = 127.0.0.1/8 ::1
Quello che è attivo è un altro meccanismo, ignoreself, che il manuale descrive come “boolean value (default true) indicates the banning of own IP addresses should be prevented”. Leggi bene: own IP addresses, gli indirizzi del server. Protegge la macchina dal bannare se stessa, non protegge te. Il tuo indirizzo di casa o dell’ufficio non c’entra niente con ignoreself.
Se vuoi un’esenzione, va scritta, in jail.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 198.51.100.7
ignoreip è la “list of IPs not to ban. They can include a DNS resp. CIDR mask too”. Con un’avvertenza da tenere presente: se la tua connessione ha un IP dinamico, quell’esenzione scade senza preavviso il giorno in cui il provider te lo cambia. Non è una rete di sicurezza. La rete di sicurezza vera è la console di emergenza del pannello del tuo provider, che passa fuori dalla rete e quindi fuori dal firewall: verifica che funzioni prima di toccare le regole, non dopo.
Il comando di unban, da imparare adesso
Questo è il motivo per cui conviene leggere questo paragrafo oggi e non quando ti serve. fail2ban-client è il programma per “configure and control the server”, e i comandi che contano sono quattro.
Per vedere cosa sta facendo:
fail2ban-client status
fail2ban-client status sshd
Il primo “gets the current status of the server”, il secondo “gets the current status of status --all [FLAVOR], che fa la stessa cosa su tutti i jail.
Per liberare un indirizzo:
fail2ban-client unban 198.51.100.7
fail2ban-client set sshd unbanip 198.51.100.7
Il primo “unbans unban --all, che “unbans all IP addresses (in all jails and database)”.
Per rileggere la configurazione senza fermare il servizio c’è reload, documentato con la sua opzione più brusca: “reloads the configuration without restarting of the server, the option ‘–restart’ activates completely restarting of affected jails, thereby can unban IP addresses (if option ‘–unban’ specified)”.
Cosa fail2ban non è
Non è un’alternativa all’autenticazione a chiave, e presentarlo così è il modo più rapido per prendere una decisione sbagliata. La soglia di maxretry vale per indirizzo: una botnet con diecimila IP diversi ha diecimila volte cinque tentativi, e ogni singolo indirizzo resta sotto la soglia senza sforzo. Contro quello non ti difende il conteggio dei fallimenti, ti difende il fatto che non ci sia una password da indovinare — PasswordAuthentication no nella configurazione di SSH.
Quello che fail2ban fa davvero, e che vale comunque la fatica di configurarlo, è ridurre il rumore: meno righe nei log, meno banda consumata, meno carico sul demone SSH, e soprattutto log in cui un tentativo anomalo si vede ancora perché non è sepolto sotto diecimila tentativi banali. È un filtro, non una porta blindata. La porta blindata è la chiave.
Se gestisci un VPS e non sei sicuro di come sia configurato — quali porte sono aperte, se gli aggiornamenti di sicurezza entrano da soli, se esiste un modo di rientrare quando qualcosa va storto — possiamo guardarlo insieme. Scrivici.
