iOS VPN という名前には三つの異なる製品があり、API を共有していません。Personal VPN はシステム内蔵の IPsec と IKEv2 トランスポートに乗ります。Custom tunnel は独自のプロトコルを network extension の中で動かします。Configuration profile はどちらかを構成し、on-demand の動作を追加できます。話す必要のあるトランスポートで選ぶほうが、先にコンパイルが通ったチュートリアルで選ぶより速いです。

各手順は前の手順が有効な構成を生み出したことを前提とするため、以下のチェックは順番に実行してください。

ステップ 1: トランスポートで選ぶ

サーバーが IKEv2 または Cisco 形式の IPsec なら、NEVPNManager で足り、システムのトランスポートが無料で付いてきます。サーバーが OpenVPN、WireGuard、その他を話すなら、サードパーティ製プロトコル用のフックがないため NEVPNManager では助けになりません。その場合は、プロトコルを実装する packet tunnel provider を伴う NETunnelProviderManager が必要です。プロファイルは構成を配布するだけで、OS にないトランスポートを追加することはできません。

PPTP は現在のシステムでは対象外です。共有シークレットの IPsec は、今でも remoteIdentifier 内のグループ名、シークレットを保持する keychain 項目、サーバーがユーザー名を求めたときの拡張認証を期待します。欠けた部分はどれも同じ一般的な構成エラーとして現れるので、何かをデバッグする前にすべて追加してください。

ステップ 2: 保存、読み込み、開始の順序を守る

NEVPNManager は preferences に書き込み、それを読み戻し、それから初めて開始します。プロトコルと manager のオブジェクトを埋め、saveToPreferences を呼び、loadFromPreferences を呼び、load completion handler から startVPNTunnel を呼びます。読み込みが戻る前に開始すると、それ以外は正しい構成に対して configuration-invalid エラー、NEVPNErrorDomain code 1 が発生します。

形は短いので、一か所にまとめておけます:

manager.protocolConfiguration = proto
manager.isEnabled = true
manager.saveToPreferences { }
manager.loadFromPreferences { manager.connection.startVPNTunnel() }

startVPNTunnel は要求が受け付けられたときに戻り、トンネルが上がったときに戻るわけではありません。NEVPNStatusDidChange を監視し、実際の状態は connection.status を読んでください。呼び出し地点で成功とみなすアプリは、extension がまだネゴシエーション中なのに VPN 接続済みと報告します。connecting 状態は一瞬なら正常ですが、そこから抜けない状態こそ、ログに残す価値のある障害です。

ステップ 3: provider プロセスに必要なものを与える

Custom transport は独自の bundle identifier と独自の entitlement を持つ別プロセスで動きます。container アプリと provider の両方が署名時に network-extension entitlement を必要とし、App Store 外の Developer ID ビルドも provisioning profile にそれを含める必要があります。entitlement の欠落は、extension が起動しないだけなのでプロトコルのバグのように見えます。

借りられる公開の socket descriptor はありません。NEPacketTunnelProvider は read、write、cancel を備えた NEPacketTunnelFlow を渡し、プライベートキーのパスに手を伸ばすコードはリリース間で壊れます。アプリがバックグラウンドでも provider は動き続けるので、そのために container アプリにバックグラウンドモードは不要です。

ステップ 4: on-demand はルールの集合であり、スイッチではない

isOnDemandEnabled はルールを有効にするだけで、トンネルがいつ上がるかは NEOnDemandRuleConnect とその仲間が決めます。probe URL を伴う evaluate-connection ルールは、その URL の良し悪しと同じだけの価値しかありません。トンネルなしで probe が応答すると、ルールはプライベートネットワークにすでに到達できると判断して決して接続しないので、内側からしか応答しないものに向けてください。

Always-on は別の約束です。監視対象のデバイスと管理されたプロファイルが必要で、監視されていないユーザーはプロファイルをいつでも削除できます。フリートが監視対象でない限り、always-on に依存する設計を出荷しないでください。

ステップ 5: ラベルではなく経路を証明する

tun、tap、ppp、ipsec のインターフェースをスキャンしても、VPN が構成されていることしか証明できず、あなたの要求がそれを使ったことは証明できません。usesInterfaceType(.other) を伴う NWPathMonitor は仮想インターフェースが利用可能だと述べ、viability handler は一つのホストをテストできます。どちらも出口経路を証明しないので、トンネルからしか到達できないリソースへの要求と組み合わせてください。インターフェース名は診断の助けであり、アプリのロジックが分岐すべきゲートではありません。

現場での確認には、トンネルありとなしで公開アドレスを比較し、内部専用ページを取得してください。status 呼び出しは provider がネゴシエーション中でも接続済みと報告することがあるので、リソースの結果に判断を任せ、status は補助的な証拠として扱ってください。

関連記事