アプリベースと宛先ベースのスプリットトンネリングは、同じ作業に対する二つの設定ではありません。前者はどのプロセスがパケットを送っているかを問い、後者はそのパケットがどこへ向かうかを問います。前者のために作られたルールは、トラフィックがヘルパープロセスから来ると静かに失敗し、後者のために作られたルールは、ホスト名が一覧にないアドレスに解決されると失敗します。要件に合う対象を選び、二つのテストパケットで証明してください。一つはトンネルを使うべきもの、もう一つは使ってはいけないものです。

二つのルールは異なる層で一致する

アプリのルールはネットワーク層より上で、OSが送信者に与えるIDに対して適用されます。Androidではパッケージ背後のUIDであり、VPNサービスはUIDごとにトラフィックがトンネルに入るかを決めます。宛先については何も問わないため、選択したアプリは何にでも到達でき、他のアプリは内部ネットワークに到達できません。

宛先のルールはパケット自体に適用されます。トンネルインターフェースは一連のプレフィックスを持ち、ルーティングテーブルが一致するトラフィックをそこへ送り、送信プロセスは無関係です。だからこそ宛先のルールは、誰も一覧に載せるのを忘れたサービスやプリンター、バッチ処理もカバーし、クライアントがプロセスを見ないプラットフォームではこのプログラムだけと表現できないのです。

アプリのルールはモバイルクライアントと管理エージェントに向く

Androidは選択肢を直接公開しています。VpnService.Builder.addAllowedApplication() と addDisallowedApplication() はパッケージ名を受け取り、AndroidのVPN開発者ガイドに記載されているとおりUIDごとに判断を適用します。iOSにもバンドル識別子をキーにしたアプリ単位のVPNルールで同等の仕組みがあり、通常は手入力ではなくMDMプロファイルで配布されます。

デスクトップクライアントが弱点です。WindowsとmacOSのほとんどのVPNクライアントは宛先でルーティングし、プロセス単位の選択機能をまったく提供しません。トンネルドライバーが見るのはパケットであり、プロセスではないからです。ノートPCで一つのアプリだけをトンネルする計画なら、それを前提に設計する前にクライアントの実際の機能を確認してください。宛先リストと二台目のマシンのどちらかを選ぶことになるかもしれません。

見落とされがちな帰結として、これらのプラットフォームではルートでアプリのルールを模倣できません。アプリのサーバーへのホストルートを追加すると、ブラウザーやアップデーターを含む全員のトラフィックがそのサーバーへトンネルされます。

宛先のルールはサブネット一覧とサイト到達性に向く

WireGuardはモデルを明示します。AllowedIPs は暗号鍵ルーティングです。宛先がピアの一覧に入る内部パケットはそのピアへ暗号化され、それ以外は通常の経路を通って暗号化されずに出ていきます。名指しすべきプロセスはなく、何が含まれるかに曖昧さもありません。

OpenVPN はプッシュされたルートテーブルで同じ地点に到達します。route-nopull はサーバーがルートを指示するのを止め、その後クライアント側の route 10.20.0.0 255.255.0.0 vpn_gateway がオフィスのプレフィックスだけを正確にカバーします。IPv6はどのクライアントでも別個の指定であり、それを忘れることが、正しく見えるIPv4スプリットを迂回して漏れる最も一般的な原因です。

ホスト名はルートではありません。プロファイルにドメインフィールドがある場合、クライアントが一度解決する名前として扱い、恒久的なアドレス範囲とは考えないでください。CDNやロードバランサーは明日には別のアドレスを返します。

両モデルが壊れる場所:ヘルパー、共有サービス、リゾルバー

ヘルパープロセスはアプリのルールを壊します。その現れ方はサーバーの障害のように見えます。ブラウザーのアップデーター、クラッシュレポーター、SYSTEMとして動く同期サービス、別パッケージに埋め込まれたWebViewはすべて別々のIDなので、ブラウザーをトンネルしても実際のリクエストはトンネルの外に残ることがあります。逆も同じくらい混乱します。トンネルされたプロセス内の共有ライブラリを呼ぶ除外アプリでも、そのトラフィックはトンネルを通ります。

宛先のルールは共有サービスと名前のところで壊れます。ファイル共有、プリンター、内部DNS、ライセンスサーバーはすべてアドレス上に存在するため、一つのアプリのトンネルはブラウザーにはアクセスを与えても、マップされたドライブは死んだままになります。このカバレッジの差はどちらのモデルの不具合でもありませんが、一つのアプリのトンネルがVPNは上がっているのに共有が見えないという状態を頻繁に生む理由です。

リゾルバーは両方を横切ります。クライアントが内部DNSサーバーを設定しても、OSがパブリックリゾルバーへDNS over HTTPSを使うと、答えはトンネルの外から来て、宛先のルールは決して一致しないかもしれません。ルールが間違っていると結論する前に、どのリゾルバーが実際に答えたかを確認し、クライアント側でトンネルのリゾルバーを導入したのかどうかを確かめてください。DNSをトンネルの外に残すスプリットは、ルーティングテーブルでは完璧に見えても、すべての内部名で失敗します。

手順:選ぶ、適用する、検証する

メニューからではなく要件から始めてください。目標がこのアプリだけがトンネルを使えるなら、アプリのルールとそれを支えるプラットフォームが必要です。目標がこれらのネットワークにトンネル経由で到達できるなら、プレフィックスが必要です。答えが両方なら、二つの独立した制御として実装し、どちらがどの障害を負うかを書き留めてください。

表現できる最も狭い形を適用し、クライアントのステータスアイコンではなくルーティングテーブルに問い合わせて検証します。

ip route get 10.20.30.40
ip -6 route get 2001:db8:10::40
wg show wg0 allowed-ips
Get-NetRoute -DestinationPrefix 10.20.0.0/16
Find-NetRoute -RemoteIPAddress 10.20.30.40

最初の二行は、実際の宛先が使うインターフェースを名指しします。wg show は設定されたプレフィックス一覧を出力し、Windowsでは Find-NetRoute がパケット送信時にスタックが答えるのと同じ問いに答えます。インターフェースがトンネルなら、ルールは一致しています。

次に、除外されるべき二つ目のパケットを、別のアプリケーションから同じ宛先へテストします。クライアントのバージョン、ルールの種類、プレフィックスまたはパッケージの一覧、観察された二つの結果をチケットに記録してください。その結果の組が、ポリシーの設定ミスなのか、そもそもそのトラフィックが対象外だったのかを次のエンジニアに伝えます。

関連記事