ソケットには、どのインターフェースが要求を運んだかを覚えているものは何もありません。そのため、フルトンネルVPNのデフォルトルートが、WAN宛てであるべき応答を捕捉してしまうことがあります。サービスが入力インターフェースを無視しているわけではありません。Linuxはローカルで生成された応答を、宛先と送信元ポリシーに従ってルーティングします。送信元ベースのルールなら、サービスの応答だけを完全なWANテーブル経由で送り、通常の外向き通信はトンネルに残せます。応答が間違った経路から出ていく間もポートは開いたままなので、症状は片側だけに見えます。

誤解:応答は到着したインターフェースをたどる

eth0に到着したパケットがeth0から出ていくのは当然に思えます。ステートレスなパケットフィルタはそう書けますし、受信側のtcpdumpは要求をまさに期待どおりの場所に表示します。しかし、ホスト自身が生成するトラフィックでは、ルーティングはそう動きません。

サービスが接続を受け入れた時点で、その応答は新しくローカルで生成されたパケットです。カーネルは応答の宛先と送信元についてルーティングルールを参照し、VPNのデフォルトルートは完全に有効な答えです。ソケットには、どのインターフェースが要求を運んだかを覚えているものは何もありません。

だから症状は片側だけに見えます。要求は届くのに応答は消えるのです。だからこそ、受信ポートにファイアウォールルールを追加しても何も変わりません。パケットは入る経路で遮断されているのではなく、出る経路で別の場所へルーティングされています。送信元を指定した検索で応答を追えば、その回り道は見えてきます。

カーネルが実際にルーティングするもの:送信元アドレス

必要な手がかりは、サービスが応答に使うローカルアドレスです。eth0の例示アドレスが192.0.2.10/24、ゲートウェイが192.0.2.1のホストなら、そのアドレスはインターフェース上に存在します。クラウドVMではパブリックアドレスが上流で変換され、ゲストには存在しないことがあるため、ホストが実際に持つアドレスを使ってください。

テーブル番号やルールの優先度を予約する前に、現在の状態を確認してください。既存のポリシールールが新しいルールを覆い隠すことがあり、同じ番号の古いテーブルが無関係なルートをすでに持っていることもあります。

ip -4 addr show dev eth0
ip -4 rule show
ip -4 route show table all

次に、送信元でテーブルを選びます。ルールはすでに192.0.2.10を持つ応答に一致し、それはまさに除外したいローカル終端のトラフィックです。転送トラフィックや他のローカルアドレスからのパケットは一致しません。

半分のデフォルトではなく、完全な戻りテーブルを作る

デフォルトルートだけのテーブルでは足りません。カーネルは依然としてゲートウェイを解決する必要があり、その解決には同じテーブル内の接続済みプレフィックスが要ります。そのプレフィックスを欠いたテーブルは、デフォルトルートが正しく見えてもゲートウェイ解決で失敗します。両方を追加し、そのうえで送信元ルールをテーブルに向けてください。

ip -4 route add table 100 192.0.2.0/24 dev eth0 src 192.0.2.10
ip -4 route add table 100 default via 192.0.2.1 dev eth0
ip -4 rule add priority 1000 from 192.0.2.10/32 lookup 100

例をコピーするのではなく、実際のプレフィックスとゲートウェイを使ってください。オンリンクゲートウェイ付きで/32として構成されたクラウドインターフェースには別の接続済みルートが必要で、誤ったプレフィックスから作ったテーブルは最初のホップで失敗します。src属性はそのルートの優先送信元を示すもので、送信元ルールの代わりにはなりません。

順序も重要です。優先度番号が小さい広いルールほど先に評価されるため、このトラフィックをメインテーブルへ戻してしまうルールより前に送信元ルールへ到達する必要があります。優先度は追加後にip rule showで確認してください。起動時だけでは不十分です。

送信元を指定した検索で検証する

カーネルに、サービスのアドレスからテスト用の宛先をルーティングさせます。答えはトンネルではなくeth0とWANゲートウェイを示すはずです。

ip -4 route get 198.51.100.25 from 192.0.2.10

次に新しい認可済み接続を確立し、両方向を観察します。正しいテーブルを選んでいるのに実際の接続がまだ失敗するなら、テーブルではなく逆経路フィルタかファイアウォールルールを疑ってください。何かを全体で無効化する前に、それらの制御を確認してください。

最終的なルートとルールは、インターフェースを所有するネットワークマネージャーの管理下に置いてください。アドレス変更、DHCPの更新、VPNの再接続はいずれもカーネルの状態を書き換えるため、手で追加したルールは次のイベント後に消えるか、古いゲートウェイを指したままになります。

この例外が止まる場所

このルールが免除するのは、一つの送信元アドレスからのローカル終端トラフィックです。Dockerが公開するポート、DNAT、ホスト経由で転送されるサービスは対象外です。それらのパケットは転送トラフィックとしてルーティングされ、ホストのローカル送信元を同じようには持ちません。そうした設計では通常、入力側でマークを付け、そのマークに一致するルールが必要です。

また、ホストのパブリックアドレスがインターフェースに現れるわけでもありません。上流のNATがそのアドレスを持つ場合、応答はそのNATが期待する経路から出なければなりません。だからこそWANテーブルは、推測したパブリック範囲ではなく元のゲートウェイとプレフィックスを反映する必要があります。

例外はテーブル番号、ルール優先度、正確な送信元アドレスとともに記録しておいてください。次にVPNやインターフェースを変更すると静かに壊れることがあり、三つの値を記したメモのほうが顧客報告から症状を再発見するより速く確認できます。

送信元を指定した検索が通った後もサービスが間違った経路で応答するなら、実ソケットの送信元アドレスをルール内のアドレスと比較してください。ワイルドカードアドレスに束縛されたサービスは、テストしたアドレスとは別の送信元を選ぶことがあり、その不一致が残る隙間です。

関連記事