VPNサーバーがパケットからノートPCのアドレスを読み取ってログに書き込む、と想像している。実際に読むのは最後のNATホップが外側ヘッダーに入れた値であり、テザリング中のスマートフォンでは数百人の他の加入者と共有されるアドレスであることが多い。自分のマシンの192.168.x.xがそのヘッダーでインターネットを越えることはなく、トンネルのアドレスは接続が確立した後にサーバーが割り当てる。だからサーバーのログとノートPCのip addrが食い違ってもどちらも間違っておらず、それらは一つのセッションの異なる層を説明している。

三つのアドレス、ログに残るのは一つだけ

混乱は三つのアドレスを一つにまとめてしまうことから生じる。それぞれ所有者が異なり、別のヘッダーに存在し、別の経路でサーバーに届く。

AddressWho assigns itWhere it sitsVisible to the VPN server
Device address, e.g. 192.168.1.24Your router via DHCPOuter header, before translationNo, rewritten in place
Translated address and portRouter, ISP CGNAT, or bothOuter header, after translationYes, this is the tuple it logs
Tunnel address, e.g. 10.8.0.6The VPN server itselfInner header, after decapsulationYes, but it chose it

接続時点で不明なのは中央の行だけである。サーバーはデーモンに応じてピアテーブル、ステータスファイル、フローログにこれを書き込み、WireGuardはエンドポイントと呼び、OpenVPNのステータスファイルはReal Addressと表示し、strongSwanはIKE_SAのリモートホストとして報告する。この値は別の送信元からパケットが届くたびに更新されるため、誰も何も再起動しないまま変化する。

一番下の行こそ、人が身元の主張だと誤読するものだ。これはサーバー設定上の枠を示すもので、人ではなく、サーバーが自分のプールから選んだからだ。二人のユーザーが別の日に同じトンネルアドレスを受け取ることもあり、一人が二台の端末から二つ保持することもある。

トンネルが上がったままログのタプルが変わる理由

アドレス変換はタイマー付きのテーブルであり、永続的な結び付きではない。家庭用ルーターは最後のパケットの後もしばらくUDPマッピングを保持し、キャリアのゲートウェイは独自のスケジュールでポートを再利用し、Wi-FiからLTEへ移動したスマートフォンはまったく別の送信元で戻ってくる。ここではLinuxのconntrackの既定値が有用な目安になり、nf_conntrack_udp_timeoutは30秒、ストリーミング向けの変種は120であり、静かなトンネルはユーザーが気づくよりずっと前にマッピングを失い得る。

トンネルが生き残るのは、プロトコルがアドレスではなくピアを認証するからだ。WireGuardはハンドシェイク後はステートレスなので、認証済みのパケットが届くたびに保存されたエンドポイントが更新され、再バインドは次のパケットで回復し、そのためにpersistent-keepalive = 25が存在してアイドル中もマッピングを温かく保つ。OpenVPNの--floatも同じ役割を果たし、認証済みのクライアントが新しいアドレスとポートから再開できる。floatが既定で無効な2.4および2.5のビルドでは、再バインドがセッションをリセットし、クライアントが再接続する。

IPsecはより多くの可動部品でそこに到達する。IKEv2はNAT検出ペイロード内の送信元と宛先のハッシュを比較して変換を検知し、UDP 4500のカプセル化に切り替え、キープアライブを新しいタプルまで追跡する。キープアライブが遮断されると、SAは書類上は稼働したまま一方方向の通信だけが静かに止まる。

サーバーが紐付けられるもの、できないもの

葬るべき神話は、変換があなたを事業者から隠すというものだ。変わるのはラベルであり、観測ではない。キャリアNATの背後にあるゲートウェイは100.64.0.0/10空間、あるいは多くの加入者が共有する一つのパブリックアドレスを見ており、それでもフローを証明書のサブジェクト、WireGuardの公開鍵、割り当てられたトンネルアドレス、バイトカウンター、時間枠に結び付けられる。そのどれもあなたのLANアドレスを必要としない。

限界は逆方向にもある。ログ上の共有アドレスは家庭ではなくゲートウェイを指し、同じ変換済みアドレスの異なるポートからの二つのセッションは別の端末で、ポートが再利用されれば同じアドレスでも別の日の二人は無関係な加入者であり得る。ピア鍵だけを記録するサーバーはアカウントデータベースなしに顧客へ結び付けられず、アドレスだけを記録するサーバーは同じルーター背後のノートPCとスマートフォンを区別できない。

推測せず両端を比較する

何が起きたかを確定するには、同じ接続を両端で同じ瞬間に取得し、アドレスではなくタプルを比較する。クライアント側では、変換前の状態で:

ss -uapn 'dport = :51820'
ip -4 route get 203.0.113.10
wg show wg0 endpoints

次にルーターまたはCGNATゲートウェイ上の変換エントリと、最終的にサーバーに届く内容を読む:

conntrack -L -p udp --dport 51820
tcpdump -ni eth0 udp port 51820

ほとんどの不一致は三つのパターンで説明できる。サーバー側の送信元ポートがローカルポートと異なり、宛先は一致する場合は通常のポート変換だ。サーバー側の送信元アドレスが自分のものではないISPのブロックに属する場合は、少なくとも一層のNATが多い。アイドル後にパケットが止まり、新しいポートで再開する場合はマッピングの期限切れであり、ルーティング障害ではない。タイムスタンプが一秒から二秒以上離れていれば、別の二回の試行を比較している。

その違いが問題を起こす場所

サーバー障害に見える停止は通常ここから始まる。自宅アドレスに固定したアクセスルールはISPのリースが変わると失敗する。変換済みアドレスに一致するfail2banルールは、そのゲートウェイ背後の全加入者を一度に禁止する。固定のリモートアドレスを持つサイト間ピアは、モバイル回線が変わると鍵の更新を止める。トンネルプロトコルは正常だったが、外側タプルについての前提が誤っていた。

同じ混乱はサーバー側でも起きる。VPNボックス自体がNATの背後にあるならポート転送と機能する転送経路が必要で、strongSwanの転送ガイドがIPsecでのそのケースを扱っている。プライベートアドレスでのみ到達できるサーバーは、クライアントが提示する変換済みタプルを決して見ず、これはクライアント側が変換されるのとは別の問題だ。

次の四つの確認を順に行い、最初に失敗したところで止める:

  • 両端で同時に取得し、送信元ポート、宛先ポート、タイムスタンプが一致することを確認する。
  • サーバーのピア一覧で、期待したアドレスではなく現在保持しているアドレスを読む。
  • conntrackまたはNATテーブルでエントリと残りの寿命を確認する。
  • クライアントのトンネルインターフェースのアドレスが、LANアドレスではなくサーバーが割り当てたものと一致することを確認する。

両方の取得結果がタプルで一致し、それでもトンネルが失敗するなら変換はあなたの問題ではないので、ハンドシェイクとトンネル内のルーティングテーブルへ進む。

関連記事