In un momento il portatile è sul Wi-Fi dell'ufficio, in quello successivo è collegato a un telefono, e l'indicatore del tunnel è di nuovo verde prima che tu abbia finito di cambiare finestra. Una luce verde riporta solo che il tunnel si è riconnesso; che cosa la politica di routing e blocco abbia fatto durante il vuoto è un'altra domanda. Quella politica è un insieme di condizioni di corrispondenza compilate all'ultima installazione, e nomina un'interfaccia, un indirizzo e una famiglia di indirizzi che il passaggio ha appena rimosso. Lo stesso cambio produce quindi tre esiti che sembrano identici: il blocco continua, la politica blocca la propria ripresa, oppure si apre un percorso che nulla copre.

Niente sullo schermo separa questi tre stati, quindi chiedi a quali identificatori fossero ancorate le regole invece di che colore mostri l'indicatore. Su Linux una regola può nominare un'interfaccia o un ipset riempito da essa; su Android si può dire a un VpnService quali reti sottostanti seguire tramite setUnderlyingNetworks. Ogni ancoraggio punta a un collegamento che sta per essere sostituito, e ognuno va rivalutato dopo.

Cosa riscrive davvero il passaggio

Quando il vecchio collegamento scompare, il kernel ne rimuove gli indirizzi, ritira le rotte che vi passavano attraverso e ricostruisce la selezione dell'indirizzo di origine da ciò che resta. La rotta predefinita si sposta sul nuovo gateway, e l'indice di interfaccia a cui facevano riferimento le vecchie regole è sparito o riciclato per un altro dispositivo. Anche i resolver per collegamento vengono aggiornati, anche se non nello stesso istante delle rotte. Tutto questo avviene sotto il client VPN, quindi un client che osserva solo lo stato del proprio tunnel non ne vede nulla.

Il guaio è che una regola codifica un identificatore preciso invece dell'intenzione che c'è dietro. Una regola che scarta tutto ciò che non esce dall'interfaccia del tunnel codifica un nome di interfaccia; una regola che consente l'endpoint codifica un indirizzo che la nuova rete potrebbe instradare altrove. Quando l'identificatore svanisce la regola non diventa visibilmente sbagliata: o smette di corrispondere a qualsiasi cosa o inizia a corrispondere alla cosa sbagliata.

Ricostruire il tunnel esterno senza aprire un buco

Il trasporto esterno era legato al vecchio indirizzo di origine, quindi deve aprire un nuovo socket dal nuovo. Quando l'endpoint è un nome host invece di un indirizzo letterale, una risoluzione deve riuscire mentre il blocco è ancora in vigore. È la trappola dell'ordine: una politica abbastanza rigida da scartare tutto tranne l'endpoint scarterà anche la risoluzione necessaria a trovarlo, a meno che la risoluzione avvenga prima che il blocco venga alzato o sia trasportata da un tunnel ancora vivo. Entrambi gli ordini possono funzionare, ma solo se il client sa in quale si trova.

I client differiscono qui in un modo che conta durante la diagnosi. Alcuni risolvono prima e installano le regole dopo, così un passaggio costa un'interruzione visibile e poi si riprende da solo; altri tengono su il blocco e contano sul tunnel sopravvissuto per portare la risoluzione, cosa che fallisce del tutto quando il tunnel è morto con il collegamento. Aggiungere una rotta diretta predefinita ampia per ripristinare la connettività fa sempre passare il test e lascia sempre la fuga che stavi impedendo.

Una rotta che punta ancora al vecchio gateway dà la stessa ripresa bloccata da un'altra causa, quindi leggi la tabella prima di dare la colpa al client. I comandi qui sotto nominano l'interfaccia e l'indirizzo di origine che il kernel userebbe davvero, e questo risolve in una riga se il vecchio percorso è ancora installato.

ip route get 1.1.1.1
ip -6 route get 2606:4700:4700::1111

Quando il nuovo collegamento porta IPv6 e le regole no

Le reti mobili e ospiti spesso distribuiscono un prefisso IPv6 globale anche quando la LAN cablata dell'ufficio non l'ha mai fatto. Una politica scritta solo come regole IPv4, la forma predefinita della maggior parte delle ricette iptables e nftables fatte a mano, non ne copre nulla. Il tunnel resta connesso, le applicazioni con raggiungibilità IPv6 ne escono fuori, e l'indicatore non può mostrare la differenza perché il tunnel è davvero attivo. Il caso speculare è altrettanto comune: un insieme di regole solo per IPv6 non fa proprio nulla su una rete che non offre v6.

Confronta ciò che il collegamento offre con ciò che la politica menziona, sui due lati del passaggio. Questi comandi stampano gli stessi fatti con cui lavora il client:

ip -4 route show default
ip -6 route show default
sudo nft list ruleset | grep -c 'ip6 '

Se il secondo comando stampa una rotta predefinita mentre il terzo restituisce zero, il nuovo collegamento può far uscire la macchina in IPv6 senza alcuna regola che la sorvegli. Esegui di nuovo tutti e tre dopo il cambio, perché il numero interessante è la differenza tra le due esecuzioni. Se il secondo non stampa nulla, vale il caso inverso e qualsiasi politica carica di v6 lì è solo decorazione.

Il resolver cambia mani a metà sessione

Sui sistemi moderni i resolver sono legati ai collegamenti, quindi un nuovo collegamento arriva con un nuovo insieme di resolver e forse con una vista split-DNS diversa. systemd-resolved tiene uno scope per collegamento e sceglie tra loro in base alla rotta, mentre macOS legge resolver con scope dallo store dinamico e rispetta l'ordine delle interfacce. Un client che installa il proprio resolver compete con quello del collegamento, e regole di priorità che nessuno guarda decidono il vincitore. Allora la risoluzione dei nomi può rompersi mentre il filtraggio dei pacchetti fa esattamente ciò per cui è stato scritto.

Leggi il resolver attivo invece di quello configurato. Su un host systemd il sottocomando status mostra da quale collegamento è venuto ogni scope e se è in vigore un nome private-DNS:

resolvectl status --no-pager
resolvectl query intranet.example.com

Se la query esce dal collegamento fisico invece che dal tunnel, il client ha perso la competizione di priorità e nessuna messa a punto del firewall sistemerà la denominazione. Confronta il percorso della query sui due lati del passaggio per vedere quale si è spostato. Un nome che risolve in un indirizzo privato che la nuova rete non può instradare è un terzo caso, e si presenta come timeout invece che come errore.

Esercita il passaggio e conserva il registro

Riproduci il cambiamento di proposito invece di aspettarlo. Alza il tunnel sul primo collegamento, avvia un trasferimento lungo e una risoluzione ripetuta, poi cambia collegamento e guarda quale sopravvive. Annota l'intervallo tra la scomparsa del vecchio collegamento e la segnalazione del tunnel di essere connesso, perché è in quella finestra che la politica ha tenuto o no. Esegui la sequenza anche nell'altra direzione, così il risultato non è a senso unico.

Le eccezioni meritano un passaggio proprio, perché un bypass giustificato sulla vecchia rete non è automaticamente giustificato sulla nuova. Una stampante, un NAS o un portale captive sulla LAN di casa non hanno equivalente sulla rete mobile, e l'eccezione che li raggiungeva diventa una regola senza equivalente.

Annota i nomi delle interfacce, le famiglie offerte, il resolver in vigore e la durata dell'interruzione per ciascuna direzione. Una riconnessione prova che il trasporto si è ripreso, non che il blocco abbia tenuto durante il vuoto, e sono affermazioni separate da mettere in un ticket. Con quel registro il passaggio successivo si confronta con uno stato noto invece che con la memoria.

Articoli correlati