クライアントは接続済みと言い、錠前は閉じているのに、サイトは依然としてあなたの住居のアドレスを記録している。何も失敗していない。トンネルはWi-Fiやモバイル回線を置き換えるものではない。特定のパケットの周りに第二の封筒をかぶせ、その封筒を既存のネットワーク越しに送り出す。仕組みを理解するには、一つのリクエストを追い、封筒がどこで開かれるかに注目するのがほとんどだ。自宅アドレスの漏えいから大きなダウンロードの停止まで、紛らわしい症状のほとんどはその境界から生まれる。

二つの封筒、一本の物理リンク

ノートPCはルーターへの通常の接続を保ち、ルーターはプロバイダーへの通常の接続を保つ。トンネルが立ち上がっても、その経路の何も変わらない。変わるのは、選ばれたパケットが仮想インターフェースに渡され、VPNピア宛ての新しいパケットの中に包まれ、物理リンク上の通常のトラフィックとして送られることだ。

受け取る側は封筒を開き、元のパケットを宛先へ向けて転送する装置だ。両端の間で、観察者には外側のヘッダーが見え、内側については何も見えない。ただしプロトコルが暗号化する場合は別で、ほとんどの消費者向けプロトコルはそうしており、一般的な手法はそれを必須としない。

だからこそ仮想インターフェースは現実でもあり誤解でもある。それはアドレスと最大伝送単位を持つカーネルオブジェクトとして存在し、OSは他のインターフェースと同じようにそこへトラフィックを流す。ケーブルではなく、その存在が証明するのは部品が導入されていることだけだ。ブラウザーがそれを使うかどうかはルートが決めるのであり、インターフェースが起動しているかではない。

一つのリクエストの旅を眺める

宛先を選び、カーネルにどのインターフェースを使うか尋ねると、この一つのコマンドで、トンネルが何かをしているかどうかをめぐる議論のほとんどが片づく。

ip route get 203.0.113.7
ip -brief addr show
ip -4 addr show dev wg0

ルートの出力がトンネルのインターフェースを挙げていれば、パケットは包まれる。物理インターフェースを挙げていれば包まれず、クライアントを何度再起動してもそれは変わらない。二つ目のコマンドは両方のインターフェースを並べて表示し、アドレスなし、あるいは誤ったファミリーのアドレスで立ち上がったトンネルを見つける方法がこれだ。

次に、封筒の内側から最初のホップを見て、外側の最初のホップと比べる。

traceroute -n -m 3 203.0.113.7
sudo tcpdump -ni wg0 -c 5

トンネルの内側では、最初のホップはピアの内部アドレスであり、あなたのルーターではない。トンネルインターフェースでのキャプチャーは内側のパケット、つまりアプリケーションが送っているつもりのトラフィックを示す。同時に物理インターフェースでキャプチャーすれば外側のパケットが示され、両者を並べることがトンネルの実際の働きの最も明快な実演になる。

三つのアドレス、サイトが見るのは一つだけ

人はこれを絶えず混同し、その混同が誤った漏えい報告を生む。別々のヘッダーにある別々のフィールドで、寿命も異なる。

AddressWhere it appearsWho can see it
Endpoint addressOuter header, the peer you connect toYour access network and the path to it
Inner tunnel addressInside the envelope, on the virtual interfaceThe peer, and anything the peer routes to
Exit addressSource of the packet that reaches the websiteThe website and its upstream

仮想アダプターの横の数字は二行目だ。サイトが記録するアドレスではなく、それを自分の公開IPとして引くのは単純に誤りである。サイトが見るアドレスは出口が割り当てるもので、プライベートネットワークへのトンネルでは公開の出口がまったくないこともある。宛先がインターネット上ではなく組織の内部にあるからだ。

拠点間トンネルは端点が違うだけで同じ仕組みだ。二台のルーターがネットワーク間のトラフィックを包み、個々の機器にクライアントは導入されていない。封筒は原理的に同一で、管理上の境界はそうではない。

封筒が開かれる場所と、そこを生き延びるもの

トンネルはそのピアで終わる。アプリケーション自身の暗号化がさらに先まで及ぶなら、それはその境界を越えて生き延びる。HTTPSではこれが普通で、ブラウザーのセッションはサイトで終わるため、出口は読めない暗号文を転送する。

それ以外は既定では生き延びない。暗号化されていないリクエストは、カフェのWi-Fiで読めたのとまったく同じように出口で読めるし、トンネルはアプリケーションが決して持たなかった保護を加えない。同様に、出口のファイアウォール規則と転送ポリシーが次に何が起きるかを決める。ハンドシェイクの成功はピア接続についての事実であり、旅の残りについてではない。

オーバーヘッドも目に見える結果だ。各パケットは余分なヘッダーを運び、外側のトランスポートが自前のヘッダーを加えることもあるので、パケットあたりの使えるペイロードは小さくなる。最大伝送単位が固定された経路で、トンネルが ping を通し、大きなアップロードを止めるのはまさにこのためだ。

アプリケーションが実際にたどった経路を確認する

最後の手順は、機械全体ではなく特定のアプリケーションを確認することであり、宛先を解決し、そのアドレスのルートを見て、開いている間に接続を観察する。

getent hosts example.com
ip route get "$(getent hosts example.com | awk '{print $1; exit}')"
ss -tnp state established '( dport = :443 )'

ルートがトンネルを挙げているのに、ソケット一覧が物理インターフェースの送信元アドレスを持つ接続を示すなら、アプリケーションは何か特定のものに縛られているか、二つ目のトンネルが既定ルートを争っている。これはルーティングの問題であり、クライアントの状態画面ではなく、この三つの出力から答えられる。

RFC 4301 のセキュリティアーキテクチャは、IPsec におけるこの包み方の IP 層版を説明しており、同じ階層の論理は WireGuard と OpenVPN にも当てはまる。内側のパケットと外側のパケットを別々の対象として指させるようになれば、トンネルの振る舞いの大半は謎ではなくなり、読めるルーティングテーブルになる。

関連記事