Il tunnel è attivo, il servizio è in ascolto e la LAN dell’ufficio continua a non ricevere nulla indietro. Nessuno ha rotto la VPN. Dal lato della LAN, l’indirizzo del tunnel del portatile non esiste finché qualcosa non dice alla LAN come raggiungerlo, e la risposta deve tornare per la stessa strada da cui è uscita la richiesta. Ho visto persone ricostruire tre volte un tunnel perfettamente sano prima che qualcuno guardasse il percorso di ritorno.

Non è il tunnel che si è rotto

Entrambe le estremità stanno su intervalli privati, che è il caso normale e non una configurazione sbagliata. Un indirizzo come 10.8.0.6 è reale dentro il tunnel e inesistente ovunque altro, quindi la LAN non ha motivo di inviargli nulla. Prima di toccare l’instradamento, conferma che il servizio sia raggiungibile dentro il tunnel: sul client, ss -ltn mostra se l’ascoltatore si lega a 127.0.0.1, all’interfaccia della LAN o all’indirizzo del tunnel. Un demone legato solo a loopback non risponde a nessuno tranne che a se stesso, e nessuna rotta, DNAT o inoltro di porta lo cambia.

Un proxy sulla porta 80 o 443 è un altro strumento con un’altra visibilità. Sposta richieste, non pacchetti, quindi è lo strato sbagliato per una connessione diretta a un database, e ricorrervi è il modo in cui questo problema finisce tre strumenti più in fondo prima che qualcuno misuri qualcosa.

Forma della rotta: dì alla LAN dov’è il pool

Una rotta statica sul gateway della LAN è la risposta più pulita quando la LAN e il pool del tunnel non si sovrappongono. Punta il pool verso la macchina che termina il tunnel:

ip route add 10.8.0.0/24 via 192.168.1.12
route add -p 10.8.0.0 mask 255.255.255.0 192.168.1.12   # Windows, persistent

Questo corregge solo una direzione, ed è per questo che le versioni a metà di questa modifica sembrano un progresso. Il lato tunnel ha bisogno della metà corrispondente, altrimenti la risposta lascia il client sulla propria rotta predefinita e muore per strada:

push "route 192.168.1.0 255.255.255.0"

Per una sottorete che sta dietro a un client invece che dietro al server, il server ha bisogno sia di una rotta di sistema sia di una voce iroute nel file di quel client sotto client-config-dir. Senza iroute, OpenVPN sa che il prefisso esiste e non ha idea di quale client lo possieda: i pacchetti arrivano e il demone non ha nulla con cui abbinarli.

Ecco il mito che merita un nome. Una rotta rende un indirizzo raggiungibile; non rende l’altro lato disposto a rispondere. I peer site-to-site aggiungono un altro strato sopra, perché il gateway remoto accetta solo origini dentro i prefissi della sua politica, e un portatile che arriva da un pool domestico non è dentro di essi.

Forma NAT: DNAT in entrata e la riga SNAT che tutti saltano

Quando non puoi toccare il gateway della LAN, o vuoi pubblicare esattamente una porta, la traduzione sull’host VPN funziona senza alcuna rotta sulla LAN. Pubblica la porta, inoltralla e traduci l’origine lungo il percorso verso il tunnel:

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.8.0.6:3000
iptables -A FORWARD -i eth0 -o tun0 -p tcp -d 10.8.0.6 --dport 3000 -j ACCEPT
iptables -t nat -A POSTROUTING -o tun0 -d 10.8.0.6 -p tcp --dport 3000 -j SNAT --to-source 10.8.0.1

La terza riga è quella che sparisce. Saltala e il client vede l’indirizzo originale della LAN come origine, risponde sulla propria rotta predefinita, e quella risposta finisce nel tunnel invece di tornare verso la LAN. Se la connessione sopravvive dipende allora da un instradamento che non hai mai pianificato. Con SNAT il client vede l’indirizzo del tunnel del server, così la risposta segue il tunnel fino all’unico host che può annullare la traduzione.

Il firmware consumer usa i propri nomi di catena e non genera quella traduzione per te. Su Tomato, una regola in ingresso finisce in WANPREROUTING e poi in una catena di filtro come wanin, e un insieme di regole che sembra completo ha ancora bisogno di una riga POSTROUTING manuale. Non è un bug del firmware; è un insieme di regole scritto solo per il traffico in ingresso.

Perché una regola che sembra corretta scarta comunque il pacchetto

La maggior parte di questi guasti non è nella regola che stai fissando. Controlla quattro cose in quest’ordine e fermati alla prima che non vale:

  • Ordine delle catene. iptables -I inserisce in cima e -A aggiunge in fondo, quindi una regola permissiva o bloccante precedente decide per prima.
  • Catena sbagliata. Il traffico indirizzato al router stesso attraversa INPUT, e il traffico inoltrato non la tocca mai. Pubblicare un solo servizio può richiedere regole in entrambe.
  • Flag di direzione. -i tun0 corrisponde al traffico che arriva dai client e -o tun0 a quello che esce verso di loro. Un REJECT con il flag sbagliato blocca qualcosa che non intendevi.
  • Restrizione di origine. Un inoltro che accetta un indirizzo di client funziona da quel client e scarta ogni altra origine senza una parola.

Una regola con un contatore di pacchetti a zero non è affatto sul percorso, e leggere i contatori è il modo più rapido per separare un problema di catena sbagliata da uno di regola sbagliata.

L’inoltro locale, quando nulla sulla LAN può cambiare

Un inoltro locale SSH aggira tutta la questione. Non richiede rotte sulla LAN, né la collaborazione del firewall del client, né una porta pubblicata da qualche parte:

ssh -N -f -L 3000:10.8.0.6:3000 user@vpn-host

Il servizio appare allora su localhost:3000 della macchina che tiene il tunnel, e da nessun’altra parte. Questo è il confine: una postazione, una porta, e l’inoltro muore con la sessione se qualcosa non lo riavvia. Per un amministratore che controlla un database di staging è lo strumento giusto. Per una telecamera o uno scanner che altre persone devono raggiungere non lo è, perché ogni utente avrebbe bisogno del proprio inoltro.

Verifica entrambe le direzioni prima di aggiungere un’altra regola

La traduzione lascia tracce, e leggerle batte tirare a indovinare. Sull’host VPN, conntrack -L mostra se la traduzione è avvenuta, iptables -t nat -L -n -v mostra se i contatori si sono mossi e tcpdump -ni any port 3000 mostra quale indirizzo di origine è davvero arrivato. Sul client, ss -ltn conferma che l’ascoltatore è su un indirizzo che il tunnel può raggiungere.

Poi scegli la forma che tieni. Una porta pubblicata, una rotta per tutto il pool e un inoltro per un solo amministratore sono tre impegni diversi, e mescolarli è il modo in cui una configurazione che funziona diventa una che nessuno saprà spiegare sei mesi dopo. Scrivi quale indirizzo è raggiungibile da dove, e la prossima persona che lo debugga parte dalla risposta invece che dal tunnel.

Articoli correlati