フルトンネルの VPN が上がった後もホストは SSH に応答し続けるのに、同じマシン上の Docker 公開サービスは沈黙する。リスナーに問題はない。コンテナは動作し、ポートマッピングは存在し、パケットは届いている。コンテナは健全で、リクエストは実際にそこへ到達している。違うのは、Linux が経路を選ぶときに返信がどこから始まるかであり、その違いが下の二つの設計のどちらを必要とするかを決める。

宛先 NAT を通してタプルを追う

ホスト 192.0.2.10 が TCP 2222 を 172.30.240.5:22 のコンテナへ公開している。リクエストの発信元は 198.51.100.25 で、これはコンテナに届く前に変換され、コンテナは自分自身として返信する。

WAN request:      198.51.100.25 -> 192.0.2.10:2222
after DNAT:       198.51.100.25 -> 172.30.240.5:22
container reply:  172.30.240.5:22 -> 198.51.100.25

ホストの経路判定の時点で、返信はまだコンテナの送信元アドレスを運んでいる。逆変換はあとから公開されたタプルを復元するので、経路が選ばれるまさにその瞬間にパケットは誤って見える。

ホスト専用のルールが見落とす理由

WAN テーブルを 192.0.2.10 からのパケットにだけ選ぶルールは、ホスト自身のデーモンを対象に含めるが、この転送された返信は完全に見落とす。返信の送信元アドレスは 172.30.240.5 であり、ホストではない。

過剰な修正は、コンテナからのすべてを WAN 経由でルーティングすることであり、これではコンテナの無関係な外向き接続まで VPN の対象外になる。それは求められた方針とは別物であり、たいてい後で誰かを驚かせる。

DNAT 後の照合は別のフックである

Docker の ファイアウォールのドキュメント は、通常のユーザーフィルタリングでは DNAT 後の宛先が見える と述べている。したがって通常のルールで公開ポート 2222 を照合することは、コンテナポート 22 を照合することと同じではない。元のタプルが重要なら、どのポートが見えるかを推測せず、サポートされている元の宛先の仕組みを使うこと。

このフックの混同は、正しく見えて決して一致しないルールを生む。ポリシーを書き直す前に、ルール自体のカウンタを確認すること。ヒットがゼロのルールは、パケットが決して届かない場所で評価されていることを告げている。

設計 A:判断を接続状態として運ぶ

接続マークは入口の判断を変換越しに運べるが、重要な経路検索の前にパケットへ戻す必要がある。段階は次のとおり。

  1. Docker、VPN、あるいは別のポリシー所有者と衝突しないマークフィールドとルール優先度を確保する。
  2. 意図した新しい WAN 接続だけを、接続追跡が利用できる DNAT 前の地点で識別する。
  3. 元のリクエスト自体を WAN テーブルへ押し込むことなく、判断を接続マークに記録する。
  4. 返信方向では、ルーティングの前に自分のマークビットだけをパケットへ戻す。
  5. VPN のルールが先に勝てないよう、完全な WAN テーブルを十分早く選ぶ。

そのテーブルには実際の接続済み経路とゲートウェイが必要だ。経路が欠けているときの挙動も定義しておく必要がある。トンネルへ黙って落ちるのが最悪の結果で、設定はまだ動いているように見えてしまう。代わりに閉じた形で失敗し、記録すること。

設計 B:ホストで終端する

コンテナの手前に TCP プロキシやリバースプロキシなど、意図的に設定したホスト側のフロントエンドを置くと、返信はホストのアドレスから出たままになる。するとホスト向けに書かれたルールが期待どおりに動き、コンテナはプライベートネットワークにとどまる。

代償は、独自の設定、送信元アドレスの扱い、アクセス制御を伴う可動部品がもう一つ増えることだ。ふつうはより単純なサポート対象の境界であり、経路ポリシーを去るかもしれない一人で維持するなら、しばしば正解になる。

意図ではなく変更を検証する

公開側から新しい許可済み接続を開き、返信が意図したインターフェースから出ることを確認する。次に、コンテナが開始した無関係なリクエストが依然として VPN を経由することを確認する。

一つのルールを試すためにマシンの接続テーブルをフラッシュしてはいけない。既存のエントリは以前のマッピングを保持しており、フラッシュはホスト上のほかのすべてを壊す。ポートを公開することは到達可能性を定める。返信経路を定めるものではない。選んだ設計をテーブル名とともに書き残し、次の人が障害時に再発見しなくて済むようにすること。

関連記事