Un firewall che ammette solo la porta VPN non è una regola. Sono tre decisioni separate che riguardano per caso lo stesso numero di porta: la catena di input decide cosa può raggiungere un processo locale, la catena di forward decide cosa può attraversare l’host per conto dei client del tunnel, e la tabella NAT riscrive l’indirizzo di origine in uscita. Sbagliane una e il risultato raramente è un’interruzione pulita. Ottieni un host che accetta client ma non carica nulla, o un host che sembra chiuso finché qualcuno non legge l’ordine delle catene.

L’unica porta che non si può chiudere

Il listener VPN deve accettare connessioni da indirizzi che non puoi prevedere, quindi è un servizio pubblico per definizione. È l’unica eccezione su cui è costruito il progetto: una porta UDP per OpenVPN, una porta UDP per WireGuard, una porta TCP per una VPN SSL. Tutto il resto appartiene dietro il tunnel, il che significa che i servizi applicativi dovrebbero legarsi all’indirizzo del tunnel o al loopback, non a tutte le interfacce.

Legarsi però non è applicare. Un processo legato a 0.0.0.0 è raggiungibile solo se una regola lo ammette, e la politica predefinita è il ripiego per il traffico che nessuna regola ha intercettato, non la regola che governa la porta. Un accept posto sopra il tuo drop resta un accept, e spostare un servizio fuori dall’indirizzo pubblico è un miglioramento indipendente, non un sostituto della catena.

Input, forward e NAT rispondono a domande diverse

Il traffico di input è destinato all’host stesso, quindi è lì che vivono il listener VPN e il percorso amministrativo. Il traffico di forward attraversa l’host, e i client del tunnel ne hanno bisogno insieme a net.ipv4.ip_forward=1. La configurazione classica mezzo rotta consente il flusso in uscita e dimentica la direzione di ritorno, così il client completa l’handshake e poi non si carica nulla; una regola conntrack per il traffico stabilito e correlato copre quella direzione.

NAT è la terza domanda. Senza una riscrittura in postrouting, i pacchetti inoltrati escono con l’origine privata del tunnel e vengono scartati più a monte, perché nessuna rete oltre l’host sa instradare il pool di ritorno. Il prefisso di origine in quella regola deve corrispondere al pool VPN, non all’indirizzo dell’host, e lo stesso vale per la regola di forward che nomina la sottorete dei client.

IPv6 è una seconda politica, non un pensiero successivo. Un host con una catena IPv4 rigorosa e una politica aperta su ip6tables non ha chiuso nulla; i servizi restano raggiungibili attraverso l’altra famiglia. O rispecchi il set di regole con ip6tables o disattivi IPv6 di proposito sull’interfaccia pubblica, e annoti quale hai scelto.

Una base leggibile in un solo passaggio

Parti dal drop, poi aggiungi solo ciò che serve all’host. Prima loopback e risposte, poi il percorso amministrativo, poi il listener del tunnel, poi la politica di forward per i client:

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p udp --dport 1194 -m conntrack --ctstate NEW -j ACCEPT
sysctl -w net.ipv4.ip_forward=1
iptables -A FORWARD -i tun0 -o eth0 -s 10.8.0.0/24 -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

I nomi delle interfacce, la porta e il pool sono segnaposto; leggili dal server invece di copiarli. Salvare il risultato conta quanto scriverlo, perché un set di regole che vive solo in memoria scompare al riavvio successivo e riapre l’host in silenzio. Rendilo persistente con il meccanismo della distribuzione e tieni una copia fuori dalla macchina per il giorno in cui la macchina non si avvierà.

Chiudere l’ultima porta senza perdere l’host

C’è un tranello nel progetto. Se SSH esiste solo attraverso il tunnel, un servizio VPN guasto porta con sé SSH, e l’unica via di ritorno può essere una console che non hai mai aperto. Prima di chiudere l’ultima porta pubblica, conferma che quella console esista e che tu possa davvero usarla, oppure tieni un percorso amministrativo stretto che non dipenda dal tunnel: SSH da un indirizzo fisso, solo chiavi, autenticazione con password disattivata.

Applica la modifica da una sessione già stabilita, non attraverso la connessione che stai per filtrare. Pianifica un rollback così che un errore scada invece di diventare permanente, per esempio un’attività systemd-run --on-active=10min che ripristina il set di regole precedente, e tieni aperta una seconda sessione SSH mentre provi la prima. Poi verifica che l’attività di rollback scatti davvero, perché un ripristino non provato equivale a nessun ripristino.

Verifica il percorso, non lo script

Leggi le regole caricate, non il file che avrebbe dovuto caricarle. Da un host esterno autorizzato, conferma che rispondono solo le porte previste; dall’interno del tunnel, conferma che i servizi che volevi pubblicare siano raggiungibili. Una politica di deny predefinita che blocca anche il traffico del tunnel sembra identica a un blocco funzionante finché qualcuno non prova a usarlo.

iptables -S
iptables -t nat -S
ss -lntup
ip -4 addr show

Annota la porta consentita, il pool di client, l’interfaccia del tunnel, l’origine amministrativa e la procedura di console accanto alla configurazione. La prossima modifica al pool o al nome dell’interfaccia deve essere allineata sia nel filtro sia nella regola NAT, altrimenti l’host torna a uno stato mezzo aperto senza che nessuno se ne accorga.

Un’abitudine vale più del set di regole: dopo ogni modifica, dimostra il percorso di forward con una richiesta di un client e il percorso di ripristino con una seconda sessione. Un blocco che non hai usato è un’ipotesi.

Articoli correlati