VPN

Android bietet APIs, mit denen Entwickler VPN-Lösungen (Virtual Private Network) erstellen können. Nachdem du diesen Leitfaden gelesen hast, weißt du, wie du deinen eigenen VPN-Client für Android-Geräte entwickeln und testen kannst.

Übersicht

VPNs ermöglichen Geräten, die sich nicht physisch in einem Netzwerk befinden, den sicheren Zugriff auf das Netzwerk.

Android enthält einen integrierten VPN-Client (PPTP und L2TP/IPSec), der manchmal als Legacy-VPN bezeichnet wird. Mit Android 4.0 (API-Level 14) wurden APIs eingeführt, mit denen App-Entwickler eigene VPN-Lösungen anbieten können. Sie verpacken Ihre VPN-Lösung in eine App, die Nutzer auf dem Gerät installieren. Entwickler erstellen eine VPN-App normalerweise aus einem der folgenden Gründe:

  • Zum Anbieten von VPN-Protokollen, die der integrierte Client nicht unterstützt
  • Damit Nutzer ohne eine komplizierte Konfiguration eine Verbindung zu einem VPN-Dienst herstellen können

Im weiteren Verlauf dieses Leitfadens wird erläutert, wie du VPN-Apps (einschließlich always-on und App-spezifischer VPNs) entwickelst. Der integrierte VPN-Client wird nicht behandelt.

Nutzererfahrung

Android bietet eine Benutzeroberfläche, über die Nutzer deine VPN-Lösung konfigurieren, starten und beenden können. Die System-UI informiert den Nutzer des Geräts auch über eine aktive VPN-Verbindung. Android zeigt die folgenden UI-Komponenten für VPN-Verbindungen an:

  • Bevor eine VPN-App zum ersten Mal aktiv werden kann, wird im System ein Dialogfeld für Verbindungsanfragen angezeigt. Im Dialogfeld wird die Person, die das Gerät verwendet, aufgefordert, zu bestätigen, dass sie dem VPN vertraut, und die Anfrage zu akzeptieren.
  • Auf dem Bildschirm mit den VPN-Einstellungen („Einstellungen“ > „Netzwerk & Internet“ > „VPN“) werden die VPN-Apps angezeigt, für die ein Nutzer Verbindungsanfragen akzeptiert hat. Es gibt eine Schaltfläche, um Systemoptionen zu konfigurieren oder das VPN zu entfernen.
  • In der Leiste mit den Schnelleinstellungen wird ein Infofeld angezeigt, wenn eine Verbindung aktiv ist. Wenn du auf das Label tippst, wird ein Dialogfeld mit weiteren Informationen und einem Link zu den Einstellungen angezeigt.
  • In der Statusleiste wird ein VPN-Symbol (Schlüssel) angezeigt, wenn eine Verbindung aktiv ist.

Deine App muss außerdem eine Benutzeroberfläche bereitstellen, über die die Person, die das Gerät verwendet, die Optionen deines Dienstes konfigurieren kann. Beispielsweise muss Ihre Lösung möglicherweise die Authentifizierungseinstellungen des Kontos erfassen. In Apps sollte die folgende Benutzeroberfläche angezeigt werden:

  • Steuerelemente zum manuellen Starten und Beenden einer Verbindung. Always-on VPN kann bei Bedarf eine Verbindung herstellen, aber Nutzer können die Verbindung bei der ersten Verwendung Ihres VPNs konfigurieren.
  • Eine Benachrichtigung, die nicht geschlossen werden kann, wenn der Dienst aktiv ist. In der Benachrichtigung kann der Verbindungsstatus angezeigt oder weitere Informationen wie Netzwerkstatistiken bereitgestellt werden. Wenn der Nutzer auf die Benachrichtigung tippt, wird die App in den Vordergrund gebracht. Entferne die Benachrichtigung, nachdem der Dienst inaktiv geworden ist.

VPN-Dienst

Ihre App verbindet das Systemnetzwerk für einen Nutzer (oder ein Arbeitsprofil) mit einem VPN-Gateway. Jeder Nutzer (oder jedes Arbeitsprofil) kann eine andere VPN-App ausführen. Du erstellst einen VPN-Dienst, den das System zum Starten und Beenden deines VPNs und zum Verfolgen des Verbindungsstatus verwendet. Ihr VPN-Dienst übernimmt die Eigenschaften von VpnService.

Der Dienst fungiert auch als Container für die VPN-Gateway-Verbindungen und ihre lokalen Geräteschnittstellen. Ihre Dienstinstanz ruft VpnService.Builder-Methoden auf, um eine neue lokale Schnittstelle einzurichten.

Abbildung 1. Wie VpnService das Android-Netzwerk mit dem VPN-Gateway verbindet
Blockarchitekturdiagramm, das zeigt, wie VpnService eine lokale TUN-Schnittstelle im Systemnetzwerk erstellt.

Ihre App überträgt die folgenden Daten, um das Gerät mit dem VPN-Gateway zu verbinden:

  • Liest ausgehende IP-Pakete aus dem Dateideskriptor der lokalen Schnittstelle, verschlüsselt sie und sendet sie an das VPN-Gateway.
  • Schreibt eingehende Pakete (vom VPN-Gateway empfangen und entschlüsselt) in den Dateideskriptor der lokalen Schnittstelle.

Pro Nutzer oder Profil gibt es nur einen aktiven Dienst. Wenn Sie einen neuen Dienst starten, wird ein vorhandener Dienst automatisch beendet.

Dienst hinzufügen

Wenn Sie Ihrer App einen VPN-Dienst hinzufügen möchten, erstellen Sie einen Android-Dienst, der von VpnService erbt. Deklarieren Sie den VPN-Dienst in der Manifestdatei Ihrer App mit den folgenden Ergänzungen:

  • Schütze den Dienst mit der Berechtigung BIND_VPN_SERVICE, damit nur das System eine Bindung an deinen Dienst vornehmen kann.
  • Bewerben Sie den Dienst mit dem Intent-Filter "android.net.VpnService", damit das System Ihren Dienst finden kann.

In diesem Beispiel wird gezeigt, wie Sie den Dienst in Ihrer App-Manifestdatei deklarieren können:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
</service>

Da Ihre App den Dienst jetzt deklariert, kann das System den VPN-Dienst Ihrer App bei Bedarf automatisch starten und beenden. Das System steuert Ihren Dienst beispielsweise, wenn ein always-on VPN ausgeführt wird.

Dienst vorbereiten

Rufen Sie VpnService.prepare() auf, um die App als aktuellen VPN-Dienst des Nutzers vorzubereiten. Wenn die Person, die das Gerät verwendet, Ihrer App noch keine Berechtigung erteilt hat, gibt die Methode einen Aktivitäts-Intent zurück. Mit diesem Intent startest du eine Systemaktivität, die um Erlaubnis bittet. Das System zeigt ein Dialogfeld an, das anderen Berechtigungsdialogfeldern ähnelt, z. B. für den Zugriff auf die Kamera oder die Kontakte. Wenn Ihre App bereits vorbereitet ist, gibt die Methode null zurück.

Es kann jeweils nur eine App der aktuelle vorbereitete VPN-Dienst sein. Rufen Sie immer VpnService.prepare() auf, da ein Nutzer möglicherweise eine andere App als VPN-Dienst festgelegt hat, seit Ihre App die Methode zuletzt aufgerufen hat. Weitere Informationen finden Sie im Abschnitt Dienstlebenszyklus.

Dienst verbinden

Sobald der Dienst ausgeführt wird, können Sie eine neue lokale Schnittstelle einrichten, die mit einem VPN-Gateway verbunden ist. Wenn Sie eine Berechtigung anfordern und Ihren Dienst mit dem VPN-Gateway verbinden möchten, müssen Sie die folgenden Schritte in der angegebenen Reihenfolge ausführen:

  1. Rufe VpnService.prepare() auf, um die Berechtigung anzufordern (falls erforderlich).
  2. Rufen Sie VpnService.protect() auf, um den Tunnel-Socket Ihrer App außerhalb des System-VPNs zu halten und eine zirkuläre Verbindung zu vermeiden.
  3. Rufen Sie DatagramSocket.connect() auf, um den Tunnel-Socket Ihrer App mit dem VPN-Gateway zu verbinden.
  4. Rufen Sie VpnService.Builder-Methoden auf, um eine neue lokale TUN-Schnittstelle auf dem Gerät für VPN-Traffic zu konfigurieren.
  5. Rufen Sie VpnService.Builder.establish() auf, damit das System die lokale TUN-Schnittstelle einrichtet und den Traffic über die Schnittstelle weiterleitet.

Ein VPN-Gateway schlägt normalerweise während des Handshakings Einstellungen für die lokale TUN-Schnittstelle vor. Ihre Anwendung ruft VpnService.Builder-Methoden auf, um einen Dienst zu konfigurieren, wie im folgenden Beispiel gezeigt:

// Configure a new interface from our VpnService instance. This must be done
// from inside a VpnService.
val builder = Builder()

// Create a local TUN interface using predetermined addresses. In your app,
// you typically use values returned from the VPN gateway during handshaking.
val localTunnel = builder
    .addAddress("192.168.2.2", 24)
    .addRoute("0.0.0.0", 0)
    .addDnsServer("192.168.1.1")
    .establish()

Das Beispiel im Abschnitt App-spezifisches VPN zeigt eine IPv6-Konfiguration mit weiteren Optionen. Sie müssen die folgenden VpnService.Builder-Werte hinzufügen, bevor Sie eine neue Schnittstelle einrichten können:

addAddress()
Fügen Sie mindestens eine IPv4- oder IPv6-Adresse zusammen mit einer Subnetzmaske hinzu, die das System als lokale TUN-Schnittstellenadresse zuweist. Ihre App empfängt die IP-Adressen und Subnetzmasken normalerweise während des Handshakings von einem VPN-Gateway.
addRoute()
Fügen Sie mindestens eine Route hinzu, wenn das System Traffic über die VPN-Schnittstelle senden soll. Routen werden nach Zieladressen gefiltert. Wenn Sie den gesamten Traffic akzeptieren möchten, legen Sie eine offene Route wie 0.0.0.0/0 oder ::/0 fest.

Die Methode establish() gibt eine ParcelFileDescriptor-Instanz zurück, die Ihre App zum Lesen und Schreiben von Paketen in den und aus dem Puffer der Schnittstelle verwendet. Die Methode establish() gibt null zurück, wenn Ihre App nicht vorbereitet ist oder jemand die Berechtigung widerruft.

Lebenszyklus des Dienstes

Ihre App sollte den Status des ausgewählten VPNs des Systems und aller aktiven Verbindungen verfolgen. Aktualisiere die Benutzeroberfläche deiner App, damit der Nutzer des Geräts über alle Änderungen informiert ist.

Dienst starten

Der VPN-Dienst kann auf folgende Weise gestartet werden:

  • Ihre App startet den Dienst – normalerweise, weil ein Nutzer auf eine Schaltfläche zum Verbinden getippt hat.
  • Das System startet den Dienst, weil always-on VPN aktiviert ist.

Ihre App startet den VPN-Dienst, indem sie einen Intent an startService() übergibt. Weitere Informationen finden Sie unter Dienst starten.

Das System startet den Dienst im Hintergrund durch Aufrufen von onStartCommand(). Android schränkt Hintergrund-Apps jedoch ab Version 8.0 (API-Level 26) ein. Wenn Sie diese API-Level unterstützen, müssen Sie Ihren Dienst in den Vordergrund verschieben, indem Sie Service.startForeground() aufrufen. Weitere Informationen finden Sie unter Dienst im Vordergrund ausführen.

Dienst beenden

Eine Person, die das Gerät verwendet, kann den Dienst über die Benutzeroberfläche der App beenden. Beenden Sie den Dienst, anstatt nur die Verbindung zu schließen. Das System beendet eine aktive Verbindung auch, wenn der Nutzer des Geräts auf dem VPN-Bildschirm der Einstellungen Folgendes tut:

  • die VPN-App trennt oder entfernt
  • deaktiviert always-on VPN für eine aktive Verbindung

Das System ruft die Methode onRevoke() deines Dienstes auf. Dieser Aufruf erfolgt jedoch möglicherweise nicht im Hauptthread. Wenn das System diese Methode aufruft, wird der Traffic bereits über eine alternative Netzwerkschnittstelle weitergeleitet. Sie können die folgenden Ressourcen sicher entsorgen:

Durchgehend aktives VPN

Android kann einen VPN-Dienst beim Hochfahren des Geräts automatisch starten und ausführen, solange das Gerät aktiv ist. Diese Funktion wird als Always-On-VPN bezeichnet und ist ab Android 7.0 (API-Level 24) verfügbar. Während Android den Dienstlebenszyklus verwaltet, ist dein VPN-Dienst für die VPN-Gateway-Verbindung verantwortlich. Mit einem always-on VPN können auch Verbindungen blockiert werden, die das VPN nicht verwenden.

Nutzererfahrung

Unter Android 8.0 oder höher werden die folgenden Dialogfelder angezeigt, um den Nutzer des Geräts auf das durchgehend aktive VPN aufmerksam zu machen:

  • Wenn die Verbindung zu einem durchgehend aktiven VPN getrennt wird oder keine VPN-Verbindung hergestellt werden kann, wird eine Benachrichtigung angezeigt, die nicht geschlossen werden kann. Wenn Sie auf die Benachrichtigung tippen, wird ein Dialogfeld mit weiteren Informationen angezeigt. Die Benachrichtigung wird ausgeblendet, wenn die VPN-Verbindung wiederhergestellt wird oder jemand die Option „Durchgehend aktives VPN“ deaktiviert.
  • Mit einem always-on VPN kann die Person, die ein Gerät verwendet, alle Netzwerkverbindungen blockieren, die nicht das VPN verwenden. Wenn diese Option aktiviert ist, werden Nutzer in der Einstellungen-App gewarnt, dass sie keine Internetverbindung haben, bevor das VPN eine Verbindung herstellt. In der Einstellungen App wird der Nutzer des Geräts aufgefordert, fortzufahren oder abzubrechen.

Da das System (und nicht eine Person) eine Always-On-Verbindung startet und beendet, müssen Sie das Verhalten und die Benutzeroberfläche Ihrer App anpassen:

  1. Deaktiviere alle Benutzeroberflächen, die die Verbindung trennen, da das System und die Einstellungen-App die Verbindung steuern.
  2. Speichern Sie die Konfiguration zwischen den einzelnen App-Starts und konfigurieren Sie eine Verbindung mit den neuesten Einstellungen. Da das System Ihre App bei Bedarf startet, möchte die Person, die das Gerät verwendet, möglicherweise nicht immer eine Verbindung konfigurieren.

Sie können auch verwaltete Konfigurationen verwenden, um eine Verbindung zu konfigurieren. Mit verwalteten Konfigurationen kann ein IT-Administrator Ihr VPN per Remote-Zugriff konfigurieren.

Always-On-Display erkennen

Android enthält keine APIs, mit denen bestätigt werden kann, ob das System Ihren VPN-Dienst gestartet hat. Wenn Ihre App jedoch alle von ihr gestarteten Dienstinstanzen kennzeichnet, können Sie davon ausgehen, dass das System nicht gekennzeichnete Dienste für Always-On-VPN gestartet hat. Hier ein Beispiel:

  1. Erstellen Sie eine Intent-Instanz, um den VPN-Dienst zu starten.
  2. Kennzeichnen Sie den VPN-Dienst, indem Sie dem Intent ein Extra hinzufügen.
  3. Suchen Sie in der Methode onStartCommand() des Dienstes nach dem Flag in den Extras des Arguments intent.

Blockierte Verbindungen

Eine Person, die das Gerät verwendet, oder ein IT-Administrator kann erzwingen, dass der gesamte Traffic über das VPN geleitet wird. Das System blockiert jeglichen Netzwerkverkehr, der nicht über das VPN läuft. Nutzer des Geräts finden den Schalter Verbindungen ohne VPN blockieren in den Einstellungen im Bereich „VPN-Optionen“.

Always-On-Display deaktivieren

Wenn deine App derzeit kein durchgehend aktives VPN unterstützt, kannst du die Funktion (in Android 8.1 oder höher) deaktivieren, indem du die Dienstmetadaten SERVICE_META_DATA_SUPPORTS_ALWAYS_ON auf false setzt. Das folgende Beispiel für ein App-Manifest zeigt, wie das Metadatenelement hinzugefügt wird:

<service android:name=".MyVpnService"
         android:permission="android.permission.BIND_VPN_SERVICE">
     <intent-filter>
         <action android:name="android.net.VpnService"/>
     </intent-filter>
     <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
             android:value=false/>
</service>

Wenn deine App das always-on VPN deaktiviert, deaktiviert das System die UI-Steuerelemente für die Optionen in den Einstellungen.

Einzelnen Apps die VPN-Nutzung erlauben

VPN-Apps können filtern, welche installierten Apps Traffic über die VPN-Verbindung senden dürfen. Sie können entweder eine Zulassungsliste oder eine Ablehnungsliste erstellen, aber nicht beides. Wenn Sie keine Zulassungs- oder Ablehnungslisten erstellen, wird der gesamte Netzwerkverkehr über das VPN gesendet.

Die Listen müssen in Ihrer VPN-App festgelegt werden, bevor die Verbindung hergestellt wird. Wenn Sie die Listen ändern müssen, richten Sie eine neue VPN-Verbindung ein. Eine App muss auf dem Gerät installiert sein, wenn Sie sie einer Liste hinzufügen.

// The apps that will have access to the VPN.
val appPackages = arrayOf(
    "com.android.chrome",
    "com.google.android.youtube",
    "com.example.a.missing.app"
)

// Loop through the app packages in the array and confirm that the app is
// installed before adding the app to the allowed list.
val builder = Builder()
for (appPackage in appPackages) {
    try {
        packageManager.getPackageInfo(appPackage, 0)
        builder.addAllowedApplication(appPackage)
    } catch (e: PackageManager.NameNotFoundException) {
        // The app isn't installed.
    }
}

// Complete the VPN interface config.
val localTunnel = builder
    .addAddress("2001:db8::1", 64)
    .addRoute("::", 0)
    .establish()

Zugelassene Apps

Wenn Sie eine App zur Zulassungsliste hinzufügen möchten, rufen Sie VpnService.Builder.addAllowedApplication() auf. Wenn die Liste eine oder mehrere Apps enthält, verwenden nur diese das VPN. Alle Apps, die nicht auf der Liste stehen, verwenden die Systemnetzwerke, als würde das VPN nicht ausgeführt. Wenn die Liste mit zulässigen Apps leer ist, verwenden alle Apps das VPN.

Nicht zugelassene Apps

Wenn Sie eine App zur Liste der unzulässigen Apps hinzufügen möchten, rufen Sie VpnService.Builder.addDisallowedApplication() auf. Nicht zugelassene Apps verwenden die Systemnetzwerke, als würde das VPN nicht ausgeführt. Alle anderen Apps verwenden das VPN.

VPN umgehen

Du kannst Apps erlauben, das VPN zu umgehen und ein eigenes Netzwerk auszuwählen. Wenn Sie das VPN umgehen möchten, rufen Sie beim Einrichten einer VPN-Schnittstelle VpnService.Builder.allowBypass() auf. Sie können diesen Wert nicht mehr ändern, nachdem Sie den VPN-Dienst gestartet haben. Wenn eine App ihren Prozess oder einen Socket nicht an ein bestimmtes Netzwerk bindet, wird der Netzwerkverkehr der App weiterhin über das VPN geleitet.

Apps, die an ein bestimmtes Netzwerk gebunden sind, haben keine Verbindung, wenn jemand Traffic blockiert, der nicht über das VPN geleitet wird. Wenn Apps Traffic über ein bestimmtes Netzwerk senden möchten, rufen sie Methoden wie ConnectivityManager.bindProcessToNetwork() oder Network.bindSocket() auf, bevor sie den Socket verbinden.

Beispielcode

Das Open-Source-Projekt für Android enthält eine Beispiel-App namens ToyVPN. Diese App zeigt, wie ein VPN-Dienst eingerichtet und verbunden wird.