VPN

Android bietet APIs für Entwickler zum Erstellen von VPN-Lösungen (Virtual Private Network). In diesem Leitfaden erfahren Sie, wie Sie Ihren eigenen VPN-Client für Android-Geräte entwickeln und testen.

Übersicht

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

Android umfasst einen integrierten (PPTP und L2TP/IPSec) VPN-Client, der manchmal auch als Legacy-VPN bezeichnet wird. Mit Android 4.0 (API-Level 14) wurden APIs eingeführt, damit App-Entwickler ihre eigenen VPN-Lösungen bereitstellen können. Sie verpacken Ihre VPN-Lösung in einer App, die auf dem Gerät installiert wird. Entwickler erstellen VPN-Apps 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 VPN-Anwendungen (einschließlich durchgehend aktives VPN und per App-basiertes VPN) entwickelt werden. Der integrierte VPN-Client wird dabei nicht behandelt.

Nutzererfahrung

Android bietet eine Benutzeroberfläche, über die Nutzer Ihre VPN-Lösung konfigurieren, starten und beenden können. Außerdem erkennt der Nutzer des Geräts über die System-UI eine aktive VPN-Verbindung. Android zeigt die folgenden UI-Komponenten für VPN-Verbindungen:

  • Bevor eine VPN-Anwendung zum ersten Mal aktiv werden kann, zeigt das System ein Dialogfeld mit einer Verbindungsanfrage an. Im Dialogfeld wird die Person, die das Gerät verwendet, aufgefordert, zu bestätigen, dass sie dem VPN vertraut und die Anfrage akzeptiert.
  • Auf dem Bildschirm „VPN-Einstellungen“ (Einstellungen > Netzwerk und Internet > VPN) werden die VPN-Apps angezeigt, für die eine Person Verbindungsanfragen akzeptiert hat. Hier können Sie Systemoptionen konfigurieren oder das VPN entfernen.
  • In der Schnelleinstellungen wird bei einer aktiven Verbindung ein Infobereich angezeigt. Wenn Sie auf das Label tippen, wird ein Dialogfeld mit weiteren Informationen und einem Link zu den Einstellungen angezeigt.
  • In der Statusleiste wird ein VPN-Symbol (Schlüssel) angezeigt, das auf eine aktive Verbindung hinweist.

Ihre App muss außerdem eine UI bereitstellen, damit der Nutzer des Geräts die Optionen Ihres Dienstes konfigurieren kann. Beispielsweise könnte Ihre Lösung die Einstellungen für die Kontoauthentifizierung erfassen. Apps sollten die folgende UI anzeigen:

  • Steuerung zum manuellen Starten und Beenden einer Verbindung. Durchgehend aktives VPN kann bei Bedarf eine Verbindung herstellen, Nutzer können die Verbindung jedoch bei der ersten Verwendung Ihres VPN konfigurieren.
  • Eine Benachrichtigung, die sich nicht schließen lässt, wenn der Dienst aktiv ist. Die Benachrichtigung kann den Verbindungsstatus oder weitere Informationen enthalten, z. B. Netzwerkstatistiken. Wenn Sie auf die Benachrichtigung tippen, wird Ihre App im Vordergrund angezeigt. Entfernen Sie die Benachrichtigung, nachdem der Dienst inaktiv geworden ist.

VPN-Dienst

Ihre Anwendung verbindet das Systemnetzwerk eines Nutzers (oder ein Arbeitsprofil) mit einem VPN-Gateway. Jeder Nutzer (oder Arbeitsprofil) kann eine andere VPN-App ausführen. Sie erstellen einen VPN-Dienst, mit dem das System Ihr VPN starten und beenden und den Verbindungsstatus verfolgen kann. Ihr VPN-Dienst übernimmt von VpnService.

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

Abbildung 1: So verbindet VpnService das Android-Netzwerk mit dem VPN-Gateway
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 VpnService übernimmt. Deklarieren Sie den VPN-Dienst in Ihrer App-Manifestdatei mit den folgenden Ergänzungen:

  • Schützen Sie den Dienst mit der Berechtigung BIND_VPN_SERVICE, damit nur das System eine Bindung zu Ihrem Dienst herstellen 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 der Manifestdatei Ihrer App 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>

Nachdem Ihre App den Dienst deklariert hat, kann das System den VPN-Dienst Ihrer App bei Bedarf automatisch starten und beenden. Beispielsweise steuert das System Ihren Dienst, wenn ein durchgehendes VPN ausgeführt wird.

Dienst vorbereiten

Rufen Sie VpnService.prepare() auf, um die App darauf vorzubereiten, zum aktuellen VPN-Dienst des Nutzers zu werden. Wenn die Person, die das Gerät verwendet, noch keine Berechtigung für die App erteilt hat, gibt die Methode einen Aktivitäts-Intent zurück. Sie verwenden diesen Intent, um eine Systemaktivität zu starten, bei der um Erlaubnis gefragt wird. Das System zeigt ein Dialogfeld an, das anderen Berechtigungsdialogen ähnelt, z. B. „Kamera- oder Kontaktzugriff“. Wenn Ihre App bereits vorbereitet ist, gibt die Methode null zurück.

Nur eine App kann der aktuell vorbereitete VPN-Dienst sein. Rufen Sie immer VpnService.prepare() auf, da ein Nutzer seit dem letzten Aufruf der Methode durch Ihre App möglicherweise eine andere App als VPN-Dienst festgelegt 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. Führen Sie die Schritte in der folgenden Reihenfolge aus, um die Berechtigung anzufordern und eine Verbindung zu Ihrem Dienst mit dem VPN-Gateway herzustellen:

  1. Rufen Sie VpnService.prepare() auf, um um die Erlaubnis zu bitten (falls erforderlich).
  2. Rufen Sie VpnService.protect() auf, um den Tunnel-Socket Ihrer App außerhalb des System-VPNs zu platzieren 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 erstellt und mit dem Weiterleiten von Traffic über die Schnittstelle beginnt.

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

Kotlin

// 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()

Java

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

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

Das Beispiel im Abschnitt VPN pro App 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. Die Anwendung empfängt die IP-Adressen und Subnetzmasken in der Regel während des Handshakes von einem VPN-Gateway.
addRoute()
Fügen Sie mindestens eine Route hinzu, wenn das System Traffic über die VPN-Schnittstelle senden soll. Routen filtern nach Zieladressen. 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 Anwendung zum Lesen und Schreiben von Paketen in den und aus dem Zwischenspeicher der Schnittstelle verwendet. Die Methode establish() gibt null zurück, wenn Ihre App nicht vorbereitet ist oder die Berechtigung widerrufen wird.

Dienstlebenszyklus

Die Anwendung sollte den Status des vom System ausgewählten VPNs und aller aktiven Verbindungen verfolgen. Aktualisieren Sie die Benutzeroberfläche Ihrer App, um die Nutzer des Geräts über Änderungen zu informieren.

Dienst starten

Sie haben folgende Möglichkeiten, Ihren VPN-Dienst zu starten:

  • Die App startet den Dienst – normalerweise, weil ein Nutzer auf eine Schaltfläche zum Verbinden getippt hat.
  • Das System startet den Dienst, da durchgehend aktives VPN aktiviert ist.

Ihre Anwendung 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(). Bei Android gelten jedoch ab Version 8.0 (API-Level 26) Einschränkungen für Hintergrund-Apps. Wenn diese API-Ebenen unterstützt werden, müssen Sie den Dienst durch Aufrufen von Service.startForeground() in den Vordergrund übertragen. 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 Ihrer App beenden. Beenden Sie den Dienst, anstatt nur die Verbindung zu schließen. Das System unterbricht auch eine aktive Verbindung, wenn die Person, die das Gerät verwendet, auf dem VPN-Bildschirm der App „Einstellungen“ Folgendes tut:

  • die VPN-App trennt oder vergisst
  • deaktiviert durchgehend aktives VPN für eine aktive Verbindung

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

Durchgehend aktives VPN

Android kann einen VPN-Dienst beim Hochfahren des Geräts starten und weiter ausführen, während das Gerät eingeschaltet ist. Diese Funktion wird als durchgehend aktives VPN bezeichnet und ist ab Android 7.0 (API-Level 24) verfügbar. Android behält zwar den Dienstlebenszyklus bei, ist jedoch Ihr VPN-Dienst, der für die VPN-Gateway-Verbindung verantwortlich ist. Durchgehend aktives VPN kann auch Verbindungen blockieren, die nicht das VPN verwenden.

Nutzererfahrung

Unter Android 8.0 oder höher werden die folgenden Dialogfelder angezeigt, um Nutzer auf ein durchgehend aktives VPN aufmerksam zu machen:

  • Wenn durchgehend aktive VPN-Verbindungen getrennt werden oder keine Verbindung hergestellt werden kann, wird eine Benachrichtigung angezeigt, die sich nicht schließen lässt. Durch Tippen auf die Benachrichtigung wird ein Dialogfeld mit weiteren Informationen angezeigt. Die Benachrichtigung verschwindet, wenn das VPN wieder verbunden wird oder jemand die Option „Durchgehend aktives VPN“ deaktiviert.
  • Durch ein durchgehend aktives VPN kann die Person, die ein Gerät verwendet, alle Netzwerkverbindungen blockieren, die das VPN nicht verwenden. Wenn Sie diese Option aktivieren, werden Nutzer von der App „Einstellungen“ gewarnt, dass sie keine Internetverbindung haben, bevor eine VPN-Verbindung hergestellt wird. Die Person, die das Gerät verwendet, wird in der App „Einstellungen“ aufgefordert, fortzufahren oder den Vorgang abzubrechen.

Da das System (und keine Person) eine durchgehend aktive Verbindung startet und stoppt, müssen Sie das Verhalten und die Benutzeroberfläche Ihrer App anpassen:

  1. Deaktivieren Sie alle UI-Elemente, die die Verbindung trennen, da sie vom System und von der App „Einstellungen“ gesteuert wird.
  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 der Nutzer des Geräts 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 Fernzugriff konfigurieren.

Always-On-Modus erkennen

Android enthält keine APIs, mit denen überprüft werden kann, ob das System Ihren VPN-Dienst gestartet hat. Wenn Ihre App jedoch Dienstinstanzen meldet, können Sie davon ausgehen, dass das System nicht gekennzeichnete Dienste für durchgehend aktives VPN gestartet hat. Hier ist ein Beispiel:

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

Blockierte Verbindungen

Eine Person, die das Gerät verwendet, (oder ein IT-Administrator) kann den gesamten Datenverkehr zur Nutzung des VPN erzwingen. Das System blockiert den gesamten Netzwerkverkehr, der das VPN nicht nutzt. Nutzer des Geräts finden in den Einstellungen im Bereich mit den VPN-Optionen den Schalter Verbindungen ohne VPN blockieren.

Always-On-Modus deaktivieren

Wenn Ihre App derzeit kein durchgehend aktives VPN unterstützt, können Sie diese Funktion (unter Android 8.1 oder höher) deaktivieren. Dazu setzen Sie die SERVICE_META_DATA_SUPPORTS_ALWAYS_ON-Dienstmetadaten auf false. Das folgende Beispiel für ein Anwendungsmanifest 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 Ihre App ein durchgehend aktives VPN deaktiviert, deaktiviert das System die UI-Steuerelemente für 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 zulässige oder eine unzulässige Liste erstellen, aber nicht beides. Wenn Sie keine Listen mit zulässigen oder unzulässigen Berechtigungen erstellen, sendet das System den gesamten Netzwerkverkehr über das VPN.

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

Kotlin

// 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()

Java

// The apps that will have access to the VPN.
String[] appPackages = {
    "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.
VpnService.Builder builder = new VpnService.Builder();
PackageManager packageManager = getPackageManager();
for (String appPackage: appPackages) {
  try {
    packageManager.getPackageInfo(appPackage, 0);
    builder.addAllowedApplication(appPackage);
  } catch (PackageManager.NameNotFoundException e) {
    // The app isn't installed.
  }
}

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

Zugelassene Apps

Rufen Sie VpnService.Builder.addAllowedApplication() auf, um eine App der Zulassungsliste hinzuzufügen. Wenn die Liste eine oder mehrere Apps enthält, verwenden nur die Apps das VPN. Alle anderen Apps, die nicht in der Liste enthalten sind, verwenden die Systemnetzwerke so, als wäre das VPN nicht aktiv. Wenn die Zulassungsliste leer ist, verwenden alle Apps das VPN.

Nicht zugelassene Apps

Rufen Sie VpnService.Builder.addDisallowedApplication() auf, um eine App der Liste der unzulässigen Apps hinzuzufügen. Unzulässige Apps nutzen das Systemnetzwerk so, als wäre das VPN nicht aktiv – alle anderen Apps verwenden das VPN.

VPN umgehen

Ihr VPN kann Apps erlauben, das VPN zu umgehen und ihr 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 nach dem Start des VPN-Dienstes nicht mehr ändern. Wenn eine Anwendung ihren Prozess oder einen Socket nicht an ein bestimmtes Netzwerk bindet, wird der Netzwerkverkehr der Anwendung über das VPN fortgesetzt.

Anwendungen, die an ein bestimmtes Netzwerk gebunden sind, haben keine Verbindung, wenn jemand Traffic blockiert, der nicht das VPN durchläuft. Um Traffic über ein bestimmtes Netzwerk zu senden, rufen Apps Methoden wie ConnectivityManager.bindProcessToNetwork() oder Network.bindSocket() auf, bevor sie den Socket verbinden.

Beispielcode

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