Android always-on VPN: come si risolve prima il nome dell'endpoint
Android blocca il traffico prima che il tunnel esista, ma il client deve comunque risolvere il proprio endpoint. Ecco l'ordine e il punto in cui si blocca.
Il telefono si riavvia, l'always-on è attivo, «blocca le connessioni senza VPN» è attivo e il tunnel non si alza mai. Nulla è configurato male: il blocco fa il suo lavoro, e il tentativo del client stesso di raggiungere il proprio endpoint vi è soggetto. Il nome dell'endpoint deve essere risolto prima che il tunnel esista, da un client a cui è consentito parlare con la rete mentre tutte le altre app non lo possono fare. Capire quale componente lo fa, e in quale ordine, è ciò che trasforma questo mistero in una questione di configurazione.
Che cosa chiede davvero il builder VPN
Un'app VPN Android non consegna alla piattaforma l'indirizzo di un server. Costruisce un'interfaccia locale: indirizzi e route per il tunnel, i resolver che le app devono usare al suo interno, e poi establish() restituisce un descrittore di file che l'app legge e scrive come pacchetti IP grezzi. L'endpoint remoto non compare mai in quel builder.
Questo è il fatto strutturale chiave. La piattaforma crea un'interfaccia tun e applica la policy su di essa; l'app implementa il trasporto nello spazio utente, il che significa che è l'app stessa a risolvere il nome del proprio endpoint e ad aprire la connessione cifrata. Un endpoint sotto forma di nome host è quindi possibile — non perché la piattaforma supporti i nomi, ma perché la piattaforma non vede mai l'endpoint.
Un solo campo del builder accetta indirizzi: i server DNS. Devono essere indirizzi numerici di resolver, di solito quelli che l'operatore vuole interrogare attraverso il tunnel. Quel campo descrive dove le app inviano le query dopo che il tunnel è attivo. Non dice nulla su come il client trovi l'endpoint, e trattare le due cose come la stessa impostazione è il punto in cui nasce la confusione.
Due domande DNS, non una
La prima domanda è: dove inviano le query le app una volta che il tunnel è attivo? La risposta è un indirizzo di resolver dentro il tunnel, raggiungibile attraverso l'interfaccia tun, numerico per costruzione.
La seconda domanda è: come trova il client il proprio endpoint prima che quel tunnel esista? Quella ricerca avviene fuori dal tunnel, sulla rete sottostante, nel processo del client. Può usare un nome, e deve usare un resolver che il dispositivo riesce a raggiungere senza VPN.
| Stage | Who resolves | Path | Failure looks like |
|---|---|---|---|
| Endpoint lookup | The VPN app | Underlying network, protected socket | Never connects; no tunnel interface traffic |
| Peer authentication | Client and server | Tunnel transport | Connects then drops; certificate or key error |
| Private name lookup | Apps via the tunnel | Resolver inside the tun | Tunnel up, internal sites do not resolve |
Tre fasi, tre sintomi diversi. Un unico messaggio di errore che dice «impossibile connettersi» le appiattisce tutte, ed è per questo che si finisce per modificare il campo del server DNS per risolvere un problema che si verifica prima che quel campo venga mai consultato.
Perché il bootstrap può bloccarsi
Always-on con il blocco attivo significa che il traffico che non è dentro la VPN viene scartato, compreso il traffico dell'app che possiede la VPN. La via d'uscita è esplicita: l'app chiama protect() su un socket, che lo lega alla rete sottostante così aggira il tunnel e può uscire. Ogni client VPN deve farlo per il proprio trasporto, altrimenti non raggiungerebbe mai un server.
Si consideri ora un'installazione in cui l'unico resolver che conosce il nome dell'endpoint è esso stesso dentro il tunnel. Il client ha bisogno del tunnel per risolvere il nome, e del nome per costruire il tunnel. È una vera dipendenza circolare, e nessuna configurazione del client la risolve. È un errore di progettazione, non un bug.
Il tempismo espone la stessa dipendenza in forme più blande. All'avvio, prima del primo sblocco, il dispositivo può non avere ancora una rete utilizzabile; su un captive portal la rete esiste ma blocca tutto tranne il portale; nel passaggio da Wi-Fi a rete cellulare la rete sottostante del socket protetto cambia e il client deve accorgersene e risolvere di nuovo. Ognuno di questi è un evento di bootstrap, e ognuno merita di essere testato di proposito invece di essere scoperto alle 3 di notte.
Perché fissare la risposta non è una soluzione
La scorciatoia tentante è risolvere il nome una volta e incollare l'indirizzo ottenuto in un campo numerico, oppure codificarlo in una build personalizzata. Sembra funzionare, e trasforma un nome in un'istantanea. Quando l'operatore sposta l'endpoint — una migrazione, un cambiamento anycast, un nuovo indirizzo regionale — il client continua a chiamare quello vecchio e nulla nell'interfaccia spiega perché.
I record DNS hanno un time to live proprio perché le risposte possano cambiare; la cache è definita in base a quella durata, non per sempre. Fissare una risposta scarta il meccanismo che rende un nome migliore di un indirizzo, mantenendo però il costo operativo dell'aver usato un nome.
Il fissaggio tende anche ad accompagnarsi a una seconda cattiva abitudine: allentare i controlli di identità perché ora la connessione fallisce in modo diverso. La verifica dell'identità del server non fa parte del problema di bootstrap, e indebolirla scambia un fastidio DNS con un rischio di intercettazione.
Progetti che sopravvivono a un riavvio
L'assetto durevole è poco appariscente. Usa un client che implementi correttamente i socket protetti e risolva il proprio endpoint sulla rete sottostante, e dagli un nome di endpoint che si risolva pubblicamente, o tramite un resolver raggiungibile prima del tunnel. Tieni attivo il blocco; disattivarlo del tutto per far funzionare il DNS rimuove la proprietà per cui l'installazione esiste.
Dove l'endpoint deve restare privato, fornisci un percorso di bootstrap esplicito che la piattaforma possa usare — un resolver raggiungibile senza il tunnel, o una connessione di gestione mantenuta dall'operatore — e mantieni quell'eccezione ristretta e di proprietà della policy, non di un interruttore utente. La documentazione di Android VpnService è il riferimento per ciò che la piattaforma garantisce su protezione e ciclo di vita, e il riferimento a VpnService.Builder conferma che il campo DNS accetta indirizzi numerici.
Poi verifica le tre fasi, nell'ordine, su una connessione nuova dopo un riavvio: il nome dell'endpoint si risolve, il tunnel si stabilisce e il peer si autentica, e un nome privato si risolve tramite il resolver del tunnel. Se la prima fase fallisce, nulla di successivo può funzionare, e la correzione appartiene al percorso di bootstrap — mai al campo del resolver.
- La risoluzione dell'endpoint avviene nell'app, fuori dal tunnel, su un socket protetto.
- Il campo DNS del builder serve alle app dentro il tunnel, non all'endpoint del client stesso.
- Un resolver raggiungibile solo attraverso il tunnel è un blocco, non un avvio lento.
- Verifica separatamente endpoint, autenticazione e lookup privato dopo un riavvio.