SSD Nodes Learn
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-07-24

UFW e IPv6: come proteggere il VPS

Scopri perché le regole UFW potrebbero non proteggere il tuo VPS su IPv6. Impara a configurare correttamente il firewall per evitare esposizioni non volute.

La trappola del firewall IPv6 in una frase

Il firewall protegge l'IPv4. Il VPS ha quasi certamente anche un indirizzo IPv6 pubblico e molti servizi sono in ascolto su di esso per impostazione predefinita. Se il firewall copre solo l'IPv4, o se utilizzi un firewall cloud che filtra solo l'IPv4, ogni servizio risulta raggiungibile da Internet tramite IPv6, mentre la parte IPv4 appare protetta. Testi una porta con curl, ricevi una connessione rifiutata e ti senti al sicuro. Un attaccante si connette alla stessa porta tramite IPv6 ed entra nel sistema.

Questa guida spiega l'origine di questa vulnerabilità su un normale VPS Ubuntu 24.04, come identificare esattamente cosa stai esponendo e come risolvere il problema. UFW non è il responsabile. Su un'installazione Ubuntu moderna, UFW gestisce già l'IPv6. L'esposizione deriva dai livelli esterni a UFW e da servizi che non sapevi fossero in ascolto.

Perché il tuo VPS utilizza IPv6

Quasi ogni VPS oggi include un indirizzo IPv6 pubblico, spesso un intero /64, insieme al proprio indirizzo IPv4. Verifica il tuo:

ip -6 addr show scope global
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet6 2001:db8:2a::1/64 scope global

Quell'2001:db8:2a::1 è raggiungibile da qualsiasi punto della rete internet, esattamente come il tuo indirizzo IPv4. Ora verifica quali servizi sono in ascolto:

sudo ss -tlnp
State   Recv-Q  Local Address:Port   Process
LISTEN  0       0.0.0.0:22           sshd
LISTEN  0       [::]:22              sshd
LISTEN  0       127.0.0.1:5432       postgres
LISTEN  0       [::]:8080            docker-proxy

Esamina attentamente la colonna Local Address. 0.0.0.0:22 significa "in ascolto su ogni indirizzo IPv4". [::]:22 significa "in ascolto su ogni indirizzo IPv6". 127.0.0.1:5432 è vincolato al loopback e non è pubblico, quindi la riga relativa a Postgres è sicura. Le due righe [::] rispondono a tutto internet via IPv6, mentre la riga docker-proxy è di quelle che si dimentica di aver avviato.

La maggior parte dei daemon è configurata per vincolarsi a :: di default, poiché su Linux un socket :: accetta solitamente anche IPv4. Di conseguenza, l'impostazione predefinita di un server nuovo è "rispondi su entrambi gli stack, ovunque". Il firewall è l'unico elemento di protezione; per questo un firewall che vede solo uno stack rappresenta un problema critico.

Da dove deriva effettivamente il gap IPv6

Esistono quattro fonti comuni. Su un determinato server è possibile riscontrare una o più di queste cause contemporaneamente.

1. Un firewall cloud che filtra solo IPv4. Molti firewall dei provider e prodotti di security-group sono nati per IPv4; pertanto, ignorano IPv6 o richiedono regole IPv6 separate da aggiungere manualmente. Se l'unico firewall in uso è quello nella dashboard del provider e non copre IPv6, i servizi [::] sono aperti, indipendentemente da ciò che viene indicato per la porta 22 su IPv4. Leggere la documentazione del firewall del provider e cercare specificamente il termine IPv6.

2. iptables configurato manualmente senza ip6tables. Il comando iptables modifica solo le tabelle IPv4. IPv6 utilizza un comando completamente separato, ip6tables, con le proprie regole distinte. Se è stato scritto uno script per il firewall contenente solo righe iptables -A INPUT ... senza le corrispondenti regole ip6tables, il firewall IPv6 è vuoto. Una catena INPUT vuota con policy di default ACCEPT permette tutto il traffico:

sudo ip6tables -L INPUT -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Quel risultato rappresenta l'intero problema in una sola schermata. L'IPv4 è filtrato, l'IPv6 accetta tutto il traffico esterno.

3. Docker che pubblica le porte bypassando il firewall. Quando viene eseguito docker run -p 8080:80, Docker inserisce le proprie regole prima di quelle di UFW. Di conseguenza, una porta pubblicata è raggiungibile anche se ufw status indica che la porta è negata; sui sistemi Docker moderni, la stessa condizione si applica anche a IPv6. Perché Docker bypassa UFW e come filtrare correttamente le porte dei container spiega il meccanismo e le soluzioni. Consultare le basi di Docker Compose su un VPS per capire come vengono dichiarate queste porte pubblicate.

4. UFW con IPv6 disattivato. UFW gestisce IPv6, ma solo se viene istruito a farlo. Verificare l'impostazione:

grep IPV6 /etc/default/ufw

Le versioni moderne di Ubuntu includono IPV6=yes, quindi UFW applica ogni regola a entrambi gli stack. Se si visualizza IPV6=no, derivante da una vecchia immagine o da una vecchia guida, ogni regola UFW scritta è valida solo per IPv4 e l'IPv6 rimane non gestito.

Verifica esattamente cosa stai esponendo

Non fare ipotesi. Misura l'esposizione dall'esterno. Per prima cosa, elenca i listener e annota tutti quelli associati a :::

sudo ss -tlnp | grep '::'

Successivamente, da una macchina diversa, connettiti all'indirizzo IPv6 pubblico del server e prova una porta che ritieni sia chiusa:

curl -6 -v http://[2001:db8:2a::1]:8080/

Se ricevi una pagina o un banner, la porta è aperta su IPv6. Una porta chiusa restituisce Connection refused o un timeout. Per un'analisi completa, esegui una scansione dell'indirizzo IPv6 con nmap da un host esterno al server:

nmap -6 2001:db8:2a::1

Ogni porta che nmap segnala come aperta su IPv6 è una porta raggiungibile dall'intera rete internet, indipendentemente dai risultati della scansione IPv4. Confrontare le scansioni IPv4 e IPv6 affiancate è il metodo più rapido per individuare le discrepanze: qualsiasi porta aperta su -6 ma chiusa su IPv4 indica un servizio non protetto dal firewall.

Chiudere le falle

Configura UFW per entrambi gli stack e imposta il default su deny. Conferma la modifica, imposta una policy di default-deny per il traffico in entrata e autorizza solo il necessario:

sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Se UFW era già attivo quando hai modificato IPV6=yes, la modifica non sarà efficace finché non eseguirai sudo ufw reload.

ufw status elenca ogni regola due volte: una versione semplice e una con suffisso (v6). Quando vedi le righe (v6), UFW sta filtrando IPv6:

22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Se gestisci iptables manualmente, duplica ogni regola in ip6tables, oppure passa a nftables, le cui tabelle inet coprono IPv4 e IPv6 in un unico punto, eliminando questa categoria di errori. Una singola tabella di filtro nftables inet è la soluzione più pulita quando si scrivono le regole manualmente.

Associa i servizi non pubblici all'interfaccia loopback. Un database, un pannello di amministrazione o un endpoint per le metriche raramente necessitano di un indirizzo pubblico. Associali a 127.0.0.1 e ::1 per evitare che ascoltino su un indirizzo raggiungibile via rete. Per Postgres, imposta listen_addresses = 'localhost'. Per un server applicativo, vincolalo a 127.0.0.1 e utilizza un reverse proxy. Chiudere il listener è più efficace che usare il firewall, poiché non ci sarà alcun servizio raggiungibile.

Non affidarti a UFW per proteggere le porte pubblicate da Docker. Pubblica le porte dei container su un indirizzo specifico invece che su tutte le interfacce, ad esempio -p 127.0.0.1:8080:80, in modo che la porta sia raggiungibile solo dall'host e dai servizi proxyati intenzionalmente. Quando un container deve essere necessariamente pubblico, posizionalo dietro un reverse proxy Traefik e pubblica solo il proxy, non ogni singola applicazione.

Aggiungi regole IPv6 al firewall del tuo provider, oppure accetta che non sia il tuo firewall per IPv6 e lascia che il compito sia svolto da UFW o nftables sull'host.

Verifica che la chiusura sia effettiva

Esegui nuovamente lo stesso test esterno dopo le modifiche:

curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1

La porta che rispondeva in precedenza deve ora rifiutare la connessione o andare in timeout; nmap deve riportare lo stato filtered o closed. Se una porta risulta ancora aperta, verifica i quattro punti sopra: un servizio ancora associato a :: senza una regola applicata, una regola Docker posizionata prima di UFW, o un firewall del provider che non supporta IPv6.

Mantenere i servizi sensibili completamente isolati da internet è una misura ancora più sicura. Configura SSH e i pannelli di amministrazione tramite WireGuard VPN e applica il firewall alle relative porte per farle rispondere solo tramite il tunnel; in questo modo il problema dell'esposizione IPv6 non si applica più a tali servizi. Per contrastare i tentativi di brute-force sulle porte rimaste pubbliche, installa Fail2ban davanti a SSH sopra un firewall con policy default-deny.

Se non hai familiarità con le porte, leggi prima cos'è una porta e come i servizi restano in ascolto.

FAQ

UFW blocca IPv6 di default?

Su una moderna installazione Ubuntu 24.04, sì. UFW legge IPV6=yes da /etc/default/ufw e applica ogni regola sia a IPv4 che a IPv6; ufw status mostra le regole IPv6 con un suffisso (v6). Il problema si verifica quando si usa IPV6=no (da una vecchia immagine o un vecchio tutorial), quando si utilizza un firewall del provider che filtra solo IPv4, o quando Docker pubblica una porta bypassando UFW. Controlla l'impostazione con grep IPV6 /etc/default/ufw.

Come posso controllare cosa espone il mio VPS su IPv6?

Esegui sudo ss -tlnp e annota ogni listener il cui indirizzo locale inizia con [::], il che significa che risponde su ogni interfaccia IPv6. Successivamente, da una macchina diversa, testa l'indirizzo IPv6 pubblico del server direttamente con curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/, oppure esegui una scansione con nmap -6 YOUR:IPV6::ADDR. Ogni porta aperta nella scansione IPv6 ma chiusa su IPv4 rappresenta una vulnerabilità.

Perché riesco a raggiungere la porta del mio container Docker anche se UFW indica che è bloccata?

Docker inserisce le proprie regole firewall prima di quelle di UFW quando si pubblica una porta con -p; la porta pubblicata è quindi raggiungibile anche se ufw status la elenca come negata. Questo accade su IPv4 e anche su IPv6 quando il supporto IPv6 di Docker è attivo. Pubblica su un indirizzo specifico come -p 127.0.0.1:8080:80, oppure posiziona il container dietro un reverse proxy e pubblica solo il proxy.

Ho ancora bisogno di un firewall IPv6 se il mio firewall IPv4 è configurato correttamente?

Sì. IPv4 e IPv6 sono stack di rete separati con regole firewall distinte. Un set perfetto di regole IPv4 non ha effetto sul traffico IPv6. Se il VPS ha un indirizzo IPv6 pubblico, come quasi tutti, ogni servizio in ascolto su :: rimane raggiungibile via IPv6 finché una regola del firewall IPv6 o un binding su loopback non lo blocca.

Come posso fare in modo che un servizio risponda solo su IPv4 o solo su localhost?

Imposta l'indirizzo di bind del servizio nella sua configurazione specifica. Usa 127.0.0.1 per il solo loopback IPv4, o 0.0.0.0 per tutti gli indirizzi IPv4 senza listener IPv6. Postgres usa listen_addresses, SSH usa ListenAddress e la maggior parte dei server applicativi espone un flag host o bind. Conferma il risultato con sudo ss -tlnp e verifica che Local Address non mostri più [::].