Android chroot VPN: waarom de tunnel werkt en apps hem negeren
Chroot verandert alleen het opzoeken van paden, niet de kernel, de netwerknaamruimte of de VPN-registratie van Android. Daaruit volgen twee uitkomsten, en beide verrassen mensen.
Je krijgt een Linux-distributie onder chroot op de telefoon aan de praat, installeert er een VPN-client in, en de shell meldt dat de tunnel klaar is. Dan ga je naar de browser en gaat het verkeer over mobiele data alsof er niets is gebeurd. Of het omgekeerde: elke app op de telefoon verliest de verbinding omdat de client een standaardroute heeft gezet in de naamruimte die Android werkelijk gebruikt. Beide symptomen komen uit hetzelfde ontbrekende stuk, en geen van beide is een fout in de VPN-client.
De shell zegt verbonden, de apps zijn het er niet mee eens
De eerste uitkomst is de gebruikelijke. Een tunnel in gebruikersruimte die vanuit chroot wordt geopend, kan zijn handshake afronden, zijn statistieken afdrukken en verkeer doorgeven dat dezelfde shell genereert, terwijl de apps van Android het standaardnetwerk van de telefoon blijven gebruiken. Niets onderschept ze, want niets heeft Android verteld dat er een VPN is.
De tweede uitkomst is de gevaarlijke. Als chroot de netwerknaamruimte van Android deelt, wat standaard gebeurt, kan een proces met genoeg rechten routes en een TUN-interface toevoegen die de hele telefoon beïnvloeden. Dan werkt de tunnel voor elke app, ook voor de apps die je niet wilde routeren, en de verbinding van de telefoon hangt nu af van een daemon zonder levenscyclusbeheer, zonder verbindingsindicator en zonder always-on-beleid.
Welke van de twee je hebt gekregen, is met één commando vast te stellen. Vergelijk ip route binnen chroot met hetzelfde commando vanuit een Android-shell erbuiten. Identieke uitvoer betekent dat chroot in de naamruimte van de host zit: elke netwerkwijziging die hij maakt, geldt voor de hele telefoon. Afwijkende uitvoer betekent dat de launcher er een naamruimte voor heeft gemaakt, en dat zijn tunnel voor Android per constructie onzichtbaar is.
Wat chroot veranderde en wat niet
| 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 |
De systeemaanroep chroot doet één ding: hij verandert de map die voor het opzoeken van padnamen wordt gebruikt. De man-pagina zegt uitdrukkelijk dat het geen beveiligingsgrens is, en zegt niets over interfaces, routes of VPN-status. Alles wat mensen van een Linux-container verwachten — een eigen netwerkstack, een gefilterd bestandssysteem, resourcelimieten — komt van andere mechanismen die de launcher wel of niet heeft gebruikt.
Daarom beantwoorden beweringen dat chroot een VPN isoleert meestal de verkeerde vraag. De relevante vraag is in welke naamruimte het proces draait en aan welk SELinux-domein het gebonden is, en beide zijn eigenschappen van hoe het is gestart, niet van het woord chroot.
TUN heeft drie dingen nodig, en een apparaatknooppunt is er maar één
Een traditionele tunnel in gebruikersruimte heeft een TUN-apparaat nodig dat hij kan openen, kernelondersteuning voor dat apparaat en toestemming om de interface en de routes te configureren. Dat zijn aparte vereisten, en het aanmaken van een padnaam onder /dev vervult er zelf geen enkele:
ls -l /dev/net/tun
ip tuntap add dev tun0 mode tun
Op Android is TUN-ondersteuning meestal in de kernel gecompileerd, omdat de VPN-dienst van het platform zelf hem gebruikt, maar dat betekent niet dat jouw proces hem mag gebruiken. Interfaceconfiguratie vereist de netwerkbeheercapaciteit, en SELinux bepaalt of het proces het apparaatknooppunt überhaupt mag bereiken. Een weigering ziet eruit als een rechtenfout zonder duidelijke oorzaak, en het antwoord staat in de audit trail:
dmesg | grep avc
logcat -d | grep "avc: denied"
Root lost dit niet op. SELinux beperkt processen die als uid 0 draaien precies zoals andere, dus een chroot die vanuit een root-shell is gestart kan nog steeds een apparaat worden geweigerd dat hij ziet. Handhaving uitzetten om een oude installer aan de praat te krijgen is een beslissing voor de hele telefoon met gevolgen ver buiten de tunnel, en het is geen oplossing voor een verouderd binair bestand.
Waarom Android-apps je tunnel negeren
Het ondersteunde model van Android is een VPN-app die zich via de VPN-dienst van het platform registreert, één keer toestemming aan de gebruiker vraagt en een bestandsdescriptor voor zijn tunnel krijgt. Het platform stuurt verkeer vervolgens naar die descriptor en handhaaft het app-bereik dat de gebruiker of beheerder heeft gekozen, waardoor hetzelfde mechanisme per app, per profiel en gecombineerd met always-on en blokkeren-zonder-VPN kan zijn. De Android VPN-ontwikkelgids beschrijft die handshake, en het belangrijkste punt is dat het een registratie is, geen routeringstruc.
Een daemon in chroot registreert zich nooit. Er is geen toestemmingsdialoog, geen sleutelicoon in de statusbalk, geen lijst om apps per app op te nemen of uit te sluiten, en geen deelname aan het always-on-beleid. Apps blijven daarom het standaardnetwerk gebruiken, en de tunnel vervoert alleen wat de shell zelf verstuurt.
De andere kant op helpt ook niet. Routes in de naamruimte van de host duwen vangt alles, inclusief verkeer dat het platform lokaal hoopte te houden, en dat gebeurt zonder beheer per app en zonder dat de gebruiker het kan zien of intrekken. Er is geen tussenstand waarin een chroot-daemon verkeer voor slechts één app routeert.
Binaire compatibiliteit is een aparte muur
Zelfs als de netwerkkant interessant is, heeft het uitvoerbare bestand zijn eigen eisen: CPU-architectuur, de dynamische loader, de C-bibliotheek en de kernelfaciliteiten die het verwacht. Android gebruikt bionic in plaats van glibc, en een oud binair bestand dat voor een desktopdistributie is gebouwd, kan bibliotheken nodig hebben die niet meer op het apparaat staan.
Een bibliotheekbestandsnaam symbolisch aan een nieuwere versie koppelen levert geen ABI-compatibiliteit op, en een gearchiveerde cryptobibliotheek uit een tien jaar oud pakket is geen onderhouden component. Als het plan daarvan afhangt, is het plan om niet-onderhouden code met netwerkrechten op een telefoon te draaien.
Bewijs het in de Android-context en ruim daarna op
Test wat je werkelijk belangrijk vindt, vanaf de kant die ertoe doet: open de specifieke app, bereik de specifieke privébestemming, controleer welke resolver antwoordde, verbreek dan de tunnel en bevestig het gedocumenteerde gedrag. Een resultaat uit een terminal binnen chroot is bewijs over de uitvoeringscontext van die terminal en niets meer.
Heb je tijdens het experimenteren hostroutes gewijzigd, zet ze dan terug en noteer wat je hebt gewijzigd. Controleer daarna of de telefoon een always-on-VPN heeft ingesteld, want een ouder experiment dat een regel heeft achtergelaten kan maanden later op een platformfout lijken.
Gebruik voor een echte behoefte aan toegang op afstand de ondersteunde client van de dienst of een goedgekeurd gateway-ontwerp. De vraag die de moeite waard is, is niet of een geroote installer aan de praat te krijgen is, maar of een ondersteunde implementatie de apps die je nodig hebt over de grens brengt die je bedoelde, zonder eigenaarloze geprivilegieerde wijzigingen op het apparaat achter te laten.