Emulatore Android e VPN sull'host: snapshot DNS e NAT spiegati
Il guest sta dietro il NAT dell'emulatore e conserva la lista DNS copiata all'avvio. Attiva il tunnel prima dell'avvio, poi verifica la risoluzione dall'interno del guest.
Quando l'emulatore non riesce a raggiungere un server di build che il browser sull'host apre senza problemi, la domanda utile non è se la VPN funziona. È quale dei due meccanismi manchi al guest. Il dispositivo virtuale esegue il proprio stack di rete dietro il NAT dell'emulatore, e la sua lista di resolver è una copia presa all'avvio, quindi un tunnel che sale dopo cambia il percorso dell'host senza cambiare ciò che il guest chiede. Capire questi due livelli spiega quasi ogni variante del problema.
Il guest è uno stack separato dietro un NAT fisso
Il networking user-mode di QEMU colloca il guest su 10.0.2.15 con gateway su 10.0.2.2 e il servizio DNS su 10.0.2.3 fino a 10.0.2.6. Il traffico in uscita del guest non viene inoltrato dal kernel dell'host; viene tradotto in socket lato host, uno per connessione del guest, e quei socket escono dallo spazio di indirizzi dell'host. Per questo una connessione del guest eredita la decisione di routing dell'host nel momento in cui viene creata, e per questo il guest non ha alcuna voce nella tabella route che nomini il tuo tunnel.
Lo stesso meccanismo offre al guest una via di fuga utile. 10.0.2.2 è un alias dell'interfaccia loopback dell'host, quindi un servizio in ascolto su 127.0.0.1 sul portatile è raggiungibile dal guest a quell'indirizzo. Tutto ciò che il client espone solo su loopback è raggiungibile così, ed è la base della soluzione proxy descritta più avanti.
Ciò che il guest non può fare è osservare la rete dell'host come se ne fosse un namespace. Non c'è bridge, non c'è tabella route condivisa e non c'è resolver ereditato, solo il servizio NAT che accetta i suoi pacchetti e lo snapshot che il launcher ha preso all'avvio del dispositivo virtuale.
Il DNS è uno snapshot, non una vista in tempo reale
All'avvio l'emulatore legge la lista di resolver dell'host e passa al guest fino a quattro server, esposti sugli indirizzi da 10.0.2.3 a 10.0.2.6. Se in quel momento il tunnel era giù, lo snapshot contiene qualunque resolver pubblico usasse il portatile, e il guest continua a interrogarlo per tutta la sua vita. I nomi pubblici si risolvono, i nomi interni falliscono, e ogni sintomo punta alla VPN mentre il valore davvero obsoleto è una lista di resolver catturata pochi minuti prima che il tunnel salisse.
La correzione dell'ordine non è quindi folclore. Connetti prima il tunnel, poi riavvia l'emulatore, e preferisci un cold boot al ripristino di uno snapshot, perché un'immagine quick-boot può tornare con lo stato di resolver e route salvato prima. Se ti serve vedere cosa usa ora il guest, chiedilo al dispositivo, non all'host:
adb shell dumpsys connectivity | grep -i -A2 dns
adb shell ping -c 1 build.internal.exampleL'emulatore accetta anche un resolver esplicito all'avvio, utile quando sai esattamente quale server deve usare il guest e pericoloso quando quel server è pubblico:
emulator -avd Pixel_API_34 -dns-server 203.0.113.53Un resolver pubblico non ha copia di una zona interna, quindi fissarne uno trasforma un problema di ordine in un guasto permanente. Se il profilo spinge un DNS privato, lascia che sia il tunnel a fornirlo e fai un cold boot del guest invece di codificare un indirizzo.
Il routing segue l'host al momento della connessione
Poiché le connessioni del guest diventano socket dell'host, la decisione di routing viene presa quando il socket viene creato. Tunnel attivo prima della richiesta significa che la richiesta esce dal tunnel, soggetta alle route del profilo. Un profilo split-tunnel che trasporta solo prefissi selezionati lascerà volentieri la tua sottorete privata fuori, quindi la raggiungibilità dell'host è un'ipotesi sul guest, non un risultato. Le connessioni del guest già esistenti mantengono il percorso con cui sono state create, ed è per questo che un client che si riconnette non sblocca sempre un download fermo dentro l'emulatore.
La verifica va fatta sull'interfaccia dell'host, non nelle statistiche del guest:
sudo tcpdump -ni tun0 'host 203.0.113.10'Se la richiesta del guest compare sull'interfaccia del tunnel, il routing funziona e il problema è più in alto. Se compare sull'interfaccia fisica, il profilo non copre quel prefisso, e nessuna riconfigurazione lato guest lo cambierà.
Quando il client offre solo un proxy loopback
Alcuni client commerciali non creano affatto un tunnel di sistema. Espongono un listener HTTP o SOCKS su 127.0.0.1 e si aspettano che le applicazioni lo usino. Il guest non vede direttamente quel socket, ma si può dire all'emulatore di usarlo, tramite i controlli estesi (Settings, poi Proxy, modalità manuale, host 127.0.0.1 e la porta stampata dal client) oppure all'avvio:
emulator -avd Pixel_API_34 -http-proxy http://127.0.0.1:8080Copia la porta dalla finestra del client; una porta indovinata fallisce in silenzio e sembra un tunnel rotto. Tieni presente cosa copre: l'impostazione di proxy HTTP è rispettata dalle applicazioni che la supportano, e i socket grezzi la ignorano del tutto. Se il carico è un binario nativo che apre le proprie connessioni, il percorso proxy non è equivalente a un tunnel e il confronto va fatto esplicitamente.
Dimostra il risultato dall'interno del guest
Separa la risoluzione dal routing con due test nella stessa sessione. Un nome che fallisce mentre l'indirizzo numerico risponde è un problema di snapshot DNS, e la soluzione è l'ordine o un resolver esplicito. Se falliscono entrambi il problema è routing o policy, e il controllo successivo è se l'app gira dentro un work profile con le proprie restrizioni di rete.
Infine, sappi su quale guest stai provando. Immagini dell'emulatore molto vecchie uscivano senza il supporto tun che serve a un client VPN lato guest, quindi nessun profilo poteva mai funzionare lì; le immagini attuali lo includono. Un client che fallisce su un'immagine attuale è un problema di client o di profilo, e ricostruire il tunnel dell'host non cambierà l'esito.