VPN in chroot su Android: perché il tunnel funziona e le app lo ignorano
chroot cambia la risoluzione dei percorsi, non il kernel, lo spazio dei nomi di rete o la registrazione VPN di Android. Ne derivano due esiti ed entrambi sorprendono.
Installi una distribuzione Linux dentro un chroot sul telefono, ci metti un client VPN e la shell annuncia un tunnel completato. Poi passi al browser e il traffico esce dai dati mobili come se nulla fosse successo. O il contrario: ogni app perde la connessione perché il client ha aggiunto una rotta predefinita nello spazio dei nomi che Android usa davvero. Questi due sintomi vengono dallo stesso pezzo mancante e nessuno è un bug del client VPN.
La shell dice connesso, le app non sono d’accordo
Il primo esito è quello comune. Un tunnel in spazio utente aperto da un chroot può completare l’handshake, stampare le statistiche e trasportare il traffico che la stessa shell genera, mentre le app di Android continuano a usare la rete predefinita del telefono. Niente le intercetta, perché niente ha detto ad Android che esiste una VPN.
Il secondo esito è quello pericoloso. Se il chroot condivide lo spazio dei nomi di rete di Android, cosa che accade per impostazione predefinita, un processo con privilegi sufficienti può aggiungere rotte e un’interfaccia TUN che toccano tutto il telefono. Allora il tunnel funziona per ogni app, comprese quelle che non volevi instradare, e la connettività del telefono ora dipende da un demone senza gestione del ciclo di vita, senza indicatore di connessione e senza criteri sempre attivo.
Quale dei due hai ottenuto si chiarisce con un comando. Confronta ip route dentro il chroot con lo stesso comando da una shell Android fuori. Un output identico significa che il chroot è nello spazio dei nomi dell’host: ogni modifica di rete che fa vale per tutto il telefono. Un output diverso significa che il launcher ha creato uno spazio dei nomi per lui, e il suo tunnel è invisibile ad Android per costruzione.
Cosa ha cambiato chroot e cosa no
| Object | Effect of a chroot |
|---|---|
| Pathname resolution | Changed: lookups start at the new root |
| Kernel and drivers | Unchanged: the phone's kernel, its TUN driver, its limits |
| Network namespace | Unchanged unless the launcher creates one |
| Android VPN registration | Unchanged: no app consented, no VPN is selected |
La chiamata di sistema chroot fa una cosa: cambia la directory usata per la risoluzione dei percorsi. La pagina di manuale dice esplicitamente che non è un confine di sicurezza e non dice nulla su interfacce, rotte o stato della VPN. Tutto ciò che le persone si aspettano da un container Linux — uno stack di rete privato, un filesystem filtrato, limiti di risorse — viene da altri meccanismi che il launcher può aver usato o meno.
Per questo le affermazioni secondo cui un chroot isola una VPN di solito rispondono alla domanda sbagliata. La domanda rilevante è in quale spazio dei nomi gira il processo e in quale dominio SELinux è confinato, e entrambe sono proprietà di come è stato avviato, non della parola chroot.
TUN richiede tre cose e un nodo di dispositivo è solo una
Un tunnel tradizionale in spazio utente richiede un dispositivo TUN che possa aprire, il supporto del kernel per quel dispositivo e il permesso di configurare l’interfaccia e le rotte. Sono requisiti separati, e creare un nome di percorso sotto /dev non ne soddisfa nessuno da solo:
ls -l /dev/net/tun
ip tuntap add dev tun0 mode tun
Su Android il supporto TUN è di solito compilato nel kernel, perché lo usa il servizio VPN della piattaforma, ma ciò non significa che il tuo processo possa usarlo. Configurare l’interfaccia richiede la capacità di amministrazione della rete e SELinux decide se il processo può raggiungere il nodo del dispositivo. Un rifiuto sembra un errore di permessi senza causa evidente e la risposta è nella traccia di audit:
dmesg | grep avc
logcat -d | grep "avc: denied"
Root non risolve. SELinux vincola i processi con uid 0 esattamente come gli altri, quindi un chroot avviato da una shell root può comunque ricevere un rifiuto per un dispositivo che vede. Disattivare l’applicazione per far funzionare un vecchio installer è una decisione per tutto il telefono con conseguenze molto oltre il tunnel e non è una correzione per un binario obsoleto.
Perché le app di Android ignorano il tuo tunnel
Il modello supportato di Android è un’app VPN che si registra tramite il servizio VPN della piattaforma, chiede il consenso dell’utente una volta e riceve un descrittore di file per il suo tunnel. La piattaforma poi instrada il traffico in quel descrittore e applica l’ambito di app scelto dall’utente o dall’amministratore, per questo lo stesso meccanismo può essere per app, per profilo e combinato con sempre attivo e blocco senza VPN. La guida allo sviluppo VPN di Android descrive quell’handshake, e il punto chiave è che è una registrazione, non un trucco di routing.
Un demone dentro un chroot non si registra mai. Non c’è finestra di consenso, né icona di chiave nella barra di stato, né elenco di inclusione o esclusione per app, né partecipazione ai criteri sempre attivo. Le app continuano quindi a usare la rete predefinita, e il tunnel trasporta solo ciò che invia la shell stessa.
Neanche la direzione opposta aiuta. Spingere rotte nello spazio dei nomi dell’host cattura tutto, incluso il traffico che la piattaforma si aspettava restasse locale, e lo fa senza controllo per app e senza modo per l’utente di vederlo o revocarlo. Non esiste un’impostazione intermedia in cui un demone nel chroot instrada il traffico di una sola app.
La compatibilità binaria è un muro a parte
Anche quando il lato rete è interessante, l’eseguibile ha i suoi requisiti: architettura della CPU, loader dinamico, libreria C e le capacità del kernel che si aspetta. Android usa bionic e non glibc, e un vecchio binario compilato per una distribuzione desktop può richiedere librerie che non esistono più sul dispositivo.
Collegare il nome di un file di libreria a una versione più recente non crea compatibilità ABI, e una libreria crittografica archiviata da un pacchetto di dieci anni fa non è un componente mantenuto. Se il piano dipende da questo, il piano è eseguire codice non mantenuto con privilegi di rete su un telefono.
Dimostralo nel contesto Android e poi pulisci
Prova ciò che ti interessa davvero, dal lato che conta: apri l’app specifica, raggiungi la destinazione privata specifica, verifica quale resolver ha risposto, poi scollega il tunnel e conferma il comportamento documentato. Un risultato da un terminale dentro il chroot è una prova sul contesto di esecuzione di quel terminale e niente di più.
Se hai cambiato rotte dell’host durante l’esperimento, ripristinale e annota cosa hai cambiato. Poi controlla se il telefono ha una VPN sempre attiva configurata, perché un vecchio esperimento che ha lasciato una regola può sembrare un guasto della piattaforma mesi dopo.
Per un’esigenza reale di accesso remoto, usa il client supportato del servizio o un progetto di gateway approvato. La domanda che vale la pena non è se si può far funzionare un installer root, ma se un’implementazione supportata porta le app che ti servono oltre il confine che intendevi, senza lasciare cambiamenti privilegiati senza proprietario sul dispositivo.