Configurare il firewall UFW senza bloccarsi fuori
L'ordine corretto per attivare UFW, le regole IPv6, nftables come backend, la limitazione con ufw limit e la via di ritorno tramite console quando qualcosa va storto lo stesso.
Su un server root un filtro di pacchetti non è un optional, è dotazione di base. UFW (Uncomplicated Firewall) rende la cosa piacevolmente semplice, ma ha una caratteristica che ogni anno chiude migliaia di amministratori fuori dai loro stessi server: il comando che arma il firewall è lo stesso che può troncare la sessione SSH in corso. Questa guida mostra l'ordine con cui questo non succede e, cosa quasi più importante, la via di ritorno se dovesse succedere lo stesso.
Tutte le indicazioni valgono per Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS e Ubuntu 22.04 LTS. I comandi sono scritti per l'esecuzione come root. Se lavori come utente normale, anteponi sudo a ogni comando.
Perché l'ordine decide tutto
L'errore classico si presenta così: qualcuno imposta prima la politica predefinita su "scarta tutto il traffico in entrata", accende il firewall e poi vuole aggiungere con calma la regola per SSH. Proprio in quell'intervallo si apre la finestra in cui il server smette di essere raggiungibile.
Il motivo per cui questo errore passa così spesso inosservato è più insidioso dell'errore stesso. In /etc/ufw/before.rules UFW porta con sé una regola che lascia passare le connessioni già stabilite:
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
La tua sessione SSH attuale è una connessione già stabilita. Sopravvive quindi all'accensione del firewall anche quando non esiste alcuna regola per SSH. Il prompt resta lì, sembra tutto a posto. È solo il tentativo di connessione successivo, cioè tipicamente il tuo accesso della mattina dopo, che finisce in timeout. Per questo vale il principio: finché il firewall non è verificato con una seconda sessione appena aperta, non chiudere la prima.
Regola pratica: prima permettere, poi scartare, poi attivare, poi verificare con una seconda sessione e solo a quel punto chiudere la prima.
Prima del primo comando: via di fuga e inventario
Prima di toccare qualsiasi cosa nel filtraggio dei pacchetti, chiarisci due punti.
1. Come arrivi al server senza SSH?
Con i server root KVM e i server dedicati di KernelHost la console VNC è raggiungibile direttamente nell'area clienti. Questa console non dipende dallo stack di rete del sistema ospite, ma dal livello di virtualizzazione o dal collegamento di rete stesso. Una regola firewall dentro il sistema ospite non può quindi bloccarla. Accedi una volta prima tramite questa console e assicurati di conoscere la password di root. Una via di fuga che si prova per la prima volta solo in emergenza non è una via di fuga.
2. Che cosa è davvero in ascolto su questo server?
Le regole per servizi che non esistono sono innocue. Un servizio che ti è sfuggito ti costa invece l'accesso oppure un disservizio. Fatti un quadro della situazione:
ss -lntup
La colonna Local Address:Port distingue con precisione tra 0.0.0.0:22 (solo IPv4), [::]:22 (IPv6 e, tramite il socket dual stack, di solito anche IPv4) e 127.0.0.1:3306 (solo locale, non serve alcuna regola firewall). Tutto ciò che è associato a 127.0.0.1 o a ::1 non va aperto.
Particolarmente importante è la porta SSH effettiva. Non tirare a indovinare, verifica. Il comando ha bisogno dei permessi di lettura sulle chiavi host, quindi va eseguito come root oppure con sudo:
sudo sshd -T | grep -i "^port "
Un dettaglio che molte guide tralasciano: su Ubuntu 24.04 il servizio SSH viene avviato tramite socket activation con ssh.socket, non con ssh.service. Lì systemctl is-enabled ssh.socket risponde enabled e ssh.service risponde disabled. Su Debian 12, Debian 13 e Ubuntu 22.04 vale esattamente il contrario, con il classico servizio sempre attivo.
La conseguenza per questo argomento è sgradevolmente concreta: su Ubuntu 24.04 la porta segnalata da sshd -T non è per forza la porta su cui il sistema è realmente in ascolto. Lì fa fede ListenStream in /lib/systemd/system/ssh.socket oppure in un file di integrazione sotto /etc/systemd/system/ssh.socket.d/. Chi ha cambiato la porta SSH e si affida a sshd -T apre in UFW il numero di porta sbagliato e resta chiuso fuori al prossimo accesso. Su tutti e quattro i sistemi l'unico dato affidabile è il processo che è davvero in ascolto:
sudo ss -lntp | grep sshd
3. L'interruttore di sicurezza a tempo
Nel caso qualcosa vada storto, prima della modifica rischiosa prepara un timer che dopo dieci minuti spegne il firewall da solo:
nohup sh -c 'sleep 600; ufw disable' >/dev/null 2>&1 &
Se tutto ha funzionato, annullalo:
pkill -f 'sleep 600; ufw disable'
Il pkill colpisce solo il processo shell che lo racchiude. Lo sleep prosegue come processo orfano e termina senza conseguenze, perché non resta più nessuno in grado di eseguire poi ufw disable.
L'ordine che non ti chiude fuori
Su Debian UFW di norma non è preinstallato, su Ubuntu Server di solito sì. Installarlo non fa danni in nessun caso:
apt-get update
apt-get install -y ufw
ufw version
Ora l'ordine, esattamente così:
ufw allow 22/tcp comment 'SSH'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
Quattro osservazioni:
- La regola di autorizzazione viene prima di tutto il resto. UFW accetta regole anche quando è inattivo e le salva in
/etc/ufw/user.rules. All'attivazione sono subito operative. - Al posto di
22/tcppuoi usare un profilo applicativo, per esempioufw allow OpenSSH. Non fidartene però alla cieca: i profili non arrivano da UFW, ma dai pacchetti installati. Su Ubuntu la directory/etc/ufw/applications.d/resta vuota finché non installi servizi, eufw app liststampa lì soltanto l'intestazioneAvailable applications:senza una singola voce. Su Debian è già il pacchetto ufw a portare circa 38 profili, e lì il profilo SSH si chiamaSSH. Il profiloOpenSSHproviene su entrambe le distribuzioni dal pacchettoopenssh-server, quindi sui server è la situazione normale. Con una porta SSH diversa da quella standard il profilo non serve comunque a nulla. Quali profili conosce il tuo sistema lo mostraufw app list; la forma basata sulla porta,ufw allow 22/tcp, funziona invece in modo identico su tutti e quattro i sistemi ed è quindi la scelta più affidabile. ufw enablechiede conferma in modo interattivo:Command may disrupt existing ssh connections. Proceed with operation (y|n)?. Negli script e nei ruoli Ansible usaufw --force enable, altrimenti l'esecuzione si blocca.- Il testo dopo
commentcompare inufw status. Senza, fra sei mesi non ricorderai più per che cosa è aperta la porta 8443.
Gli altri servizi si aggiungono dopo, per esempio un server web:
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
E solo a questo punto apri un secondo terminale e accedi di nuovo. La faccenda è chiusa soltanto quando questo accesso riesce.
IPv6: la seconda famiglia di indirizzi che ci si dimentica
Ogni server moderno ha IPv6, quasi sempre senza che nessuno lo abbia configurato di proposito. Chi pensa solo a IPv4 si ritrova alla fine con un firewall che regola esattamente metà del traffico e lascia passare l'altra metà. Verifica prima di tutto se esistono indirizzi IPv6 globali configurati:
ip -6 addr show scope global
La buona notizia: su tutte e quattro le distribuzioni trattate qui in /etc/default/ufw è impostato di serie IPV6=yes. UFW mantiene allora in parallelo una regola IPv6 per ogni regola IPv4. Meglio verificare che fidarsi:
grep '^IPV6' /etc/default/ufw
Una seconda prova, più solida, è la politica predefinita della catena IPv6 stessa:
ip6tables -L INPUT -n
Nella prima riga deve comparire Chain INPUT (policy DROP). Se invece c'è policy ACCEPT e sotto nessuna catena ufw, il tuo server è completamente aperto via IPv6, per quanto buone possano essere le regole IPv4.
Due trappole:
- Una modifica a
IPV6in/etc/default/ufwnon diventa effettiva conufw reload. Servonoufw disableseguito daufw enable. Proprio in quella pausa resti per un attimo senza firewall, quindi non farlo di sfuggita su un sistema esposto. - Chi blocca ICMPv6 in blocco distrugge la propria connettività. Neighbor Discovery e "Packet too big" con IPv6 non sono opzionali, fanno parte del protocollo. UFW permette già i tipi necessari in
/etc/ufw/before6.rules. Metti mano a questo file solo se sai esattamente che cosa stai facendo.
Quando una regola basata sulla porta viene creata correttamente in entrambe le famiglie di indirizzi, UFW stampa due righe durante l'aggiunta: Rule added e Rule added (v6). Se manca la seconda riga, manca metà della protezione. Fanno eccezione le regole limitate a una sorgente: con ufw allow from 203.0.113.10 to any port 22 proto tcp compare solo Rule added, ed è corretto così, perché a un indirizzo sorgente IPv4 non corrisponde alcun equivalente IPv6.
Che cosa scrive davvero UFW: nftables come backend
Qui circolano molte mezze verità. La situazione su Debian 12, Debian 13, Ubuntu 22.04 e Ubuntu 24.04 è uniforme: UFW parla ancora la sintassi iptables, ma il comando iptables su tutti e quattro i sistemi è lo strumento di compatibilità iptables-nft. Le regole finiscono quindi nel sottosistema nftables del kernel. La prova in una riga:
iptables -V
L'output termina con (nf_tables). Se compare (legacy), il tuo sistema lavora con il vecchio backend. Funziona, però porta a due mondi di regole che convivono nel kernel e si coprono a vicenda. Quale variante è impostata lo mostra update-alternatives --display iptables.
Visto dal lato nftables la cosa si presenta così. È importante guardare solo dopo aver acceso il firewall:
apt-get install -y nftables
nft list tables
Finché UFW non è attivo la tabella infatti non esiste proprio: iptables-nft la crea solo quando vengono caricate regole vere, quindi non prima di ufw --force enable. Prima di quel momento nft list tables resta vuoto e un nft list table ip filter si interrompe con Error: No such file or directory. Non è un difetto, è lo stato previsto. Solo quando nell'elenco compaiono table ip filter e table ip6 filter vale la pena guardarci dentro:
nft list table ip filter
L'output inizia con la riga Warning: table ip filter is managed by iptables-nft, do not touch!, ed è da prendere alla lettera: guardare sì, modificare a mano no. Sotto trovi catene come ufw-before-input, ufw-user-input e ufw-after-input. È questo il punto in cui diventa visibile il conflitto più importante: non mescolare UFW e regole nft scritte a mano. Un nft flush ruleset cancella dal kernel tutte le regole UFW senza che UFW se ne accorga. Da quel momento ufw status continua a segnalare Status: active, mentre di fatto non è attiva nemmeno una regola. È una delle fonti di errore più sgradevoli in assoluto, perché lo strumento con cui verifichi ti mente. La via di ritorno:
ufw reload
Dopo ogni intervento su altri strumenti firewall (Docker, Kubernetes, software VPN, iptables-persistent) non controllare quindi lo stato di UFW, ma il set di regole reale del kernel.
ufw limit contro il brute force, e dove si ferma
Per SSH UFW offre una limitazione della frequenza:
ufw limit 22/tcp comment 'SSH rate limit'
La semantica è definita chiaramente nel manuale: le connessioni vengono permesse normalmente, ma scartate non appena un singolo IP sorgente apre sei o più connessioni nuove nell'arco di 30 secondi. I valori sono cablati nel codice e dall'interfaccia di UFW non si possono cambiare. Sotto il cofano lavora il modulo recent, visibile in iptables -S. Per IPv6 UFW crea una regola equivalente, ammesso che il modulo del kernel sia disponibile, cosa che vale su tutte e quattro le distribuzioni.
Se avevi già impostato ufw allow 22/tcp, ora nasce una seconda regola. La vecchia va tolta, altrimenti scatta per prima e la limitazione resta senza effetto:
ufw status numbered
ufw delete allow 22/tcp
Se invece cancelli tramite il numero (ufw delete 3), tieni presenti due particolarità. Primo, ufw status numbered numera le regole IPv4 e IPv6 di seguito in un unico elenco, e dopo ogni cancellazione tutti i numeri successivi si spostano. Cancella quindi sempre una regola sola, fatti ristampare l'elenco subito dopo, oppure procedi dal numero più alto verso il basso. Secondo, in questa operazione UFW chiede conferma in modo interattivo: Proceed with operation (y|n)?.
E ora la valutazione onesta che nella maggior parte delle guide manca:
- Contro gli attacchi distribuiti non serve. Il conteggio avviene per IP sorgente. Una botnet con mille indirizzi fa cinque tentativi per indirizzo e resta sotto la soglia.
- Colpisce le tue stesse automazioni. Uno script di backup con molte chiamate
rsyncseparate, un'esecuzione Ansible con più fork oppure un job CI possono finire nello stesso limite. In quel caso non blocchi l'aggressore, ma la tua pipeline di deployment. Per queste sorgenti è meglio mettere davanti un'eccezione esplicita, per esempioufw allow from 203.0.113.10 to any port 22 proto tcpcon l'indirizzo reale del tuo server di build. - Non sostituisce una configurazione SSH pulita. Il passo più efficace contro chi prova a indovinare le password è disattivare le password:
PasswordAuthentication noin/etc/ssh/sshd_configoppure in un file sotto/etc/ssh/sshd_config.d/. Chi non accetta password non può nemmeno farsele indovinare. In aggiunta ha senso fail2ban, che a differenza diufw limitreagisce alle voci di log e blocca più a lungo.
Come capisci che ha funzionato davvero
"Il comando è andato a buon fine" non è una prova. Quattro verifiche che dicono davvero qualcosa:
Primo, lo stato complessivo.
ufw status verbose
L'output atteso ha questa forma:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp LIMIT IN Anywhere
22/tcp (v6) LIMIT IN Anywhere (v6)
Decisiva è la seconda riga con l'aggiunta (v6). Senza di essa manca la copertura IPv6.
Secondo, il riavvio. Un firewall che non sopravvive a un riavvio non vale nulla.
systemctl is-enabled ufw
La risposta deve essere enabled. E poi riavvia davvero il server una volta e accedi. È l'unico test che risponde veramente alla domanda.
Terzo, lo sguardo dall'esterno. Verifica da un altro host che una porta volutamente non aperta sia davvero chiusa, per esempio con nc -zv TUO-IP 3306. Importante: un test da localhost non dimostra nulla, perché il traffico sull'interfaccia di loopback viene sempre lasciato passare da UFW.
Quarto, i log. Qui c'è una differenza reale tra le distribuzioni. Con l'impostazione predefinita low UFW registra i pacchetti bloccati tramite il log del kernel. Su Ubuntu 22.04 e 24.04 rsyslog è presente e i messaggi finiscono anche in /var/log/ufw.log. Su Debian 12 e in modo ancora più marcato su Debian 13 rsyslog manca nelle installazioni minime, e lì quel file semplicemente non esiste. La strada che funziona ovunque:
journalctl -k -n 50
Cerchi le righe che iniziano con [UFW BLOCK]. Se vuoi vedere di più, alza il livello (ufw logging medium) e poi riportalo su ufw logging low. Su un server con traffico di pubblico il livello più alto riempie il disco più in fretta di quanto si pensi.
I messaggi di errore alla lettera
ERROR: problem running ufw-init: la causa più frequente è un conflitto con un secondo strumento firewall, di solito nftables.service o iptables-persistent, oppure una mescolanza tra backend legacy e nft. Controlla iptables -V e disattiva i servizi concorrenti. UFW porta con sé anche uno script di verifica che passa in rassegna i requisiti del kernel uno per uno e segnala quale modulo manca.
ERROR: Could not find a profile matching 'OpenSSH': manca il profilo applicativo, perché openssh-server non è installato oppure il file sotto /etc/ufw/applications.d/ è stato rimosso. Su Ubuntu questa directory è comunque vuota finché non installi servizi. Usa invece il numero di porta.
ERROR: Bad port: di solito un errore di battitura o un nome di servizio che /etc/services non conosce. I numeri di porta sono sempre univoci.
Skipping adding existing rule oppure Skipping adding existing rule (v6): non è un messaggio di errore, ma l'avviso che la regola c'è già. Se credi di aver modificato una regola e questa resta invariata, il motivo è questo.
ERROR: Invalid position '0': per cancellare e inserire UFW conta a partire da 1, non da 0. I numeri provengono da ufw status numbered e si spostano dopo ogni cancellazione. Cancella quindi sempre dal numero più alto verso il basso.
WARN: Rules updated but not applied: la regola è nella configurazione, ma il firewall è inattivo. Manca un ufw enable.
Differenze tra le distribuzioni in sintesi
- Debian 13 (trixie): UFW da installare a parte. Backend nf_tables.
IPV6=yesdi serie. rsyslog manca nelle installazioni minime, quindi log tramitejournalctl -k. SSH tramitessh.service. - Debian 12 (bookworm): UFW da installare a parte. Backend nf_tables.
IPV6=yesdi serie. rsyslog presente a seconda del tipo di installazione, quindi/var/log/ufw.lognon è garantito. SSH tramitessh.service. - Ubuntu 24.04 LTS: UFW presente nelle immagini server, ma inattivo. Backend nf_tables.
IPV6=yesdi serie./var/log/ufw.logpresente. SSH con socket activation tramitessh.socket, quindi verifica la porta in ascolto sempre consudo ss -lntp | grep sshde non consshd -T. - Ubuntu 22.04 LTS: UFW presente nelle immagini server, ma inattivo. Backend nf_tables.
IPV6=yesdi serie./var/log/ufw.logpresente. SSH tramitessh.service.
Ciò che è uguale su tutti e quattro i sistemi: dopo l'installazione UFW è sempre inattivo. Nessuno ti accende il firewall di nascosto e nessuno lo spegne di nascosto.
Docker scavalca UFW
Se sul server gira Docker vale una limitazione importante: le porte dei container pubblicate ignorano le tue regole UFW. Docker crea catene proprie e lavora con la traduzione dell'indirizzo di destinazione, così i pacchetti passano accanto alle catene UFW in INPUT. Un docker run -p 8080:80 è quindi raggiungibile dall'esterno, anche se ufw status mostra un pulito "deny incoming".
La contromisura più semplice e robusta è non pubblicare affatto su tutti gli indirizzi, ma solo in locale, e far passare l'accesso attraverso un reverse proxy:
docker run -d -p 127.0.0.1:8080:80 nginx
In un file Compose questo corrisponde all'indicazione di porta "127.0.0.1:8080:80". In alternativa esiste la possibilità di agganciare regole proprie di UFW nella catena DOCKER-USER riservata da Docker. È efficace, ma richiede molta manutenzione e va ricontrollata a ogni aggiornamento di Docker. L'associazione a 127.0.0.1 risolve il problema alla radice.
Se ti sei comunque chiuso fuori
È successo, SSH non risponde più. Con ordine:
- Non riavviare. Un riavvio non aiuta, perché UFW è attivato come servizio systemd e all'avvio ripristina le sue regole. Il server torna su chiuso esattamente come si è spento.
- Apri la console VNC nell'area clienti e accedi lì come root.
- Spegni il firewall:
ufw disable. Così sei di nuovo in gioco, ma anche di nuovo senza protezione. - Trova la causa, non indovinarla. Guarda
ufw status numberede la porta SSH effettiva dass -lntup. In nove casi su dieci la causa è una di queste tre: non è mai esistita una regola di autorizzazione per SSH, la regola punta alla porta 22 mentre sshd è in ascolto su un'altra porta, oppure l'apertura era limitata a un indirizzo IP che la tua connessione nel frattempo non ha più (assegnazione dinamica degli indirizzi sulla linea internet). - Correggi e riattiva, questa volta nell'ordine giusto e con l'interruttore di sicurezza a tempo impostato.
Se vuoi azzerare completamente il set di regole esiste ufw reset. Importante saperlo: questo comando disattiva il firewall e deposita copie di sicurezza dei file di regole precedenti sotto /etc/ufw/, con marca temporale nel nome del file. Nel dubbio puoi quindi rileggere che cosa valeva prima. Non eseguirlo mai attraverso una connessione SSH senza avere la console aperta accanto.
Una configurazione di partenza sensata
Per un tipico server web la sequenza completa è questa:
apt-get update
apt-get install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw limit 22/tcp comment 'SSH rate limit'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
ufw --force enable
ufw status verbose
Nota che qui default deny incoming viene sì prima della regola SSH, ma l'attivazione avviene proprio alla fine. Finché UFW è inattivo la politica predefinita non fa alcun danno. Critico è soltanto lo stato nel momento dell'enable, e a quel punto la regola SSH è registrata da un pezzo.
Chi vuole inoltre rendere gli accessi amministrativi raggiungibili solo dalla propria rete lavora con regole limitate alla sorgente secondo lo schema ufw allow from 203.0.113.0/24 to any port 22 proto tcp. Ancora più pulito è non esporre affatto i servizi di amministrazione sulla rete aperta, ma collegarli attraverso una VPN. Un'istanza WireGuard si configura in pochi minuti su ognuno dei quattro sistemi e sostituisce un'intera pila di eccezioni firewall con una sola porta UDP aperta.
E per finire la valutazione di cui conviene essere consapevoli: UFW filtra sul server stesso. Contro gli attacchi volumetrici che saturano la connettività non serve, per principio, perché i pacchetti hanno già percorso la linea quando il tuo kernel li scarta. Per quello serve un filtraggio nella rete a monte. In KernelHost se ne occupa l'infrastruttura a monte nel datacenter maincubes di Francoforte sul Meno con filtraggio Arbor in tempo reale. Il tuo firewall locale e la protezione di rete risolvono due problemi diversi, e ti servono entrambi.
Domande frequenti
"ufw enable" mi chiude subito fuori dalla sessione SSH in corso?
Devo attivare IPv6 in UFW separatamente?
UFW usa iptables o nftables?
Che cosa fa esattamente ufw limit?
Non riesco più ad accedere al server via SSH. Serve un riavvio?
Perché i miei container Docker sono raggiungibili dall'esterno nonostante il firewall UFW sia attivo?
Perché sul mio server Debian /var/log/ufw.log non esiste?
2026 KernelHost GmbH. Tutti i diritti riservati. Questa guida è protetta dal diritto d'autore. La ripubblicazione su altri siti web, anche parziale o in forma modificata, non è consentita senza il nostro consenso scritto. Le citazioni con indicazione della fonte e un link sono le benvenute.

