Il traffico IPv6 deve passare dalla VPN o andare diretto?
Decidete se ::/0 appartiene dentro il tunnel, con tre condizioni: supporto dell'uscita, raggiungibilità dell'endpoint e reti solo locali.
Il mese scorso due persone mi hanno chiesto esiti opposti: una voleva che la VPN coprisse IPv6, l'altra voleva che la stampante dell'ufficio continuasse a funzionare. Una sola rotta ::/0 non può fornire entrambe da sola. Se quella rotta appartenga dentro il tunnel dipende da tre condizioni, e la prima la esclude per la maggior parte delle uscite commerciali. Se l'uscita non trasporta IPv6, instradare la rotta predefinita al suo interno nasconde il traffico in un tunnel che non ha dove mandarlo.
Tre condizioni decidono la risposta
La rotta predefinita è una riga, ::/0, e corrisponde a qualsiasi destinazione IPv6 senza una rotta più specifica (RFC 4291). Installarla è facile. Decidere se dovreste farlo si riduce a tre condizioni.
Condizione uno: l'uscita a cui vi collegate ha IPv6 funzionante ed è disposta a inoltrarlo. Condizione due: il vostro client può mantenere un percorso verso l'endpoint del tunnel, che di solito si trova nell'intervallo che state per instradare. Condizione tre: potete nominare le destinazioni IPv6 locali che devono restare fuori dal tunnel.
Ogni condizione fallisce a modo suo, ed è per questo che indovinare costa una serata intera. Una condizione uno fallita vi dà silenzio e una tabella di routing che sembra corretta. Una condizione due fallita uccide l'handshake pochi secondi dopo l'inizio. Una condizione tre fallita lascia un tunnel funzionante e un collega scontento alla stampante.
| What you see | Decision |
|---|---|
| Exit answers IPv6 probes, no local IPv6 devices to protect | Route ::/0 into the tunnel |
| Exit answers IPv6 probes, printer or NAS reachable by a global address | Route ::/0, then give those prefixes a route of their own |
| Exit is IPv4-only inside the tunnel | Do not route ::/0; block IPv6 on the client |
Endpoint is reached over IPv6 and sits inside ::/0 | Install a host route for the endpoint first |
L'uscita trasporta davvero IPv6?
Un'interfaccia di tunnel con un indirizzo IPv6 non dimostra nulla sull'altro lato. Molti profili commerciali sono solo IPv4 all'interno: il server vi dà un indirizzo v4 e scarta tutto il resto. Installate sopra una rotta predefinita da AllowedIPs e il kernel invierà volentieri IPv6 in un'interfaccia il cui pari non lo vede mai.
Provate dal client mentre il tunnel è attivo. ping -6 -c 3 2001:4860:4860::8888 e ip -6 route get 2001:4860:4860::8888 rispondono a domande diverse: se torna una risposta e quale dispositivo ha scelto il kernel. Quando il secondo nomina il vostro tunnel e il primo non restituisce nulla, il problema è l'uscita, non la rotta.
Se gestite voi l'uscita, il test del client può passare e il percorso finire comunque in un vicolo cieco. Confermate net.ipv6.conf.all.forwarding=1 sul gateway e che il suo firewall accetti i pacchetti inoltrati fuori dal tunnel. Un pacchetto instradato correttamente verso un'uscita che rifiuta di inoltrare sembra esattamente un bug di routing dal lato client.
Il client può mantenere il suo percorso verso l'endpoint?
Questo è il caso che mi ha colto su un portatile con un endpoint WireGuard solo IPv6. L'indirizzo dell'endpoint è un normale indirizzo globale, quindi cade dentro ::/0. Instradate l'intero prefisso nell'interfaccia e i pacchetti dell'handshake inseguiranno se stessi nel tunnel che stanno ancora cercando di costruire. Il tunnel non sale mai, e nulla nell'output del client dice perché.
wg-quick se ne occupa su Linux. Deriva le rotte da AllowedIPs, le installa nella propria tabella e aggiunge una regola di policy così il traffico verso l'endpoint viene risolto prima tramite la tabella principale: la regola suppress_prefixlength 0 che trovate in ip -6 rule. Per questo wg-quick up riesce dove una ip -6 route add default dev wg0 scritta a mano vi taglia fuori.
Controllate entrambi gli stati con ip -6 route get <endpoint-v6>, prima e dopo aver alzato il tunnel. Se la risposta cambia verso il vostro dispositivo di tunnel, avete rimosso il percorso che il tunnel richiede. Quando scrivete le rotte voi stessi, aggiungete una rotta host verso l'endpoint attraverso il gateway originale, oppure collegatevi all'endpoint tramite IPv4 e lasciate IPv6 al payload.
Cosa deve restare fuori dal tunnel?
Le rotte più specifiche battono quella predefinita, quindi un prefisso on-link o un intervallo ULA con una propria rotta continua a funzionare senza sforzo extra. Questa è la ragione silenziosa per cui tante configurazioni IPv4 a tunnel completo sopravvivono: i prefissi della LAN sono più specifici della rotta predefinita e non entrano mai nel tunnel.
Si rompe quando un dispositivo locale è raggiungibile tramite un indirizzo globale, cosa normale per una stampante a cui lo stesso router ha dato un prefisso pubblico, un NAS o una televisione. Quegli indirizzi corrispondono a ::/0 e a nient'altro, quindi si spostano nel tunnel con tutto il resto e spariscono.
Decidete per destinazione, non per protocollo. Se contano due o tre host locali, date ai loro prefissi una rotta verso l'esterno tramite l'interfaccia fisica, oppure toglieteli dall'insieme negoziato. Se la lista continua a crescere, la risposta onesta è lo split tunneling per IPv6 con un elenco scritto di eccezioni, non una rotta predefinita che continuate a rattoppare.
Se la risposta è no, bloccatelo
Lasciare IPv6 senza rotta non è lo stesso che lasciarlo in pace. Un host con una rotta predefinita IPv6 funzionante continua a usarla mentre il tunnel trasporta IPv4, quindi il traffico esce accanto alla VPN e ogni servizio che preferisce IPv6 vede il vostro indirizzo reale. È una fuga, non un tunnel diviso.
Sceglietene uno e scrivete quale avete scelto. Instradate la rotta predefinita e accettate i compromessi sopra, oppure disattivate IPv6 per la durata del tunnel. Omettere il prefisso da AllowedIPs non basta da solo, perché il sistema operativo mantiene la propria rotta predefinita. Su Linux la versione brutale è sysctl -w net.ipv6.conf.all.disable_ipv6=1; quella più fine fa cadere IPv6 fuori dal tunnel con una regola del firewall. I client per telefono hanno un interruttore esplicito di blocco IPv6 che fa lo stesso lavoro.
Verificatelo come una fuga
Alzate il tunnel e leggete lo stato invece di fidarvi dell'interruttore. ip -6 route show default dovrebbe mostrare una sola rotta predefinita, e ip -6 route get per una destinazione fuori dalla vostra LAN dovrebbe nominare il dispositivo di tunnel. Ripetete entrambi dopo una disconnessione: una rotta predefinita che sopravvive al tunnel è il bug che cercavate.
Poi provate l'altra direzione. Da un host della rete locale, raggiungete il client sul suo indirizzo IPv6 locale. Se smette di funzionare, la condizione tre non è mai stata soddisfatta, qualunque cosa dicesse allora la tabella di routing.
Scrivete l'esito dove lo troverà la persona successiva: quali prefissi restano locali, perché e cosa fa il client dopo una disconnessione. Due righe in un runbook risparmiano al prossimo ingegnere di riscoprire il loop dell'endpoint a mezzanotte.