Ho iniziato dall’esterno, come farebbe un utente: ho digitato il nome nel browser di un telefono in dati mobili e ho guardato la connessione restare appesa fino al timeout. Il nome si risolveva, quindi il DNS andava bene. Anche la pagina di port forwarding del router di casa era un vicolo cieco, perché il suo indirizzo WAN segnava 100.64.31.42, e non è un indirizzo verso cui un provider mi instradi da internet. Quel numero spiega tutto il guasto: la linea sta dietro carrier-grade NAT, quindi l’indirizzo pubblico dalla mia parte del tunnel appartiene al provider. Quello che segue è il percorso che ho fatto da quella scheda bloccata a un servizio che risponde.

Partire dal client e procedere verso l’interno

Prima di cambiare qualsiasi regola, dimostra dove si ferma la catena. Sul portatile ho eseguito dig +short files.example.net e ho ottenuto l’indirizzo del VPS, il che escludeva un DNS obsoleto sulla mia macchina. Poi curl -v --connect-timeout 5 files.example.net:5022 è scaduto invece di essere rifiutato. La differenza conta: un timeout di solito significa che i pacchetti vengono scartati lungo il percorso, mentre un rifiuto significa che qualcosa ha risposto e ha detto no.

Il salto successivo verso l’interno era il VPS stesso. ss -ltnp non mostrava nulla in ascolto sulla 5022, cosa attesa prima che esistesse la regola DNAT. Sulla stessa macchina wg show (o ip -br a show tun0 per OpenVPN) ha confermato che il peer di casa era attivo con un handshake recente. Se il peer è giù, nessuna regola NAT aiuta, e la luce verde sulla pagina VPN del router di casa non significa nulla.

Confermare che la linea di casa è davvero dietro CGNAT

Confronta l’indirizzo che il router riporta sulla sua interfaccia WAN con quello che vede un servizio esterno. Tutto ciò che sta in 100.64.0.0/10, cioè da 100.64.x.x a 100.127.x.x, è l’intervallo di indirizzi condivisi del RFC 6598 e non è raggiungibile da internet. Arrivi alla stessa conclusione quando il router dichiara un normale indirizzo privato e una ricerca pubblica restituisce qualcosa di non correlato. Una linea CGNAT può anche avere un indirizzo pubblico che dura solo fino al lease successivo, così un port forwarding sembra funzionare per un’ora e poi muore in silenzio.

Alcuni provider assegnano davvero un indirizzo pubblico reale ma bloccano le porte in ingresso 80 e 443 sugli account residenziali. Se il tuo servizio è solo web e l’indirizzo WAN è davvero pubblico, chiedi al supporto prima di costruire un relay. Se non lo è, il progetto qui sotto vale allo stesso modo per ogni porta, e vale la pena fermare l’esperimento lato router: il traduttore nella rete del provider chiude la connessione prima che arrivi alla tua interfaccia WAN.

Puntare il nome all’indirizzo del VPS

Il DNS non inoltra il traffico, quindi il record A del servizio deve contenere l’indirizzo pubblico del VPS. Un record A verso 192.168.1.20 si risolve in modo utile solo per i client già dentro la VPN; da internet si risolve in un indirizzo che nessuno può instradare. Per l’accesso amministrativo del VPS stesso tengo un nome separato, così non confondo mai a quale host punta un record.

Se sia il VPS sia la macchina di casa servono HTTP, dai a ciascuno il proprio nome e lascia che il VPS decida chi risponde. Fonderli in un solo nome e sperare che il proxy scelga il backend giusto è esattamente come si finisce a debuggare l’host sbagliato per un pomeriggio.

Una porta in DNAT, SSH resta sulla 22

L’opzione diretta è il NAT di destinazione sul VPS. Attiva il forwarding per il kernel in esecuzione e tieni l’indirizzo tunnel del server di casa nella regola, non il suo indirizzo LAN:

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A PREROUTING -p tcp -d 198.51.100.10 --dport 5022 -j DNAT --to-destination 10.8.0.2

Lascia SSH verso il VPS stesso sulla 22 e inoltra un’altra porta, per esempio 5022, alla macchina di casa. Se inoltri la 22 a una macchina spenta perdi l’unico percorso che avrebbe potuto ripararla. La stessa logica vale per il proxy: tieni la porta di gestione del VPS fuori dalle regole di relay.

Rendi persistenti le regole quando funzionano. Su Debian o Ubuntu, apt install iptables-persistent e poi netfilter-persistent save le scrivono in /etc/iptables/rules.v4. Su un host con firewalld l’equivalente è un forward-port permanente sulla zona, che richiede firewall-cmd --reload prima di essere applicato. Le regole che vivono solo in memoria spariscono al primo riavvio di manutenzione del provider, di solito a un’ora scomoda.

Correggere la rotta di ritorno, o evitare di averne bisogno

DNAT riscrive solo la destinazione. Il server di casa vede una connessione dall’indirizzo del client originale, e se la sua rotta predefinita punta al router di casa, la risposta esce dalla WAN domestica dove il traduttore del provider la scarta. Quell’asimmetria è stato il secondo guasto che ho incontrato, dopo che la regola DNAT sembrava del tutto corretta.

Due correzioni funzionano. Dai alla macchina di casa una rotta di policy così il traffico dal tunnel torna attraverso il tunnel:

ip rule add from 10.8.0.2 table 100
ip route add default dev wg0 table 100

Oppure lascia che il VPS riscriva la sorgente così la risposta ha un indirizzo che può raggiungere, e lascia che conntrack la inverta in uscita:

iptables -t nat -A POSTROUTING -d 10.8.0.2 -p tcp --dport 5022 -j SNAT --to-source 10.8.0.1

La versione SNAT nasconde l’indirizzo reale del client al server di casa, quindi i log applicativi mostrano 10.8.0.1 come peer. Passa l’indirizzo originale al livello applicativo, oppure accetta di perdere il logging per client, ma decidi consapevolmente invece di scoprirlo durante un incidente.

Controllare tutto il percorso dall’esterno

Verifica da una rete che non sia né il VPS né la casa: un telefono in dati mobili, o una shell economica altrove. nc -vz files.example.net 5022 dovrebbe riportare una connessione riuscita, e openssl s_client -connect files.example.net:443 -servername files.example.net dovrebbe mostrare il certificato per il nome pubblico. Poi controlla il security group o il firewall cloud del provider. Una regola DNAT che non vede mai un pacchetto di solito è bloccata un livello sopra l’host.

Infine, decidi cosa succede quando il tunnel cade. Una regola DNAT continua ad accettare connessioni e le riversa in silenzio in un tunnel morto, cosa che sembra esattamente un servizio guasto. Un health check sul VPS che rifiuta la porta mentre il peer è giù costa poche righe e risparmia un’ora di congetture. Dopo, smetti di modificare regole e osservalo per un giorno prima di fidarti.

Articoli correlati