ルールは VPN サーバーのアドレスを公開サービスへ許可せよと言い、それでも接続は落ちる。この結果はよくあるもので、Docker の不具合ではない。別々の二つの照合が誤っている。書いた送信元はホストが受け取る送信元ではないかもしれず、書いたポートはフィルターが見るポートではないかもしれない。両方を直すには、サービスの図ではなくパケットの経路を見る必要がある。

神話:VPN サーバーを許可すれば完了

ルーティング型 VPN はクライアントのトンネルアドレスを Docker ホストまで保つことができる。送信元変換を行うゲートウェイはそのアドレスを自身のアドレスに置き換えられる。どちらの設計も正当であり、同じ利用者操作に対して異なる送信元を生む。

つまり VPN サーバーの IP という表現は観測ではなく前提を述べている。目の前にあるのがどちらの設計か分かるまで、そのルールは推測にすぎない。

送信元:クライアントプールか変換済みゲートウェイか

四つの点を順に確認する。第一にクライアントのアドレスプール、たとえば 10.90.0.0/24。第二に、ゲートウェイがそのプールをホストへ届く前に変換するかどうか。第三に、新しい接続一本に対してホストが実際に観測する送信元。第四に、アプリケーションの認証が個々の利用者を区別するかどうか。共有の変換アドレスでは区別できない。

プールが保たれる場合、ゲートウェイの公開アドレスだけに一致するルールはすべてのクライアントを取りこぼす。ゲートウェイが意図的に変換する場合、変換後のアドレスを許可すると背後の全利用者を入れてしまう。独自のログインを持つサービスなら許容できるが、それ以外では許容できない。

ポート:DNAT が照合する番号を変える

Docker iptables による通常の Linux ブリッジ公開では、ホストの 8443 からコンテナの 443 番ポートへの対応付けは、転送フィルターが動く前に変換される。パケットは宛先ポート 443 でフィルターに届く。その時点で 8443 に一致するルールは決して発火しない。クライアントが入力したのは 8443 であってもだ。

公開ポートは接続追跡の中に依然として存在する。だから本当に必要なときは、そこに元の宛先を照合できる。

どちらの番号が意図を表すかを決める。ルールがコンテナ内のサービスを守るなら、DNAT 後のコンテナポートに一致させる。ルールがクライアントの要求を記述すべきなら、conntrack オプションで元の宛先に一致させ、それに伴う照合コストの増加とルール順序への注意を受け入れる。

チェーン:INPUT ではなく DOCKER-USER

Docker の iptables ガイドは利用者の転送ポリシーを DOCKER-USER チェーンに置く。このチェーンは Docker 自身の転送ルールより前に評価される。ホストのサービスから INPUT チェーンへコピーしたルールは、ホスト宛ての通信を絞り込み、コンテナへ転送される通信には効かない。この誤りは正しく見えるのにコンテナの経路に何の効果もないルールを生む。

送信元範囲、実効的な宛先ポート、プロトコル、入り口インターフェースを同じルールにまとめて保つ。転送されるすべての宛先に対して送信元だけで許可するのは、公開サービス一つを許可するより広い。

新しい接続で検証する

新しい接続で両方向を試す。許可される VPN クライアント一つと、拒否されたままであるべき送信元一つだ。既存の接続状態はポリシー変更後もセッションを生かし続けるので、古い接続は新しいルールについて何も証明しない。次に 応答経路を確認する。コンテナは依然として自分が見る送信元へ戻る経路を必要とするからだ。

Docker の iptables ガイド は、これらの確認の背後にあるチェーンの順序とポート変換の挙動を記述している。レシピをコピーする前に読み、導入済みの Docker がどのバックエンドを使うかを確認すること。nftables バックエンドはポリシーの統合方法が異なる。

関連記事