フルトンネルを有効にすると、トンネルを終端するマシンを含め、すべての宛先が VPN インターフェイスに属します。トンネルを運ぶ外側のパケットはそのマシン宛てなので、経路は自分が依存する転送そのものを消費してしまいます。具体的な経路を一つ追加すれば解決し、その理由は理解する価値があります。理由を押さえないと、次に作り直したときに同じ障害が戻ります。

自分の転送を食べてしまう経路

カプセル化は各パケットを二つ目のヘッダーで包みます。外側のヘッダーはローカルの物理アドレスからサーバーのパブリックアドレスへ向かい、トンネルが存在する前に物理リンクを越えなければなりません。

ここで、そのパブリックアドレスの検索にトンネルを選ばせてみます。パケットを送るにはマシンがカプセル化する必要があり、そのためには同じアドレスへの経路が要り、その経路がトンネルを返します。クライアントの扱いは二通りです。慎重なものはデフォルトルートの設定を拒否してエラーを報告し、慎重でないものは成功を報告してから何も動かしません。静かな失敗こそが半日を奪います。

最長プレフィックス一致はメトリックを見る前に検索を決めるので、単一アドレスの経路はマシン上のどのデフォルトルートよりも優先されます。修正の仕組みはこれだけです。

デフォルトより先に例外を入れる

デフォルトルートが動く前に、物理ゲートウェイを次のホップとする単一アドレスの経路をサーバー用に作ります。順序が重要で、後から入れると入力している間ずっと壊れたトンネルで過ごすことになります。

ほとんどのクライアントは頼まれなくてもこれをします。OpenVPNはリモートアドレスを引いてデフォルトゲートウェイを書き換える前に固定し、route-delay オプションはその順序が競合するプラットフォーム向けに存在します。wg-quick はまったく別の道を選び、パケットに印を付けてポリシールールを参照することで例外を足す代わりにしています。仕組みは違っても要件は同じで、外側のパケットは自分が作るトンネルから逃げなければなりません。

ip route add 203.0.113.10/32 via 192.168.1.1 dev eth0
ip route get 203.0.113.10

検索の出力が受け入れテストです。物理デバイスと物理的な次のホップを名指ししていなければなりません。その答えにトンネルデバイスが現れるなら、例外が欠けているか、それを上書きする何かの後ろに置かれています。

サーバー側の鏡像

同じ罠が反対側でも逆向きに待っています。サーバーに別のトンネル経由のデフォルトルートを与えるか、クライアントのパブリックアドレスを覆う経路を押し込むと、その応答は逆方向へ向かう癖を持ちます。

接続してリクエストを送るクライアントを想像します。応答はそのクライアントのパブリックアドレス宛てで、それが今はトンネル経路に一致するため、クライアントが一度もダイヤルしていない送信元を載せて別のインターフェイスから出ていきます。クライアントはそのパケットを受け取っても、送ったリクエストと結び付くものがないため破棄できます。実際の選択を示すのは片方だけなので、クライアントのタイムアウトではなくサーバーのルーティング判断から迫ってください。

ip route get 198.51.100.44 from 10.90.0.1
ip rule show
sudo tcpdump -ni any host 198.51.100.44

修正は三つあり、等価ではありません。サービスを物理アドレスに固定することと送信元を変換することは、どちらもルーティング判断を正すのではなく隠します。トンネルプールの応答トラフィックを専用テーブル経由で送ることは原因を直し、残り二つはルーティング設計が凍結されている間だけ許容されます。

トンネルが奪い続ける例外

トンネルがデフォルトルートを握っていても、開いたインターネットを必要とする作業がいくつかあり、それぞれに専用の例外が要ります。動的 DNS の更新プログラムがたいてい最初に壊れます。

プロバイダーを解決し、接続を開き、自分のパケットが数秒前にアドレスが変わったトンネルを通っていることに気づくので、更新は失敗するか間違ったアドレスを公開します。出口は三つあり、プロバイダーを物理ゲートウェイ経由のホストルートで固定する、トンネルが上がる前に更新を走らせる、更新プログラムにトンネルが触れないポリシーテーブルを与える、のいずれかです。時刻同期もリゾルバーなしで起動する機器では同じ露出を持ち、証明書検証のエンドポイントも同じ理由で一覧に入ります。

誰も書き留めなかった例外は、次の作り直しが黙って落とす例外です。

再接続後に証明するのであって、その前にするのではありません

一つの宛先ではなく、エンドポイント、トンネルの向こう側のプライベートアドレス、その先のパブリックアドレスです。成功した検索が一つあっても、残り二つについてはほとんど何も言えません。

ip route get 203.0.113.10
traceroute -n 203.0.113.10
ip route get 10.90.0.5
ip route get 1.1.1.1

再接続時にテーブルを作り直すクライアントは手で打った経路を捨てるので、そのうえで接続を張り直します。kill switch が有効な場合は、ネットワーク変更後にトンネルが戻らず症状が障害のように見えるので、エンドポイントが許可されているか確認してください。例外はシェル履歴ではなくプロファイルや unit ファイルに保存し、再接続がそれを消すのではなく作り直すようにします。

関連記事