VPN

Android fornisce agli sviluppatori le API per creare soluzioni di rete privata virtuale (VPN). Dopo aver letto questa guida, saprai come sviluppare e testare il tuo client VPN per i dispositivi Android.

Panoramica

Le VPN consentono ai dispositivi che non si trovano fisicamente su una rete di accedervi in modo sicuro.

Android include un client VPN integrato (PPTP e L2TP/IPSec), a volte chiamato VPN precedente. Android 4.0 (livello API 14) ha introdotto le API per consentire agli sviluppatori di app di fornire le proprie soluzioni VPN. Puoi pacchettizzare la tua soluzione VPN in un'app che le persone installano sul dispositivo. Normalmente gli sviluppatori creano un'app VPN per uno dei seguenti motivi:

  • Per offrire protocolli VPN non supportati dal client incorporato.
  • Per consentire alle persone di collegarsi a un servizio VPN senza bisogno di complesse configurazioni.

La parte rimanente di questa guida spiega come sviluppare app VPN (incluse le VPN sempre attive e per app) e non riguarda il client VPN integrato.

Esperienza utente

Android fornisce un'interfaccia utente (UI) per aiutare gli utenti a configurare, avviare e arrestare la tua soluzione VPN. L'interfaccia utente di sistema rileva anche la presenza di una connessione VPN attiva sul dispositivo. Android mostra i seguenti componenti dell'interfaccia utente per le connessioni VPN:

  • Prima che un'app VPN possa diventare attiva per la prima volta, il sistema visualizza una finestra di dialogo per la richiesta di connessione. La finestra di dialogo chiede alla persona che utilizza il dispositivo di confermare che la VPN è attendibile e accettare la richiesta.
  • Nella schermata delle impostazioni VPN (Impostazioni > Rete e internet > VPN) vengono visualizzate le app VPN per cui una persona ha accettato le richieste di connessione. C'è un pulsante per configurare le opzioni di sistema o per dimenticare la VPN.
  • Quando una connessione è attiva, la barra Impostazioni rapide mostra un riquadro informativo. Quando tocchi l'etichetta, viene visualizzata una finestra di dialogo con ulteriori informazioni e un link alle impostazioni.
  • La barra di stato include un'icona (chiave) VPN che indica una connessione attiva.

L'app deve anche fornire un'interfaccia utente in modo che la persona che utilizza il dispositivo possa configurare le opzioni del servizio. Ad esempio, la tua soluzione potrebbe dover acquisire le impostazioni di autenticazione dell'account. Le app devono mostrare la seguente UI:

  • Controlli per avviare e interrompere manualmente una connessione. La VPN sempre attiva può connettersi quando necessario, ma consente agli utenti di configurarla la prima volta che utilizzano la tua VPN.
  • Una notifica non ignorabile quando il servizio è attivo. La notifica può mostrare lo stato della connessione o fornire ulteriori informazioni, ad esempio le statistiche della rete. Se tocchi la notifica, la tua app viene messa in primo piano. Rimuovi la notifica quando il servizio diventa inattivo.

Servizio VPN

L'app connette la rete di sistema di un utente (o un profilo di lavoro) a un gateway VPN. Ogni utente (o profilo di lavoro) può eseguire un'app VPN diversa. Puoi creare un servizio VPN che il sistema utilizza per avviare e arrestare la VPN, nonché per monitorare lo stato della connessione. Il tuo servizio VPN eredita da VpnService.

Il servizio funge anche da container per le connessioni del gateway VPN e le relative interfacce dei dispositivi locali. I metodi di chiamata dell'istanza di servizio VpnService.Builder per stabilire una nuova interfaccia locale.

Figura 1. In che modo VpnService connette la rete Android al gateway VPN
Diagramma dell'architettura a blocchi che mostra come VpnService crea un'interfaccia TUN locale nel networking di sistema.

L'app trasferisce i seguenti dati per connettere il dispositivo al gateway VPN:

  • Legge i pacchetti IP in uscita dal descrittore dei file dell'interfaccia locale, li cripta e li invia al gateway VPN.
  • Scrive i pacchetti in entrata (ricevuti e decriptati dal gateway VPN) nel descrittore dei file dell'interfaccia locale.

Esiste un solo servizio attivo per utente o profilo. Avviando un nuovo servizio, interrompe automaticamente un servizio esistente.

Aggiungi un servizio

Per aggiungere un servizio VPN alla tua app, crea un servizio Android ereditati da VpnService. Dichiara il servizio VPN nel file manifest dell'app con le seguenti aggiunte:

  • Proteggi il servizio con l'autorizzazione BIND_VPN_SERVICE in modo che solo il sistema possa essere associato al tuo servizio.
  • Pubblicizza il servizio con il filtro per intent "android.net.VpnService" in modo che il sistema possa trovarlo.

Questo esempio mostra come dichiarare il servizio nel file manifest dell'app:

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

Ora che la tua app dichiara il servizio, il sistema può avviare e interrompere automaticamente il servizio VPN dell'app quando necessario. Ad esempio, il sistema controlla il tuo servizio quando viene eseguita una VPN sempre attiva.

Prepara un servizio

Per preparare l'app a diventare l'attuale servizio VPN dell'utente, chiama VpnService.prepare(). Se la persona che utilizza il dispositivo non ha già concesso l'autorizzazione per la tua app, il metodo restituisce un intent di attività. Utilizzi questo intent per avviare un'attività di sistema che richiede l'autorizzazione. Il sistema mostra una finestra di dialogo simile ad altre finestre di dialogo di autorizzazione, come l'accesso alla fotocamera o ai contatti. Se la tua app è già preparata, il metodo restituisce null.

Il servizio VPN attualmente preparato può essere assegnato a una sola app. Chiama sempre VpnService.prepare() perché una persona potrebbe aver impostato un'app diversa come servizio VPN dall'ultima volta che l'app ha chiamato questo metodo. Per saperne di più, consulta la sezione Ciclo di vita del servizio.

Collega un servizio

Quando il servizio è in esecuzione, puoi stabilire una nuova interfaccia locale connessa a un gateway VPN. Per richiedere l'autorizzazione e connetterti al servizio al gateway VPN, devi completare i passaggi nell'ordine seguente:

  1. Chiama il numero VpnService.prepare() per chiedere l'autorizzazione (se necessario).
  2. Chiama VpnService.protect() per mantenere il socket tunnel dell'app al di fuori della VPN di sistema ed evitare una connessione circolare.
  3. Chiama DatagramSocket.connect() per connettere il socket tunnel dell'app al gateway VPN.
  4. Chiama i metodi VpnService.Builder per configurare una nuova interfaccia TUN locale sul dispositivo per il traffico VPN.
  5. Chiama VpnService.Builder.establish() per fare in modo che il sistema stabilisca l'interfaccia TUN locale e inizi a indirizzare il traffico attraverso l'interfaccia.

Un gateway VPN solitamente suggerisce le impostazioni per l'interfaccia TUN locale durante l'handshake. L'app chiama i metodi VpnService.Builder per configurare un servizio, come mostrato nell'esempio seguente:

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

L'esempio nella sezione VPN per app mostra una configurazione IPv6 che include più opzioni. Devi aggiungere i seguenti valori VpnService.Builder prima di poter creare una nuova interfaccia:

addAddress()
Aggiungi almeno un indirizzo IPv4 o IPv6 insieme a una subnet mask che il sistema assegna come indirizzo dell'interfaccia TUN locale. Solitamente l'app riceve gli indirizzi IP e le subnet mask da un gateway VPN durante l'handshake.
addRoute()
Aggiungi almeno una route se vuoi che il sistema invii il traffico attraverso l'interfaccia VPN. Le route vengono filtrate in base agli indirizzi di destinazione. Per accettare tutto il traffico, imposta una route aperta come 0.0.0.0/0 o ::/0.

Il metodo establish() restituisce un'istanza ParcelFileDescriptor che l'app utilizza per leggere e scrivere pacchetti da e verso il buffer dell'interfaccia. Il metodo establish() restituisce null se la tua app non è preparata o se qualcuno revoca l'autorizzazione.

Ciclo di vita dei servizi

L'app deve monitorare lo stato della VPN selezionata nel sistema e di eventuali connessioni attive. Aggiorna l'interfaccia utente (UI) dell'app per informare la persona che utilizza il dispositivo di eventuali modifiche.

Avvio di un servizio

Il servizio VPN può essere avviato nei seguenti modi:

  • La tua app avvia il servizio, in genere perché una persona ha toccato un pulsante Connetti.
  • Il sistema avvia il servizio perché la VPN sempre attiva è attiva.

L'app avvia il servizio VPN passando un intent a startService(). Per ulteriori informazioni, consulta la sezione Avvio di un servizio.

Il sistema avvia il servizio in background chiamando onStartCommand(). Tuttavia, Android pone limitazioni alle app in background nella versione 8.0 (livello API 26) o successive. Se supporti questi livelli API, devi eseguire la transizione del servizio in primo piano chiamando Service.startForeground(). Per saperne di più, consulta Esecuzione di un servizio in primo piano.

Interruzione di un servizio

Chi utilizza il dispositivo può interrompere il servizio tramite l'interfaccia utente della tua app. Arresta il servizio invece di chiudere semplicemente la connessione. Il sistema interrompe una connessione attiva anche quando la persona che utilizza il dispositivo esegue una delle seguenti operazioni nella schermata VPN dell'app Impostazioni:

  • Disconnette o rimuove l'app VPN
  • disattiva la VPN sempre attiva per una connessione attiva

Il sistema chiama il metodo onRevoke() del tuo servizio, ma questa chiamata potrebbe non avvenire nel thread principale. Quando il sistema chiama questo metodo, un'interfaccia di rete alternativa sta già instradando il traffico. Puoi eliminare in sicurezza le seguenti risorse:

VPN sempre attiva

Android può avviare un servizio VPN all'avvio del dispositivo e mantenerlo in esecuzione mentre il dispositivo è acceso. Questa funzionalità è chiamata VPN sempre attiva ed è disponibile su Android 7.0 (livello API 24) o versioni successive. Anche se Android mantiene il ciclo di vita dei servizi, la connessione del gateway VPN è esclusivamente del servizio VPN. La VPN sempre attiva può anche bloccare le connessioni che non usano la VPN.

Esperienza utente

In Android 8.0 o versioni successive, il sistema mostra le seguenti finestre di dialogo per informare la persona che utilizza il dispositivo della VPN sempre attiva:

  • Quando le connessioni VPN sempre attive si disconnettono o non riescono a connettersi, gli utenti visualizzano una notifica che non può essere ignorata. Toccando la notifica viene visualizzata una finestra di dialogo con ulteriori spiegazioni. La notifica scompare quando la VPN si riconnette o se qualcuno disattiva l'opzione VPN sempre attiva.
  • La VPN sempre attiva consente alla persona che utilizza un dispositivo di bloccare le connessioni di rete che non utilizzano la VPN. Se attivi questa opzione, l'app Impostazioni avvisa gli utenti che non dispongono di una connessione Internet prima della connessione della VPN. L'app Impostazioni chiede alla persona che utilizza il dispositivo di continuare o annullare.

Poiché il sistema (e non una persona) avvia e interrompe una connessione sempre attiva, devi adattare il comportamento dell'app e l'interfaccia utente:

  1. Disattiva qualsiasi UI che disconnette la connessione perché il sistema e l'app Impostazioni controllano la connessione.
  2. Salva qualsiasi configurazione tra un avvio dell'app e l'altro e configura una connessione con le impostazioni più recenti. Poiché il sistema avvia l'app on demand, la persona che utilizza il dispositivo potrebbe non voler configurare sempre una connessione.

Puoi anche utilizzare le configurazioni gestite per configurare una connessione. Le configurazioni gestite aiutano un amministratore IT a configurare la tua VPN da remoto.

Rilevamento sempre attivo

Android non include API per verificare se il sistema ha avviato il servizio VPN. Tuttavia, quando l'app segnala qualsiasi istanza di servizio avviata, puoi presumere che il sistema abbia avviato i servizi non segnalati per la VPN sempre attiva. Ecco un esempio:

  1. Crea un'istanza Intent per avviare il servizio VPN.
  2. Segnala il servizio VPN aggiungendo un extra nell'intent.
  3. Nel metodo onStartCommand() del servizio, cerca il flag negli extra dell'argomento intent.

Connessioni bloccate

Una persona che utilizza il dispositivo (o un amministratore IT) può forzare tutto il traffico a utilizzare la VPN. Il sistema blocca tutto il traffico di rete che non utilizza la VPN. Le persone che utilizzano il dispositivo possono trovare l'opzione Blocca connessioni senza VPN nel riquadro delle opzioni VPN in Impostazioni.

Disattivare l'opzione Sempre attiva

Se al momento la tua app non supporta la VPN sempre attiva, puoi disattivarla (in Android 8.1 o versioni successive) impostando i metadati del servizio SERVICE_META_DATA_SUPPORTS_ALWAYS_ON su false. Il seguente esempio di file manifest dell'app mostra come aggiungere l'elemento dei metadati:

<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>

Quando la tua app disattiva la VPN sempre attiva, il sistema disattiva i controlli dell'interfaccia utente delle opzioni nelle Impostazioni.

VPN per app

Le app VPN possono filtrare le app installate autorizzate a inviare traffico attraverso la connessione VPN. Puoi creare un elenco di app autorizzate o un elenco di app non autorizzate, ma non entrambi. Se non crei elenchi consentiti o non consentiti, il sistema invia tutto il traffico di rete tramite la VPN.

L'app VPN deve impostare gli elenchi prima che la connessione venga stabilita. Se devi modificare gli elenchi, stabilisci una nuova connessione VPN. Quando aggiungi un'app a un elenco, devi installare un'app sul dispositivo.

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

App consentite

Per aggiungere un'app all'elenco delle app consentite, chiama il numero VpnService.Builder.addAllowedApplication(). Se l'elenco include una o più app, solo quelle nell'elenco utilizzeranno la VPN. Tutte le altre app (non presenti nell'elenco) utilizzano le reti di sistema come se la VPN non fosse in esecuzione. Se l'elenco delle app consentite è vuoto, tutte le app usano la VPN.

App non autorizzate

Per aggiungere un'app all'elenco delle app non consentite, chiama il numero VpnService.Builder.addDisallowedApplication(). Le app non consentite usano le reti di sistema come se la VPN non fosse in esecuzione, mentre tutte le altre app usano la VPN.

Bypassa la VPN

La tua VPN può consentire alle app di bypassare la VPN e di selezionare la propria rete. Per bypassare la VPN, chiama VpnService.Builder.allowBypass() quando crei un'interfaccia VPN. Non puoi modificare questo valore dopo aver avviato il servizio VPN. Se un'app non associa il proprio processo o un socket a una rete specifica, il traffico di rete dell'app continua attraverso la VPN.

Le app associate a una rete specifica non hanno una connessione quando qualcuno blocca il traffico che non passa attraverso la VPN. Per inviare il traffico attraverso una rete specifica, le app chiamano metodi come ConnectivityManager.bindProcessToNetwork() o Network.bindSocket() prima di connettere il socket.

Codice di esempio

Android Open Source Project include un'app di esempio chiamata ToyVPN. Questa app mostra come configurare e connettere un servizio VPN.