Android Always-On-VPN: wie der Endpunktname zuerst aufgelöst wird
Android blockiert Verkehr, bevor der Tunnel existiert, doch der Client muss seinen Endpunkt trotzdem auflösen. Hier ist die Reihenfolge und wo es zur Sackgasse wird.
Das Telefon startet neu, Always-On ist aktiviert, Verbindungen ohne VPN blockieren ist eingeschaltet, und der Tunnel kommt nie hoch. Nichts ist falsch konfiguriert: Die Sperre erfüllt ihre Aufgabe, und der eigene Versuch des Clients, seinen Endpunkt zu erreichen, unterliegt ihr ebenfalls. Der Endpunkt muss aufgelöst werden, bevor der Tunnel existiert, und zwar von einem Client, der mit dem Netz sprechen darf, während jede andere App es nicht darf. Zu verstehen, welche Komponente das tut und in welcher Reihenfolge, macht aus einem Rätsel eine Konfigurationsfrage.
Was der VPN-Builder tatsaechlich verlangt
Eine Android-VPN-App übergibt der Plattform keine Serveradresse. Sie baut eine lokale Schnittstelle: Adressen und Routen für den Tunnel, die Resolver, die Apps darin nutzen sollen, und dann liefert establish() einen Dateideskriptor, den die App als rohe IP-Pakete liest und schreibt. Der entfernte Endpunkt taucht in diesem Builder überhaupt nicht auf.
Das ist der entscheidende strukturelle Punkt. Die Plattform erstellt eine tun-Schnittstelle und erzwingt darauf die Richtlinie; die App implementiert den Transport im Userspace, also loest die App selbst ihren Endpunktnamen auf und öffnet die verschlüsselte Verbindung. Ein Hostname als Endpunkt ist deshalb möglich, nicht weil die Plattform Namen unterstützt, sondern weil die Plattform den Endpunkt nie sieht.
Ein Feld des Builders nimmt Adressen: die DNS-Server. Das müssen numerische Resolver-Adressen sein, meist die, die der Betreiber ueber den Tunnel abfragen will. Dieses Feld beschreibt, wohin Apps ihre Anfragen schicken, nachdem der Tunnel steht. Es sagt nichts darüber, wie der Client den Endpunkt findet, und die beiden als dieselbe Einstellung zu behandeln, ist der Anfang der Verwirrung.
Zwei DNS-Fragen, nicht eine
Die erste Frage lautet: wohin schicken Apps ihre Anfragen, sobald der Tunnel steht? Die Antwort ist eine Resolver-Adresse im Tunnel, erreichbar ueber die tun-Schnittstelle, konstruktionsbedingt numerisch.
Die zweite Frage lautet: Wie findet der Client seinen eigenen Endpunkt, bevor dieser Tunnel existiert? Diese Auflösung geschieht außerhalb des Tunnels, im darunterliegenden Netz, im Prozess des Clients. Sie kann einen Namen verwenden und muss einen Resolver nutzen, den das Gerät ohne VPN erreicht.
| Phase | Wer auflöst | Pfad | So sieht ein Fehler aus |
|---|---|---|---|
| Endpunkt-Suche | Die VPN-App | Darunterliegendes Netz, geschützter Socket | Verbindet nie; kein Verkehr auf der Tunnelschnittstelle |
| Peer-Authentifizierung | Client und Server | Tunnel-Transport | Verbindet, bricht dann ab; Zertifikats- oder Schlüsselfehler |
| Private Namensaufloesung | Apps ueber den Tunnel | Resolver im tun | Tunnel steht, interne Seiten loesen nicht auf |
Drei Phasen, drei verschiedene Symptome. Eine einzige Fehlermeldung wie kann nicht verbinden fasst sie zusammen, und deshalb bearbeiten Leute das DNS-Server-Feld, um ein Problem zu beheben, das auftritt, bevor dieses Feld je gelesen wird.
Warum der Bootstrap in eine Sackgasse laufen kann
Always-On mit aktivierter Sperre bedeutet: Verkehr, der nicht im VPN liegt, wird verworfen, auch Verkehr der App, der das VPN gehoert. Die Ausnahme ist ausdrücklich: Die App ruft protect() auf einem Socket auf, was ihn an das darunterliegende Netz bindet, sodass er den Tunnel umgeht und hinausgelassen wird. Jeder VPN-Client muss das für seinen eigenen Transport tun, sonst könnte er nie einen Server erreichen.
Nun betrachte man ein Deployment, bei dem der einzige Resolver, der den Endpunktnamen kennt, selbst im Tunnel liegt. Der Client braucht den Tunnel, um den Namen aufzulösen, und den Namen, um den Tunnel zu bauen. Das ist eine echte zirkuläre Abhängigkeit, und keine Client-Konfiguration loest sie. Es ist ein Designfehler, kein Bug.
Timing legt dieselbe Abhängigkeit in milder Form offen. Beim Booten vor dem ersten Entsperren hat das Gerät möglicherweise noch kein nutzbares Netz; in einem Captive Portal existiert das Netz, blockiert aber alles außer dem Portal; bei einem Wechsel von WLAN zu Mobilfunk ändert sich das darunterliegende Netz des geschuetzten Sockets, und der Client muss das bemerken und neu auflösen. Jedes davon ist ein Bootstrap-Ereignis, und jedes sollte man bewusst testen, statt es um 3 Uhr nachts zu entdecken.
Warum das Festnageln der Antwort keine Loesung ist
Der verlockende Ausweg ist, den Namen einmal aufzulösen und die resultierende Adresse in ein numerisches Feld zu kleben oder sie in einem eigenen Build hart zu kodieren. Es scheint zu funktionieren, und es verwandelt einen Namen in eine Momentaufnahme. Wenn der Betreiber den Endpunkt verschiebt, etwa durch Migration, eine Anycast-Änderung oder eine neue regionale Adresse, waehlt der Client weiter die alte, und nichts in der Oberfläche erklärt warum.
DNS-Eintraege tragen eine Time to Live gerade damit sich Antworten ändern koennen; Caching ist ueber diese Lebensdauer definiert und nicht für immer. Eine Antwort festzunageln verwirft den Mechanismus, der einen Namen besser macht als eine Adresse, und behält zugleich die Betriebskosten der Namensnutzung.
Festnageln geht ausserdem oft mit einer zweiten schlechten Angewohnheit einher: Identitätsprüfungen zu lockern, weil die Verbindung jetzt anders scheitert. Die Verifikation der Serveridentitaet gehoert nicht zum Bootstrap-Problem, und sie zu schwächen tauscht eine DNS-Unannehmlichkeit gegen ein Abfangrisiko.
Entwürfe, die einen Neustart überstehen
Die dauerhafte Loesung ist unspektakulär. Verwende einen Client, der geschützte Sockets korrekt implementiert und seinen Endpunkt im darunterliegenden Netz auflöst, und gib ihm einen Endpunktnamen, der öffentlich oder ueber einen vor dem Tunnel erreichbaren Resolver auflöst. Lass die Sperre aktiviert; sie pauschal abzuschalten, damit DNS funktioniert, entfernt die Eigenschaft, für die das Deployment existiert.
Wo der Endpunkt privat bleiben muss, stelle einen ausdrücklichen Bootstrap-Pfad bereit, den die Plattform nutzen kann, etwa einen ohne Tunnel erreichbaren Resolver oder eine vom Betreiber gepflegte Management-Verbindung, und halte diese Ausnahme eng und durch Richtlinie verantwortet statt durch einen Nutzer-Schalter. Die Android-VpnService-Dokumentation ist die Referenz dafuer, was die Plattform zu Schutz und Lebenszyklus garantiert, und die VpnService.Builder-Referenz bestätigt, dass das DNS-Feld numerische Adressen annimmt.
Dann prüfe in den drei Phasen, in dieser Reihenfolge, an einer frischen Verbindung nach einem Neustart: Der Endpunktname loest auf, der Tunnel wird aufgebaut und der Peer authentifiziert sich, und ein privater Name loest ueber den Resolver des Tunnels auf. Wenn Phase eins scheitert, kann nichts später funktionieren, und die Reparatur gehoert in den Bootstrap-Pfad, nie in das Resolver-Feld.
- Die Endpunkt-Auflösung geschieht in der App, außerhalb des Tunnels, auf einem geschuetzten Socket.
- Das DNS-Feld im Builder ist für Apps im Tunnel, nicht für den eigenen Endpunkt des Clients.
- Ein Resolver, der nur ueber den Tunnel erreichbar ist, ist eine Sackgasse, kein langsamer Start.
- Teste Endpunkt, Authentifizierung und private Auflösung nach einem Neustart getrennt.