+39 3662317539/+34 620899163
info@simonecosci.com

ufw su un VPS: chiudere tutto tranne il necessario senza tagliarsi fuori

Sviluppo Siti Web a Tenerife

ufw su un VPS: chiudere tutto tranne il necessario senza tagliarsi fuori

Un VPS appena consegnato ha tutto aperto. Non è un problema teorico: nel giro di qualche ora
qualcuno sta già provando password su SSH, e qualunque servizio che hai avviato per “provare” è
raggiungibile da Internet. Il firewall è la prima cosa da sistemare, e ufw è lo strumento più
rapido per farlo su Debian e Ubuntu.

Il motivo per cui molti non lo attivano è sempre lo stesso: la paura di chiudere fuori se stessi.
È una paura fondata, ma il comportamento di ufw è documentato con precisione, e una volta che sai
in che ordine muoverti il rischio sparisce.

Cosa fa ufw, e cosa non fa

La man page è onesta sin dalla prima riga: ufw è un “program for managing a netfilter firewall”.
Non è un firewall: è l’interfaccia comoda davanti a netfilter, lo stesso su cui lavora iptables.
E mette subito in chiaro i suoi limiti:

ufw is not intended to provide complete firewall functionality via its command interface, but
instead provides an easy way to add or remove simple rules.

Tradotto: per le regole semplici è perfetto, per scenari complessi è il posto sbagliato in cui
infilarle.

Appena installato, il pacchetto si trova in questo stato — e vale la pena leggerlo con attenzione,
perché è già quasi quello che vuoi:

disabled with a default incoming policy of deny, a default forward policy of deny, and a default
outgoing policy of allow

Ingresso negato, uscita permessa, firewall spento. Non serve costruire le policy da zero: serve
aprire il necessario e accendere.

L’ordine giusto: la regola prima di enable

Qui sta l’errore che costa l’accesso al server. Attivare ufw non è un’operazione neutra sulle
connessioni già aperte, e la man page ha una sezione dedicata — REMOTE MANAGEMENT — che lo dice
chiaramente:

When running ufw enable or starting ufw via its initscript, ufw will flush its chains.
This is required so ufw can maintain a consistent state, but it may drop existing connections
(eg ssh).

La soluzione è nella stessa sezione: le regole si possono aggiungere prima di accendere il
firewall. L’esempio è letteralmente quello della documentazione:

ufw allow proto tcp from any to any port 22
ufw enable

The rules will still be flushed, but the ssh port will be open after enabling the firewall.

Le catene vengono svuotate comunque, ma al termine la porta SSH è aperta. Se sei collegato via SSH,
ufw te lo chiede prima di procedere: “By default, ufw will prompt when enabling the firewall
while running under ssh”
. Quella domanda si salta con ufw --force enable — utile in uno script,
pessima idea a mano.

Il dettaglio che quasi nessuno conosce

Una volta che il firewall è attivo, aggiungere e togliere regole è sicuro. Modificarne una o toccare
le policy, no. È una singola frase, ed è la più importante di tutta la pagina:

Please note that once ufw is ‘enabled’, ufw will not flush the chains when adding or removing
rules (but will when modifying a rule or changing the default policy).

Quindi ufw allow 443/tcp su un server in produzione non interrompe nulla. ufw default deny
incoming
eseguito via SSH, invece, svuota le catene: è quello il comando da cui ti aspetti dei
problemi, non l’apertura di una porta. Se devi cambiare una policy, fallo dopo aver verificato
che la regola per SSH esista già.

L’ordine delle regole conta

ufw valuta le regole dall’alto verso il basso e si ferma alla prima che corrisponde:

Rule ordering is important and the first match wins. Therefore when adding rules, add the more
specific rules first with more general rules later.

Conseguenza pratica: se hai già un allow generico sulla porta 80, aggiungere dopo un deny da un
indirizzo specifico non serve a niente, perché la prima regola ha già deciso. Per questo esistono
ufw status numbered (vedi la numerazione reale), ufw insert NUM (inserisce come regola numero
NUM) e ufw delete NUM. Il mio ordine di lavoro è sempre lo stesso: ufw status numbered prima di
ogni modifica, e ufw --dry-run quando non sono sicuro — l’opzione fa esattamente quello che
promette, “don’t modify anything, just show the changes”.

Un’altra differenza utile: deny ignora il pacchetto, reject risponde. “Sometimes it is desirable
to let the sender know when traffic is being denied, rather than simply ignoring it. In these cases,
use
reject instead of deny“. Verso Internet si usa deny, in rete interna reject evita
timeout lunghi a chi sbaglia indirizzo.

Due comandi che fanno risparmiare tempo

Il primo è ufw limit, rate limiting già confezionato per SSH. Il comportamento è definito:

ufw will normally allow the connection but will deny connections if an IP address attempts to
initiate 6 or more connections within 30 seconds.

Sei tentativi in trenta secondi dallo stesso IP e passa a negare. Non sostituisce
l’autenticazione a chiave, ma togliere rumore dai log ha il suo valore.

Il secondo è ufw show listening, ed è quello che dovrebbe venire prima di scrivere qualunque
regola. Mostra

the ports on the live system in the listening state for tcp and the open state for udp, along with
the address of the interface and the executable listening on the port

con, sotto ciascuna porta, le regole che la riguardano. Serve a rispondere alla domanda giusta —
cosa sta ascoltando davvero su questo server — invece di aprire porte a memoria. Per i servizi noti
c’è anche ufw app list, che elenca i profili applicativi disponibili.

Il caso in cui ufw non ti protegge: Docker

Questo è il punto che manda in produzione database raggiungibili da tutti, e non è un dettaglio di
configurazione: è un’incompatibilità dichiarata dalla documentazione di Docker.

Docker and ufw use firewall rules in ways that make them incompatible with each other.

When you publish a container’s ports using Docker, traffic to and from that container gets
diverted before it goes through the ufw firewall settings.

Il perché è tecnico ma breve: “Docker routes container traffic in the nat table, which means that
packets are diverted before it reaches the INPUT and OUTPUT chains that ufw uses”
. Le regole di
ufw vivono in INPUT e OUTPUT, e il pacchetto destinato al container non ci arriva mai.

Quindi: ufw deny 3306 con un MySQL avviato come -p 3306:3306 non chiude niente. E non è un
caso limite, è il comportamento di default — la documentazione di Docker lo scrive in questi
termini: “Publishing container ports is insecure by default. Meaning, when you publish a
container’s ports it becomes available not only to the Docker host, but to the outside world as
well”
.

La soluzione non passa da ufw. Si pubblica la porta solo su localhost, perché “if you include the
localhost IP address (127.0.0.1, or ::1) with the publish flag, only the Docker host can access
the published container port”
:

docker run -p 127.0.0.1:3306:3306 mysql

Il comportamento di default si può cambiare una volta per tutte in /etc/docker/daemon.json,
impostando com.docker.network.bridge.host_binding_ipv4 a 127.0.0.1. Se invece servono regole di
filtro vere sul traffico verso i container, il posto giusto è la catena DOCKER-USER, che la
documentazione descrive come “a placeholder for user-defined rules that will be processed before
rules in the DOCKER-FORWARD and DOCKER chains”
— non FORWARD, dove le tue regole non
vedrebbero quei pacchetti.

Un’ultima nota, utile se hai server non aggiornati: sulle versioni di Docker precedenti alla
28.0.0, “hosts within the same L2 segment (for example, hosts connected to the same network
switch) can reach ports published to localhost”
. Su un VPS in un datacenter condiviso non è una
rassicurazione.

Errori da evitare

  • Accendere il firewall e poi aprire SSH. L’ordine è l’inverso: regola, verifica, enable.
  • Cambiare la default policy via SSH senza controllare che la regola per la porta 22 ci sia.
    È l’unico comando, tra quelli di uso quotidiano, che svuota le catene.
  • Aprire porte a memoria. ufw show listening dice cosa ascolta davvero; spesso la risposta è
    che un servizio non dovrebbe essere in ascolto su un’interfaccia pubblica, e la regola di firewall
    è la cura sbagliata per quel sintomo.
  • Dare per scontato che ufw status descriva il traffico verso i container. Non lo fa, per
    progetto. Il controllo va fatto dall’esterno, su una porta per volta.
  • Usare --force enable a mano. Quella conferma esiste proprio per il caso in cui stai per
    tagliarti fuori.

Fonti


Se hai un VPS in produzione e non sei sicuro di cosa sia raggiungibile dall’esterno, è il tipo di
verifica che si chiude in mezza giornata, container compresi. Scrivici.