Wanneer je een VPN-scherm in een iOS-app bouwt, is de eerste verrassing dat de protocollijst van de Instellingen-app niet de API-lijst is. Het Network Extension-framework laat je app één persoonlijke VPN-configuratie maken, eigendom van die app, en het accepteert slechts enkele protocolobjecten. Al het andere dat mensen verwachten, van L2TP tot een gedownload profiel, valt buiten dat oppervlak of is privé voor Apple. De fouten die volgen zijn meestal geen bugs, het is de API die werk weigert dat hij nooit kreeg.

De mythe: de API is een Instellingen-editor

Men gelooft dat omdat iOS IKEv1-, IKEv2-, L2TP- en IPsec-profielen kan configureren, een app elk daarvan programmatisch kan maken. Dat kan niet. Het framework biedt de aanroepende app precies één configuratieslot, en dat slot accepteert slechts een kleine set openbare protocolklassen. Al het andere dat mensen verwachten, van L2TP tot een gedownload profiel, valt buiten dat oppervlak of is privé voor Apple.

De fouten die volgen zijn meestal geen bugs. Het is de API die werk weigert dat hij nooit kreeg, en de oplossing is het scherm ontwerpen rond wat de API bezit.

Drie protocolobjecten en degene die niet bestaan

Je opties zijn IKEv1 via NEVPNProtocolIPSec, IKEv2 via NEVPNProtocolIKEv2, en NETunnelProviderProtocol als je je eigen pakkettunnel-extensie levert. De NEVPNManager-referentie somt de geaccepteerde protocolobjecten op. L2TP en PPTP verschijnen nooit in die lijst omdat Apple er geen klassen voor publiceerde. Een van beide proberen eindigt bij het opslaan met een fout, of erger, met een configuratie die het systeem stilzwijgend weigert te gebruiken.

Desktopcode volgt andere regels. Op macOS stapelt SCNetworkInterfaceCreateWithInterface een interfacetype zoals L2TP over IPv4 en koppelt het aan een netwerkdienst, en networksetup -connectpppoeservice start het resultaat. Beheerdersrechten en een hulpprogramma zijn nodig, omdat het gedeelde geheim en het wachtwoord in de systeemsleutelhanger gaan. Kopieer dat ontwerp naar iOS en niets compileert.

Geef een sleutelhangerreferentie door, niet het wachtwoord

Geen van beide IKE-klassen accepteert een wachtwoord als tekst. De twee eigenschappen passwordReference en sharedSecretReference willen een blijvende sleutelhangerreferentie, die je van SecItemCopyMatching krijgt door kSecReturnPersistentRef te vragen. De itemgegevens doorgeven, of een string in het veld duwen, levert een opslagfout of een verbinding met lege inloggegevens op. De referentie is slechts zo duurzaam als het sleutelhangeritem erachter, daarom moet het item bestaan voordat de configuratie wordt samengesteld. Maak het in dezelfde transactie als het opslaan en je verwijdert een hele klasse ordeningsbugs.

Certificaatauthenticatie neemt een andere route: identityData plus identityDataPassword voor een PKCS#12-blob. Die blob in de app-bundel leveren is een gewoonte uit de voorbeeldcode, geen releasebeslissing, omdat het wachtwoord meereist en elke kopie van de app dezelfde identiteit draagt.

Recht en beschikbaarheid bepalen alles

De beheerder bestaat misschien helemaal niet, en de reden is provisioning. Je app-identificatie heeft de Network Extension-mogelijkheid nodig samen met het recht voor persoonlijke VPN, en het profiel dat de build ondertekent moet beide dragen.

Sla dat over en de gedeelde instantie geeft nil terug, of het opslaan mislukt met een fout die het recht nooit noemt. Beschikbaarheid is de andere poort. Richt je op iets ouder dan iOS 8 en het symbool ontbreekt tijdens runtime, dus de fout is een crash of een nil-beheerder, niet een beheerder zonder configuratie. Controleer de runtime en het recht voordat je je objectgraaf debugt.

Laad, wijzig, sla op en let daarna op de volgorde op aanvraag

Behandel de levenscyclus als laden, wijzigen, opslaan, en sla het laden nooit over. Totdat loadFromPreferences is voltooid, houdt de beheerder niets vast en kan het protocolobject niet worden gelezen of vervangen.

Een tweede opslag vervangt de configuratie in plaats van er nog een toe te voegen, dus een halfvoltooide opslag kan een profiel beschadigen dat werkte. Regels op aanvraag vormen een geordende array, en die volgorde is het beleid. Zet een verbind-overal-regel vóór de verbrekingsregel en op een handmatige verbreking volgt een onmiddellijke herverbinding, wat als een defect lijkt maar de gedocumenteerde evaluatie volgt.

Verplaats de verbrekingsregel naar voren wanneer de actie van de gebruiker moet winnen. Wat buiten de controle van de app blijft, is net zo belangrijk. Routes komen van de tunnel of het serverbeleid in plaats van een voorkeur die jij instelt, waarbij een pakkettunnelprovider ze meldt wanneer hij start. De app kan profielen die door andere apps of configuratieprofielen zijn geïnstalleerd niet tonen of bewerken, dus beloof geen scherm dat elke VPN op het apparaat toont.

Gerelateerde artikelen