Rozdelené tunelovanie v Android VpnService: čo sa naozaj dá smerovať
VpnService odovzdáva len pakety, ktoré builder vloží do svojho deskriptora. Skontrolujte prefixy, autentifikáciu proxy, DNS a zoznamy aplikácií, kým obviníte rozdelené tunelovanie.
Rozdelené tunelovanie v aplikácii VpnService sa láme na niekoľkých konkrétnych miestach a oprava je pre každé iná. Klient zobrazuje len jeden neúspešný cieľ, takže prvou úlohou je zistiť, na ktorú hranicu narážate: trasy, ktoré dostal builder, proxy, ktoré neviete autentifikovať, resolver, ktorý ste len oznámili, alebo zoznam aplikácií, ktorý platforma uplatňuje pre každého používateľa. Práve preto držím kontrolu v štyroch krokoch. Ide v poradí nižšie a zastaví sa pri prvej chybe.
Overte, čo builder odovzdal platforme
VpnService.Builder je zoznam deklarácií. establish() vracia ParcelFileDescriptor pre virtuálne rozhranie a platforma do tohto deskriptora posiela len prevádzku zodpovedajúcu prefixom z addAddress a addRoute; addDnsServer hovorí klientom, ktorý resolver majú osloviť. Cieľ mimo všetkých pridaných prefixov sa do vašej aplikácie nikdy nedostane, nech rozhranie sľubuje čokoľvek. Tento jediný fakt vysvetľuje väčšinu hlásení o pokazenom rozdelenom tunelovaní ešte pred zmenou kódu.
Zaznamenajte zoznamy adries, trás a DNS, ktoré builder zostavil, a porovnajte ich s cieľmi, ktoré zlyhávajú. Chýbajúci /32 pre správcovský host je oveľa bežnejší než pokazený tunel. Ak je zoznam správny, pokračujte; ak nie, zvyšok kontrolného zoznamu je zbytočná práca.
excludeRoute existuje až od API 33
Vylúčenie podľa cieľa je požiadavka, ktorá najčastejšie skončí ako vlákno podpory. builder.excludeRoute(IpPrefix) prišiel v API 33; predtým je jediná podporovaná aproximácia pridať trasy pre všetko okrem vylúčeného prefixu, čo znamená doplnkové prefixy vypočítať sami. Obe volania nie sú zrkadlové obrazy: jedno odčíta z množiny trás rozhrania, druhé množinu stavia od nuly, a staršie zariadenie neznáme volanie vyhodí alebo potichu ignoruje. Vetvite podľa Build.VERSION.SDK_INT a obe cesty testujte na skutočnom hardvéri.
Pri výpočte doplnkov sledujte zarovnanie prefixov. Vylúčenie /24 v trase /16 potrebuje prekrývajúce prefixy, ktoré skutočne opisujú zvyšok, nie jeden zdeformovaný /8. Zaznamenajte konečný zoznam a overte, že vylúčený host sa stále prekladá cestou mimo tunela.
Prihlasovacie údaje proxy sa nedajú vyjadriť
setHttpProxy(ProxyInfo) nasmeruje rozhranie na HTTP alebo SOCKS proxy a PAC URL môže medzi nimi vyberať podľa cieľa. Ani ProxyInfo, ani formát PAC neprenášajú používateľské meno a heslo, takže proxy vyžadujúca autentifikáciu zostáva mimo API platformy. Aplikácie, ktoré ju potrebujú, zvyčajne vkladajú používateľský zásobník, tun2socks alebo podobný engine a na výzvu 407 odpovedajú samy.
Táto voľba má dôsledky. Používateľský zásobník vidí len prevádzku, ktorú doň smerujete, takže musí žiť v tej istej hranici deskriptora a dedí rovnakú požiadavku protect() pre vlastný uplinkový socket. Ak vynecháte protect(), tunel zachytí vlastnú dopravu, čo sa prejaví ako handshake, ktorý sa po prvom pakete zasekne. Testujte proti proxy, ktorá vyžaduje prihlasovacie údaje, pretože otvorená proxy prejde cestou platformy a nič nedokazuje.
Zoznamy aplikácií aj DNS sú tiež deklarácie
Builder má addAllowedApplication aj addDisallowedApplication a navzájom sa vylučujú: volanie oboch vyhodí výnimku. V každom prípade sa zoznam uplatní pre každého používateľa pri nastavení rozhrania, takže aplikáciu v pracovnom profile riadi tunel pracovného profilu a nie ten váš. Čo žiadne API nedáva, je kontrola nad rozhodnutím inej aplikácie: nemôžete zvonku vtlačiť paket do svojho tunela a nemôžete zavrieť ani prekonfigurovať už aktívnu VPN.
DNS sleduje rovnaký vzor. addDnsServer oznamuje adresu resolvera; neumožňuje vašej aplikácii odpovedať na dopyty. Ak chcete názvy prekladať sami, spustite resolver a nasmerujte jeho adresu do tunela. Aplikácie používajúce DoT alebo DoH na pevný názov hostiteľa môžu oznámenú adresu obísť, takže preklad názvov berte ako najlepšiu snahu, ak nezachytíte aj cestu dopytu.
Manifest, always-on a test v štyroch krokoch
Spôsobilosť sa deklaruje v manifeste: BIND_VPN_SERVICE, filter intentu android.net.VpnService a služba v popredí, ak má tunel prežiť na pozadí. Always-on VPN navyše vyžaduje čistý onRevoke, pretože systém ho volá, keď používateľ zmení nastavenia alebo prevezme kontrolu iná aplikácia.
Vykonajte kontroly v poradí a zastavte sa pri prvej chybe. Najprv vypíšte zoznamy adries, trás a DNS odovzdané builderu. Potom overte, že establish() vrátil deskriptor a že transportný socket je chránený. Tretie: vyžiadajte jeden cieľ v zahrnutom prefixe a jeden vo vylúčenom. Štvrté: zistite, ktorý resolver skutočne odpovedal, dopytom na názov hostiteľa, ktorý dokáže preložiť len váš resolver.
Dokončený handshake nedokazuje žiadny z ďalších krokov. Každá kontrola izoluje jednu vrstvu, takže úspech v druhom kroku nič nehovorí o štvrtom.
Referencia VpnService.Builder uvádza celý povrch buildera. Všetko, čo potrebujete navyše, je iné API, iná aplikácia alebo váš vlastný používateľský zásobník, a to je rozhodnutie o dizajne, nie chyba rozdeleného tunelovania.