パケットは OpenVPN のルートに従いながら IPsec ポリシーに捕捉されることがあり、その組み合わせは正しい角度から以外はどこから見てもルーティングの不具合に見えます。Linux はルートを解決したうえで別のセキュリティポリシーデータベースを参照するため、より具体的な宛先ルートが一致する XFRM ポリシーを押しのけることはありません。目に見える症状は、ホストを出たまま到達しないトラフィックで、しかも両方のトンネルは健全だと報告します。解決策は三つあり、どれを選ぶかは二つの経路のうちどちらがそのプレフィックスを運ぶべきかで決まります。

決断は二つ、一つではない

ルート選択は狭い問いに答えます。どの次ホップと送信元アドレスがこのパケットを運ぶべきかという問いです。セキュリティポリシーデータベースは別の問いに答えます。このパケットに IPsec 保護が必要か、必要ならどのセレクターの下かという問いです。どちらも同じパケットに対して動き、前者を満たしても後者については何も言えない。だからメトリックを変えたり静的ルートを足したりしても症状はそのままなのです。

広い IPsec セレクターがよくある原因です。172.16.0.0/12 を保護するポリシーは 172.20.8.0/24 も覆ってしまい、管理者はむしろ OpenVPN トンネル経由でそこへ届くつもりかもしれません。その意図はルーティングテーブルにしか存在せず、ポリシーデータベースはそれを知りません。

これは二つのメトリックの争いではなく、設定された二つの保護経路の衝突として扱ってください。修正は、誤った範囲で設定された側のコンポーネントに入ります。

カーネルが実際に選ぶルートを読む

カーネルが行う検索から始めます。送信元アドレスと、別のテーブルへ迂回させうるポリシールールも含めます。メインテーブルのルートは、先行するルールが別のテーブルを選べば参照されないことがあり、最長プレフィックス一致はルールが選んだテーブルの中だけで働きます。

ip rule show
ip route show table all
ip route get 172.20.8.20 from 10.44.7.15

肝心なのは最後のコマンドです。選ばれたルートと、カーネルが使う送信元アドレスを一緒に出力します。その出力と IPsec ポリシーが一致するセレクターを比べてください。ルートが OpenVPN インターフェースを指し、ポリシーが同じ組に一致しているなら、当て推量なしで衝突を再現できたことになります。

チケットに載せる前に、ダンプから無関係なアドレスを除いておきます。本番ホストのルーティングテーブルは内部トポロジーを含むので、問いに答える行だけを貼る価値があります。

同時に適用されるポリシーも読む

ポリシーの一覧は方向、セレクター、そして一致したパケットをどう扱うかを決めるテンプレートを示します。まず送信方向を読み、次に戻りトラフィックのために受信側を読みます。片方向だけの除外は、始まって完了しない接続を生むからです。

ip -s xfrm policy
swanctl --list-sas

一覧のセレクターを、接続が折衝したプレフィックスと比べます。strongSwan のルート自動投入を切ってもこれらのポリシーは消えず、メトリックを変えてもどの送信元と宛先の組をポリシーが保護するかは変わりません。この二つの空振りは早めに除外する価値があります。人がまず試すものだからです。

一覧に期待したプレフィックスのポリシーがなければ、問題は別の場所にあり、ルーティングテーブルはおそらく正しいことを言っています。そのプレフィックスを覆うより広いポリシーがあれば、捕捉の原因が見つかったことになります。

両方の経路が正当な場合の三つの選択肢

広いプレフィックスが要件ではなく便宜だったなら、IPsec セレクターを絞るのが最もきれいな選択肢です。リモートトラフィックセレクターを本当に IPsec の背後にあるサブネットまで狭めれば、すべてのホストの衝突が一度に消え、費用は新しいコンポーネントではなく鍵の再折衝で済みます。

広いセレクターを残さねばならない場合、すり抜けの除外が外科的な選択肢です。strongSwan は子が pass モードを使う別の接続をサポートし、一致したトラフィックを IPsec で処理してはならないとカーネルに伝えます。ループバックのリモートアドレスは、その接続が受信側ピアに選ばれるのを防ぎ、セレクターはすべてではなく実際に許可された組を記述しなければなりません。

connections {
    openvpn-exception {
        remote_addrs = 127.0.0.1
        children {
            openvpn-exception {
                local_ts = 10.44.7.0/24
                remote_ts = 172.20.8.0/24
                mode = pass
                start_action = trap
            }
        }
    }
}

strongSwan の設定例 はこのポリシー種別とループバックピアの慣例を説明しています。三つ目の選択肢は XFRM インターフェースによるルートベース設計 で、グローバルなセレクターではなく名前付きインターフェースを通るトラフィックにポリシーを結びつけます。これは調整ではなく移行であり、トラフィックは依然として折衝済みのセレクターを満たす必要があります。

OptionUse it whenCost
Narrow the selectorThe broad prefix was convenienceA rekey and a policy review
Passthrough childThe broad selector must stayOne exception per intended pair
XFRM interfacePolicy should follow routingA design migration

残りを弱めずに除外を証明する

変更を読み込んだら、除外と、保護されたままであるべき通常の宛先を試します。両方向を確認し、次に OpenVPN トンネルを落として除外したトラフィックがどうなるかを確かめます。トンネルが落ちているときにそのプレフィックスを黙って平文で送る除外は、事故の最中に発見するものではなく、意図して決めるべきポリシー判断です。

どのプレフィックスがどの経路を使い、なぜそうするのかを記録します。さもなければ、広いセレクターを見た次のエンジニアが同じ調査をやり直し、答えは誰も読まない commit メッセージに置かれたままになります。

関連記事