スプリットトンネリングがどの通信を VPN に通すかを決める仕組み
スプリットトンネリングは経路とバインディングに関する一連の判断であり、オンにするモードではありません。アプリごとのルール、プレフィックス、DNS、キルスイッチがどう関わるかを見ていきます。
スプリットトンネリングとは、一連のルールとして表現されるルーティングポリシーであり、クライアントはそれをインストールするだけの存在にすぎない。分離が起きるのは二か所で、各パケットの経路を選ぶ照合ルールと、テーブルが何と言おうとアプリケーションを一つのインターフェースにつなぎ止めるバインディングである。どちらの仕組みにクライアントが依存しているかが、人が報告する驚きの大半を説明する。
一行で言い表せるポリシーは検証できる。より速いブラウジングだと説明されたポリシーは検証できず、要求が誤った方向へ進んだときにどのルールを変えるべきかも教えてくれない。
ルールをインストールするのはクライアント、選択を行うのはカーネル
トンネルが上がると、クライアントは他のインターフェースが使うのと同じテーブルに経路を追加し、多くの場合ポリシールールも加える。すると各パケットは、宛先を候補のプレフィックスすべてと比較して最も具体的な一致を残す検索に到達し、異なるテーブルを指すルール間の優劣は優先度が決める。その検索は、パケットがバックアップ作業ではなくブラウザのタブから来たことを知らない。勝つルールが経路を決めるのであり、変更の前後でそれを読むことができる。
だからスプリットトンネルは恣意的に見えることがある。一つのアプリケーション内の二つの宛先が別の経路を取るのは、片方のプレフィックスがもう片方より狭いというだけの理由かもしれない。
同じ選択はどのプラットフォームにもあり、名前だけが変わる。Windows は判断をフィルタープラットフォームに押し込み、Android は VPN サービスを通じてアプリごとの許可リストを公開し、macOS はルーティングテーブルに従うデバイスにトンネルを結び付ける。 Microsoft のルーティングに関する覚書は Windows の場合に読む価値があり、一つのプラットフォーム向けに書かれたガイドが別のプラットフォームにそのまま移せることはめったにない。
アプリごとのバインディングと宛先プレフィックスは別の道具
宛先ポリシーはパケットがどこへ向かうかを問い、経路テーブルかポリシールールを参照する。アプリごとのポリシーはどのプログラムがソケットを開いたかを問い、より具体的な経路が別を指していても、プラットフォームはそのプログラムをトンネルのインターフェースにつなぎ止める。後者のほうが強いのはテーブルの変更を生き延びるからだが、プラットフォームが帰属を特定できるプロセスしか覆わない。補助プロセス、埋め込みブラウザ、あるいは独自に接続を開くサービスは、想定したバインディングを継承しないことがある。
名前ベースのポリシーに対しては、どちらの手法も助けなしでは生き残れない。大規模サイトのアドレスはコンテンツネットワーク間で回転するので、ホスト名をいくつかのプレフィックスに固定すると、静かに一致しなくなるルールができる。名前を追わなければならないポリシーには、静的経路ではなくリゾルバーを意識した構成要素が要る。
DNS とキルスイッチは独自のルールに従う
名前解決はパケットの経路制御とは別の判断であり、スプリットトンネルは通常二つのリゾルバーを求める:社内名には会社のもの、それ以外にはローカルのものだ。クライアントはインターフェースごとの DNS 設定をインストールしたり、問い合わせを中間リゾルバーに渡したり、システムの既定をそのままにしたりする。独自の暗号化リゾルバーを持つブラウザは三つの取り決めすべてを迂回する。リゾルバーが経路に従ったと決めつけずに、検索を試してほしい。
キルスイッチは上にフィルターを足すが、そのフィルターは意図した除外と偶発的な漏れをいつも区別できるわけではない。トンネルの外をすべて遮断するクライアントもあれば、ポリシーが宣言した除外を許すクライアントもあるので、どちらかに頼る前に自分のものがどちらの振る舞いを実装しているかを読んでほしい。
分離は両側から確認する
含まれる経路と除外される経路を別々に試し、結果は公開アドレスではなくインターフェース名で判断する。これらのコマンドはトンネルデバイスがすでに上がっている Linux ホストを前提とする。203.0.113.10 はポリシーが内側へ通す宛先に、198.51.100.25 は外側へ送る宛先に置き換えてほしい。
ip route get 203.0.113.10
ip route get 198.51.100.25
resolvectl query intranet.example.com
curl -s --interface tun0 https://example.com -o /dev/null -w '%{http_code}\n'
最初の二つの答えは、それぞれに示されるインターフェースがその宛先をトンネルに載せたかどうかを表すため、一緒に読む。リゾルバーへの問い合わせは社内名にどのサーバーが答えたかを示し、最後のコマンドはトンネルデバイスが要求を運べること自体を証明する。一行目が成功しているのに二行目で失敗するなら、ポリシーは命じられたとおりに動いている。
再接続の後とネットワーク変更の後に、同じ組を繰り返す。リースの更新とインターフェースの入れ替わりは、分離が作られた意図と合わなくなる場所であり、各実行の出力を保存しておけばずれがはっきりする。