nftables が OpenVPN のクライアント間通信を認識できない理由
ピア間の通信は VPN プロセス内部で配送されるため、フォワードのカウンタは決して動かない。計測するにはカーネル経路とプロセス経路のどちらかを選ぶ必要がある。
同じサーバー上の二つのクライアントがファイルを交換し、転送は成功するのに、トンネルに向けたフォワードルールはすべてゼロパケットを報告する。ルールが壊れているわけでも、チェーンが誤った位置にあるわけでもない。ピア間フォワードが有効な場合、パケットはサーバープロセス内部で配送され、ホストスタックがルーターになることはないため、カーネルのファイアウォールが数える対象が存在しない。この分岐を理解しているかどうかが、近道を残すかカーネル経由で経路を作り直すかを決める。
サーバーを通る一つのパケットを追う
ルーティングされたトンネルでは、デーモンが各クライアントの復号済みパケットをトンネルデバイスに渡す。そこからホストが経路選択し、フォワードフックが走り、トンネルデバイスに一致するルールが通信を数えたり破棄したりできる。これが多くのファイアウォール解説が前提とする経路である。
| Forwarding path | Who decides | What the host firewall sees |
|---|---|---|
| Kernel routing between clients | Host routing table and forward rules | Every inner packet, with interface names |
| Peer forwarding inside the server process | The VPN daemon's own peer table | Only the outer encrypted transport |
近道はホストを中継から外す。あるセッションの復号済みパケットはデーモン内部で別のセッションへ渡され、内側のパケットがトンネルデバイスに出力されることも、ホストに経路選択されることもない。カプセル化された通信は物理インターフェースを通過するため、外側のキャプチャは忙しく見える一方で内側のルールは沈黙したままになる。トンネルデバイスで取得したキャプチャには何も現れず、そこで転送を観察できると期待した人は驚く。この非対称性が、デーモンの外から近道を見分ける最短の手がかりである。
どちらの経路を通ったかを決めるカウンタ試験
適用されるべきルールにカウンタを取り付け、そのルールが遮断すべき二つのクライアント間で転送を実行し、あとでカウンタを読む。
sudo nft list ruleset
sudo nft add rule inet filter forward iifname "tun0" oifname "tun0" counter accept
sudo nft list chain inet filter forward
転送が成功してカウンタがゼロのままなら、カーネルはそのパケットを一度も扱っておらず、ルールをどう編集しても変わらない。同じ試験をサーバー自身のサービスに対して繰り返すと、そのフローは実際にホストスタックを通るためカウンタが動く。二つの結果を比べることで答えが出る。
誤読を防ぐ詳細が二つある。チェーンのラベルに書かれたインターフェース名はそれ自体では何も絞り込まない。一致はルール内部のインターフェース項目で判定されるからである。またブリッジ構成のレイヤー2設計では挙動がさらに変わる。そこではクライアントが同じブロードキャストドメインを共有するため、ピアの発見は想定内であり、ルーティングの不具合ではない。
スイッチはアクセスリストではない
ここが半日を費やす部分である。ピア間フォワードはオンかオフかの一つの設定であり、オンの間はデーモンが唯一のフォワード点になる。ホストのファイアウォールは誰が誰に到達できるかを表現できず、クライアントごとのファイルもスイッチをポリシーのマトリクスには変えない。
ピア単位のルールが必要なら、近道を諦めるか、制御手段としてのホストのファイアウォールを諦めるかの選択になる。無効にすればカーネルのフォワードが戻るが、サーバー側で IP フォワードを有効にし、望むフローに対する明示的なルールが必要になる。有効のままにすればポリシーはデーモンの設定に住むことになり、実際には全ピアかゼロピアのどちらかになる。
独自のアドレスプールとポートを持つ別々のサーバープロセスが、現実的な中間案である。各プロセスはアクセス判断をちょうど一つだけ持つため、二つのグループがポリシーのマトリクスなしに異なるルールを持てる。意図する場合にはホストがプール間を経路選択でき、そのときファイアウォールはホストを横切るこれらのフローを見る。代償は追加のポート、追加の証明書、グループごとのプロファイルである。
フォワード点の選択
ファイアウォールがポリシーのエンジンである場合、フロー単位の記録が必要な場合、あるいは会計が重要な場合はカーネルのフォワードを選ぶ。代償はホストスタックを通るスループットと、保守すべきルールの増加である。利点は、カウンタからフローエクスポーターまで、既存のあらゆる道具が再び通信を記述できることだ。小さいサーバーではこの代償は見えないが、多忙なリレーではそうはいかない。
ピア間通信がすべて許可かすべて拒否のどちらかで、サーバーがそれに見合う規模ならプロセス内フォワードを選ぶ。代償は、カウンタ、キューの規律、コネクション追跡が内部フローを記述しなくなることであり、後で誰もそれらに依存しない場合にのみ受け入れられる。
どちらを選ぶにせよ、ファイアウォールのルールの隣に書き残す。ピア間通信を許可しているように見えるのに実際はデーモンが決めているルール群は、失敗した転送を調べる次の人を誤らせる。ルールだけを読む人はカーネルが関門だと思い込む。
ルールを変える前に確認すること
サーバーが実際に読み込んだ設定を読み、編集した記憶のあるファイルを読まないこと。ピアのオプションが存在するかを確認する。変更したなら、ディスク上のファイルを書き換えただけでなく、サービスが再読み込みされたことを確かめる。
次にクライアントからサーバーへのフローで同じ試験を繰り返し、ルールがそもそも一致することを示す。プッシュされたルートも確認する。ルートの欠落はクライアント側から見るとファイアウォールの遮断とまったく同じに見えるからだ。ルールの順序を変えるのは、どのフォワード点が判断を持っているかを把握してからにする。