Cosa può creare NEVPNManager su iOS (e cosa no)
L’API VPN di iOS costruisce una configurazione di proprietà dell’app da tre classi di protocollo. L2TP, PPTP e i profili firmati non fanno parte di quella superficie.
Quando costruisci una schermata VPN dentro un’app iOS, la prima sorpresa è che l’elenco dei protocolli dell’app Impostazioni non è l’elenco dell’API. Il framework Network Extension consente alla tua app di creare una configurazione VPN personale, di proprietà di quell’app, e accetta solo pochi oggetti protocollo. Tutto il resto che le persone si aspettano, da L2TP a un profilo scaricato, resta fuori da quella superficie o è privato di Apple. Gli errori che seguono di solito non sono bug, è l’API che rifiuta un lavoro che non le è mai stato affidato.
Il mito: l’API è un editor di Impostazioni
Si crede che, poiché iOS può configurare profili IKEv1, IKEv2, L2TP e IPsec, un’app possa crearne qualunque per via programmatica. Non può. Il framework espone esattamente uno slot di configurazione all’app chiamante, e quello slot accetta solo un piccolo insieme di classi di protocollo pubbliche. Tutto il resto che le persone si aspettano, da L2TP a un profilo scaricato, resta fuori da quella superficie o è privato di Apple.
Gli errori che seguono di solito non sono bug. È l’API che rifiuta un lavoro che non le è mai stato affidato, e la correzione è progettare la schermata attorno a ciò che l’API possiede.
Tre oggetti protocollo e quelli che non esistono
Le tue opzioni sono IKEv1 tramite NEVPNProtocolIPSec, IKEv2 tramite NEVPNProtocolIKEv2, e NETunnelProviderProtocol se distribuisci una tua estensione di tunnel a pacchetti. Il riferimento NEVPNManager elenca gli oggetti protocollo accettati. L2TP e PPTP non compaiono mai in quell’elenco perché Apple non ha pubblicato classi per loro. Tentare l’uno o l’altro termina al salvataggio con un errore, o peggio, con una configurazione che il sistema si rifiuta silenziosamente di usare.
Il codice desktop segue regole diverse. Su macOS, SCNetworkInterfaceCreateWithInterface impila un tipo di interfaccia come L2TP su IPv4 e lo collega a un servizio di rete, e networksetup -connectpppoeservice avvia il risultato. Servono diritti di amministratore e uno strumento ausiliario, perché il segreto condiviso e la password finiscono nel portachiavi di sistema. Copia quel design su iOS e nulla si compila.
Passa un riferimento al portachiavi, non la password
Nessuna delle due classi IKE accetta una password come testo. Le due proprietà passwordReference e sharedSecretReference vogliono un riferimento persistente al portachiavi, che ottieni da SecItemCopyMatching chiedendo kSecReturnPersistentRef. Passare i dati dell’elemento, o inserire una stringa nel campo, produce un errore di salvataggio o una connessione con credenziali vuote. Il riferimento è duraturo solo quanto l’elemento del portachiavi dietro di esso, ed è per questo che l’elemento deve esistere prima che la configurazione venga assemblata. Crealo nella stessa transazione del salvataggio ed elimini un’intera classe di bug di ordinamento.
L’autenticazione con certificato segue un’altra strada: identityData più identityDataPassword per un blob PKCS#12. Distribuire quel blob dentro il bundle dell’app è un’abitudine ereditata dal codice di esempio, non una decisione di rilascio, perché la password viaggia con esso e ogni copia dell’app porta la stessa identità.
Diritto e disponibilità condizionano tutto
Il gestore potrebbe non esistere affatto, e il motivo è il provisioning. L’identificatore della tua app ha bisogno della capacità Network Extension insieme al diritto VPN personale, e il profilo che firma la build deve portare entrambi.
Salta questo passaggio e l’istanza condivisa restituisce nil, oppure il salvataggio fallisce con un errore che non menziona mai il diritto. La disponibilità è l’altro cancello. Se punti a qualcosa di più vecchio di iOS 8, il simbolo manca a runtime, quindi il guasto è un crash o un gestore nil, non un gestore senza configurazione. Controlla il runtime e il diritto prima di eseguire il debug del tuo grafo di oggetti.
Carica, modifica, salva, poi osserva l’ordine su richiesta
Tratta il ciclo di vita come carica, modifica, salva, e non saltare mai il caricamento. Finché loadFromPreferences non è completato, il gestore non tiene nulla e l’oggetto protocollo non può essere letto o sostituito.
Un secondo salvataggio sostituisce la configurazione invece di aggiungerne un’altra, quindi un salvataggio a metà può danneggiare un profilo che funzionava. Le regole su richiesta formano un array ordinato, e quell’ordine è la policy. Metti una regola di connessione ovunque prima della regola di disconnessione e a una disconnessione manuale segue una riconnessione immediata, che sembra un difetto ma segue la valutazione documentata.
Sposta la regola di disconnessione in testa quando l’azione dell’utente deve vincere. Ciò che resta fuori dal controllo dell’app conta altrettanto. Le rotte arrivano dal tunnel o dalla policy del server anziché da una preferenza che imposti tu, e un fornitore di tunnel a pacchetti le segnala all’avvio. L’app non può elencare o modificare i profili installati da altre app o da profili di configurazione, quindi non promettere una schermata che mostri ogni VPN del dispositivo.