iptables e nftables su Ubuntu: quale usa davvero?
Scopri perche iptables su Ubuntu scrive regole nftables, verifica il backend attivo e leggi il ruleset nativo. Capisci anche i conflitti tra ufw e Docker.
iptables vs nftables su Ubuntu: quale dei due è in esecuzione sul server?
Su Ubuntu 20.04 e versioni successive, il comando iptables è un front-end che scrive regole nftables. Nel kernel viene eseguito un solo packet filter, nftables, mentre due comandi in user space lo configurano. Una riga iptables -A INPUT continua a funzionare esattamente come prima e la regola che crea è una regola nftables che nft può visualizzare.
Verificatelo sul vostro server prima di darlo per certo.
iptables -V
sudo update-alternatives --display iptables
sudo nft list rulesetSu Ubuntu 24.04 (iptables 1.8.10, ad agosto 2026), iptables -V visualizza iptables v1.8.10 (nf_tables). Il nome tra parentesi quadre indica il back-end. (nf_tables) significa che il comando comunica con nftables. (legacy) indica il vecchio back-end x_tables, che Ubuntu distribuisce ancora come iptables-legacy e che il kernel mantiene come ruleset completamente separato. update-alternatives visualizza il symlink alla base di questa scelta: link currently points to /usr/sbin/iptables-nft.
Su un VPS appena installato e senza firewall configurato, sudo nft list ruleset non visualizza nulla. Questo output vuoto costituisce la baseline. Aggiungete una regola nel modo tradizionale e controllate di nuovo.
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo nft list ruleset# Warning: table ip filter is managed by iptables-nft, do not touch!
table ip filter {
chain INPUT {
type filter hook input priority filter; policy accept;
tcp dport 22 counter packets 0 bytes 0 accept
}
chain FORWARD {
type filter hook forward priority filter; policy accept;
}
chain OUTPUT {
type filter hook output priority filter; policy accept;
}
}La vostra regola iptables è una regola nftables. iptables-nft identifica le tabelle che crea e nft visualizza quell'avviso quando rileva l'identificatore, perché modificare una tabella di questo tipo con nft assegna a due strumenti la gestione delle stesse regole. Osservate ciò che ha prodotto un solo comando: una tabella che non avete nominato e catene che non avete richiesto. Questo è il modello precedente ed è il primo elemento a cambiare quando scrivete direttamente le regole nftables.
Cosa ti nasconde iptables -L
iptables -L mostra soltanto la tabella filter. Le regole NAT (network address translation) richiedono iptables -t nat -L, mentre le regole mangle richiedono -t mangle. IPv6 usa un comando separato, ip6tables, con una copia distinta di ogni regola. Un sistema può quindi apparire pulito in un elenco, mentre una regola presente in una tabella che non hai controllato scarta o modifica i pacchetti.
sudo nft list ruleset stampa in un unico output ogni famiglia, ogni tabella, ogni catena e ogni regola. Su un server che non hai configurato personalmente, questo comando è il modo più rapido per verificare cosa è realmente caricato. Aggiungi -a per stampare gli handle delle regole, necessari per eliminare una singola regola invece dell’intera catena.
Conviene correggere anche due abitudini. iptables -L risolve gli indirizzi e le porte nei rispettivi nomi; su un sistema con un resolver non funzionante può quindi sembrare bloccato: usa iptables -nvL. Verifica inoltre che il backend legacy sia vuoto con sudo iptables-legacy -nvL, perché se esistono regole in entrambi i backend il kernel le valuta entrambe le configurazioni e nessun elenco mostra il quadro completo.
Tabelle e chain create da te, non ereditate
nftables parte da una configurazione vuota. Non esiste alcuna tabella filter finché non la crei, e la parola filter è soltanto un nome scelto da te. Una chain riceve pacchetti solo quando le assegni un tipo, un hook e una priorità, diventando così una base chain. Una chain priva di questi elementi viene raggiunta soltanto tramite un jump o un goto esplicito, quindi non comporta alcun costo finché qualcosa non vi esegue un salto.
L'altro cambiamento rilevante è la famiglia inet. Una tabella inet gestisce IPv4 e IPv6 con le stesse regole, eliminando un'intera categoria di bug in cui una porta è chiusa in iptables e completamente aperta in ip6tables. Questa discrepanza è abbastanza comune da avere una propria modalità di errore sui server con ufw.
Ecco un ruleset completo per il server. Va inserito in /etc/nftables.conf.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set admin_ips {
type ipv4_addr
flags interval
elements = { 203.0.113.5, 198.51.100.0/24 }
}
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
ip protocol icmp accept
meta l4proto ipv6-icmp accept
tcp dport 22 ip saddr @admin_ips counter accept
tcp dport { 80, 443 } counter accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}Leggi due volte la riga 2. flush ruleset elimina tutte le tabelle presenti sul server, comprese quelle create da ufw e Docker per uso proprio. Continua a leggere prima di eseguire questo comando su un server in produzione.
La prima regola della chain input svolge la maggior parte del lavoro. ct state established,related accept consente il ritorno delle risposte alle connessioni avviate da te, quindi il resto della chain deve decidere soltanto sulle nuove connessioni. ct state invalid drop scarta i pacchetti che non corrispondono ad alcuna connessione nota e non rappresentano un avvio valido. Tutto ciò che segue è un'apertura esplicita, mentre policy drop gestisce il resto.
Controlla il file prima di caricarlo e mantieni aperta una seconda sessione SSH durante l'operazione. policy drop, insieme a un solo errore di battitura nella regola SSH, può impedirti di accedere al tuo server.
sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo nft list rulesetnft -c -f analizza il file e segnala gli errori senza caricare nulla. Se l'analisi è corretta, non viene visualizzato alcun output.
Gli insiemi sostituiscono lunghi elenchi di regole
tcp dport { 80, 443 } è un insieme anonimo: una regola e una ricerca, invece di una regola per ogni porta. Un insieme denominato come admin_ips è ancora più flessibile, perché puoi modificarlo mentre il firewall è in esecuzione.
sudo nft add element inet filter admin_ips { 203.0.113.9 }
sudo nft list set inet filter admin_ips
sudo nft delete element inet filter admin_ips { 203.0.113.9 }Non servono ricaricamenti né la riassegnazione dei numeri delle regole. La corrispondenza resta una singola ricerca, sia che l'insieme contenga cinque indirizzi sia che ne contenga cinquantamila. flags interval consente a un insieme di contenere intervalli e prefissi CIDR (classless inter-domain routing) come 198.51.100.0/24. Senza quel flag, l'insieme accetta soltanto indirizzi singoli e il caricamento del prefisso non riesce.
Gli insiemi possono anche far scadere automaticamente i propri elementi.
set banned {
type ipv4_addr
flags timeout
timeout 1h
}Con una regola ip saddr @banned drop, ogni elemento viene rimosso automaticamente un'ora dopo l'aggiunta. È così che l'azione nftables in fail2ban su Ubuntu 24.04 blocca un indirizzo: aggiunge un elemento a un insieme, non una regola. Se le porte sono ancora un argomento nuovo, inizia da che cos'è realmente una porta in Linux.
Una differenza diventa evidente durante una migrazione. nftables non conta i pacchetti se non glielo chiedi. iptables -nvL mostra sempre i contatori per ogni regola. In nftables, invece, hanno numeri soltanto le regole che contengono la parola chiave counter; inserisci quindi counter in ogni regola che prevedi di dover analizzare in seguito.
Come hook e priorità determinano l’ordine
Una base chain indica un hook, cioè il punto del percorso dei pacchetti in cui viene eseguita. prerouting viene eseguito prima della decisione di instradamento. input viene eseguito per i pacchetti indirizzati a questa macchina. forward viene eseguito per i pacchetti instradati attraverso di essa. output viene eseguito per i pacchetti generati dai processi locali. postrouting viene eseguito per ultimo, subito prima che il pacchetto lasci la macchina.
La priorità ordina le chain all’interno di uno stesso hook, iniziando dal numero più basso. nftables assegna un nome ai valori tradizionali: raw corrisponde a -300, mangle a -150, dstnat a -100, filter a 0 e srcnat a 100. Scrivere priority filter; equivale a scrivere priority 0;.
Ora vediamo il comportamento che determina se è possibile combinare gli strumenti. Ogni base chain registrata su un hook viene eseguita nell’ordine stabilito dalla priorità. Un pacchetto accettato nella propria chain non ha terminato l’elaborazione: accept termina solo quella chain e il pacchetto prosegue verso la base chain successiva dello stesso hook. drop è definitivo in ogni contesto e interrompe immediatamente l’elaborazione del pacchetto. Di conseguenza, una regola permissiva nella propria tabella non può annullare un drop nella tabella di ufw, indipendentemente da quale delle due venga eseguita per prima; inoltre, il proprio accept non protegge da una chain eseguita in seguito.
Due base chain sullo stesso hook con la stessa priorità vengono eseguite nell’ordine di registrazione, che dipende dal servizio avviato per primo. Questo ordine può cambiare dopo un riavvio. Se è necessario eseguire la propria tabella insieme a ufw, assegnarle una priorità distinta, così l’ordine è definito esplicitamente invece di dipendere da una contesa.
Perché non esiste alcuna regola di reverse NAT da scrivere?
Questa è la domanda su cui si commette più spesso un errore, quindi ecco la risposta diretta. Il connection tracking scrive automaticamente la traduzione inversa. Non è necessario aggiungere una seconda regola.
Una tabella nat che svolge entrambe le funzioni tipiche di un VPS è simile a questa.
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "enp1s0" tcp dport 8080 dnat ip to 10.0.0.5:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.0.0.0/24 oifname "enp1s0" masquerade
}
}Solo il primo pacchetto di una connessione viene valutato rispetto a una catena nat. Quando una regola corrisponde, il kernel memorizza la traduzione nella tabella di connection tracking, insieme all'entry della connessione. Ogni pacchetto successivo, in entrambe le direzioni, viene riscritto usando l'entry memorizzata; nessuna regola viene letta di nuovo. Installa lo strumento conntrack e visualizza un'entry in tempo reale.
sudo apt install -y conntrack
sudo conntrack -L -p tcptcp 6 431999 ESTABLISHED src=198.51.100.20 dst=203.0.113.10 sport=54321 dport=8080 src=10.0.0.5 dst=198.51.100.20 sport=80 dport=54321 [ASSURED] mark=0 use=1Interpreta l'output come due tuple. I primi quattro campi rappresentano la connessione così come è stata inviata dal client, indirizzata a 203.0.113.10:8080, il tuo indirizzo pubblico. I quattro campi successivi rappresentano la risposta prevista dal kernel, già invertita e già tradotta, proveniente da 10.0.0.5:80, il backend reale. Questa seconda tupla è la regola inversa. Il kernel l'ha scritta quando il primo pacchetto ha corrisposto alla regola.
Non scrivere quindi una regola per la direzione di ritorno. Non può corrispondere, perché i pacchetti di ritorno appartengono a una connessione già stabilita e non raggiungono mai una catena nat; se vi arrivassero in qualche modo, tradurresti un pacchetto che il kernel ha già modificato.
La posizione della riscrittura dipende dallo stesso meccanismo. La traduzione della destinazione deve essere eseguita in prerouting, prima della decisione di routing, perché il routing deve vedere la nuova destinazione; altrimenti il pacchetto viene inviato nel posto sbagliato. Il traffico generato dal sistema stesso viene gestito nell'hook output per lo stesso motivo. La traduzione della sorgente, inclusa la riscrittura della porta sorgente, deve essere eseguita in postrouting, dopo che il routing ha scelto l'interfaccia in uscita. masquerade ricava il proprio indirizzo da quell'interfaccia, che non è nota finché il routing non è stato eseguito.
Per questo una regola come questa appartiene alla fine del percorso e in nessun altro punto.
ip saddr 10.0.0.0/24 oifname "enp1s0" snat ip to 203.0.113.10:20000-30000L'intervallo di porte riscrive la porta sorgente insieme all'indirizzo sorgente. È ciò che serve quando molti client interni condividono un unico indirizzo pubblico e le rispettive porte sorgente entrano in conflitto. Una risposta arriva indirizzata a una porta compresa nell'intervallo; conntrack la associa all'entry e ripristina la porta sorgente originale prima di consegnare il pacchetto. Anche in questo caso non serve una seconda regola.
Una conseguenza pratica è che la modifica di una regola NAT non sposta le connessioni già esistenti, perché la loro traduzione è già memorizzata. Queste connessioni mantengono il comportamento precedente finché le relative entry non scadono. sudo conntrack -D -p tcp --dport 8080 elimina le entry corrispondenti e sudo conntrack -F le elimina tutte. Usa il secondo comando con cautela su un dispositivo NAT, perché sono proprio queste traduzioni memorizzate a mantenere attive le connessioni correnti; svuotarle interrompe simultaneamente tutte le connessioni che attraversano il dispositivo.
ufw e Docker scrivono ciascuno le proprie regole
ufw è un front end per iptables, che su Ubuntu è a sua volta un front end per nftables. Un host con ufw contiene quindi una tabella ip filter con catene denominate ufw-before-input, ufw-user-input e così via, oltre a una copia ip6 filter della stessa struttura. Verificala con sudo nft list ruleset | grep ufw. Queste catene vengono generate dai file in /etc/ufw e ufw reload le riscrive da zero. Per questo una regola iptables aggiunta manualmente scompare al successivo reload. Le nozioni di base di ufw per un VPS descrivono la struttura di questi file.
Docker programma direttamente il firewall e non consulta ufw. La pubblicazione di una porta con -p 80:80 scrive una regola DNAT nella tabella nat e una regola accept nel percorso di forwarding. Entrambe vengono elaborate prima delle catene utente di ufw. Il risultato sorprende almeno una volta: viene caricato ufw deny 80, ma il container è ancora raggiungibile da Internet. La soluzione si trova nella catena DOCKER-USER, che Docker lascia a disposizione per le regole personalizzate; perché i container Docker ignorano ufw spiega il meccanismo. Verifica le regole presenti sull'host con sudo nft list ruleset | grep -i docker.
Ora rileggi la riga flush ruleset della configurazione precedente. Elimina tutte le tabelle, comprese quelle gestite dai due strumenti. Su un host Docker, le porte pubblicate smettono di funzionare finché sudo systemctl restart docker non ricrea le catene. Questa riga è il modo più comune con cui si rendono non disponibili i propri servizi mentre si riordina la configurazione del firewall.
Regole che restano attive dopo un riavvio
Nessuno dei due ruleset è persistente da solo. Il kernel dimentica tutto durante lo spegnimento e ogni soluzione richiede un pacchetto separato.
Per nftables, /etc/nftables.conf viene letto da nftables.service. Ubuntu installa questo servizio disabilitato. Verificarne lo stato prima di considerarlo affidabile.
systemctl is-enabled nftables
sudo nft -c -f /etc/nftables.conf
sudo systemctl enable --now nftablesPer iptables, il pacchetto è iptables-persistent. Installa netfilter-persistent e salva le regole in /etc/iptables/rules.v4 e /etc/iptables/rules.v6.
sudo apt install -y iptables-persistent
sudo netfilter-persistent saveNon eseguire entrambe le soluzioni. Due file che dichiarano entrambi di contenere il firewall possono divergere. La soluzione caricata per ultima prevale, ma non è possibile prevedere quale sarà leggendo uno solo dei due file.
Esiste un problema analogo quando si esporta un ruleset attivo. sudo nft -s list ruleset > /etc/nftables.conf acquisisce tutto ciò che è caricato in quel momento, comprese le tabelle di ufw e Docker. Se lo ripristini all'avvio, ottieni una copia fissa delle regole che questi strumenti prevedono di creare autonomamente, quindi una seconda copia quando vengono avviati. Esporta soltanto la tua tabella con sudo nft -s list table inet filter. Il flag -s esclude i contatori, che non devono essere presenti in un file di configurazione.
Passare a nftables sul VPS?
Lascia ufw invariato, a meno che non ti serva una funzione che non è in grado di esprimere. ufw copre il caso d’uso ordinario di un VPS: criterio predefinito di deny con poche porte aperte. Sostituirlo con un ruleset scritto a mano senza una necessità concreta ti lascia con lo stesso firewall e un ulteriore componente da mantenere.
Passa alla gestione nativa quando ti servono funzioni al di fuori del modello di ufw: NAT e port forwarding, set aggiornabili a runtime, una regola valida per entrambe le famiglie di indirizzi oppure priorità delle chain definite direttamente da te. Sono motivi concreti, che ufw non può gestire.
Se passi alla gestione nativa, fallo completamente. Esegui sudo ufw disable e sudo systemctl disable --now ufw, verifica con sudo nft list ruleset che le relative table siano state rimosse, quindi carica il tuo file. Un sistema che esegue ufw insieme a una table scritta a mano continua a inoltrare il traffico, ma la policy attiva diventa l’unione di due ruleset valutati secondo un ordine determinato dall’avvio dei servizi. Chi legge uno dei due file non può quindi capire quale sia il comportamento effettivo del sistema.
Migrazione di un ruleset iptables esistente
iptables-translate converte una regola e stampa la sintassi nftables corrispondente. Non modifica nulla sul sistema.
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPTnft add rule ip filter INPUT tcp dport 22 counter acceptiptables-restore-translate -f /etc/iptables/rules.v4 esegue la stessa operazione su un ruleset salvato completo. Considera l'output solo come una prima bozza. La conversione è meccanica e procede regola per regola, quindi mantiene i vecchi nomi di tabelle e catene, restituisce due ruleset distinti per IPv4 e IPv6 e non include nessuno dei set che giustificano la migrazione. Riscrivilo manualmente come un'unica tabella inet, quindi verificalo con nft -c -f prima di applicarlo a un server in produzione.
Gli indirizzi di questi esempi appartengono agli intervalli riservati alla documentazione 203.0.113.0/24 e 198.51.100.0/24, mentre enp1s0 è un nome di interfaccia. Recupera i tuoi valori con ip route show default e ip -br addr invece di copiare quelli dell'esempio, perché nelle immagini Ubuntu attuali l'interfaccia raramente si chiama eth0.
FAQ
iptables è deprecato su Ubuntu?
Il comando non è stato rimosso e continua a funzionare su Ubuntu 24.04. È cambiato ciò che avviene internamente: iptables è un front end che scrive le regole nftables tramite il back end iptables-nft. Verifica quale usi con iptables -V, che su 24.04 restituisce iptables v1.8.10 (nf_tables). Il vecchio back end x_tables è ancora distribuito come iptables-legacy e gestisce un ruleset completamente separato. Inserisci quindi le regole in un solo back end, non in entrambi.
Devo aggiungere una seconda regola per annullare il NAT al ritorno?
No. Il connection tracking memorizza la traduzione quando il primo pacchetto di una connessione corrisponde a una regola nat. Ogni pacchetto successivo in entrambe le direzioni viene riscritto in base a quella voce memorizzata. sudo conntrack -L la mostra come due tuple per connessione: prima la direzione originale, poi la risposta già invertita. Una regola scritta per la direzione di ritorno non è utile, perché i pacchetti di ritorno non raggiungono mai una catena nat.
Posso eseguire ufw e le mie regole nftables contemporaneamente?
È possibile, ma crea un problema operativo. Viene eseguita ogni base chain associata a un hook. La policy effettiva è quindi la combinazione dei due ruleset, ordinati in base alla priorità e, a parità di priorità, in base al servizio avviato per primo. Un drop in uno dei due è definitivo, mentre un accept nel tuo ruleset non impedisce all'altro di eliminare lo stesso pacchetto. Scegli un solo strumento. Se scegli nftables, disabilita prima ufw e verifica con sudo nft list ruleset che le sue tabelle siano state rimosse.
Come posso fare in modo che le regole nftables restino attive dopo un riavvio su Ubuntu?
Inserisci il ruleset in /etc/nftables.conf, verificalo con sudo nft -c -f /etc/nftables.conf, quindi esegui sudo systemctl enable --now nftables. Il servizio non è abilitato per impostazione predefinita, quindi conviene eseguire una volta systemctl is-enabled nftables. Quando generi quel file, esporta soltanto la tua table con sudo nft -s list table inet filter, perché un dump completo list ruleset include anche le tabelle gestite autonomamente da ufw e Docker.