クライアントは認証し、PPP はアドレスを交渉し、それから何も動かない。ping は失敗し、ブラウザも失敗し、コンテナはずっと接続済みに見える。このパターンはルーティングテーブルの問題ではない、なぜならルーティングの問題は通常、制御接続も壊すからだ。これはペイロードの経路であり、PPTP ではペイロードが今成功したプロトコルとは別のプロトコルである。

最も時間を節約できる順に五つの確認。

確認 1: 制御チャネルとペイロードを分ける

PPTP は TCP 1723 で認証し、その後 PPP ペイロードを GRE、IP プロトコル 47 の内部で運ぶ。GRE は TCP でも UDP でもないため、制御ポートを転送する規則は GRE について何も言っていない。1723 を通す NAT やファイアウォールは、苦情もなく 47 プロトコルを落とすことがあり、セッションは成功のように見える形で半分だけ構築されたまま残る。

ログインが通り、パケットが戻ってこないなら、まず GRE を疑い、次にそれを証明する。

確認 2: ホストカーネルのヘルパーを確かめる

接続追跡はヘルパーなしでは TCP 制御接続を GRE フローに結び付けられない。モジュールは nf_conntrack_pptp で、ホストカーネルにあり、コンテナイメージ内で読み込んでも何も得られない、モジュールはホスト全体に効くからだ。Docker ホストで lsmod | grep nf_conntrack_pptp を実行するのが確認である。なければ modprobe nf_conntrack_pptp が実行中のカーネルに読み込むが、次の起動については何も言わないので、ホストの設定にも追加する。

再起動で消えるモジュールは、来月も同じチケットになる。

確認 3: それを無効にするカーネルポリシーを読む

モジュールがあっても、4.7 以降のカーネルは net.netfilter.nf_conntrack_helper を既定で 0 にするため、すべてのプロトコルでヘルパーの自動割り当てが無効になる。この既定値のせいで、正しく導入したモジュールでも何も起きない:コードはそこにあり、ポリシーが使うなと言っている。sysctl net.netfilter.nf_conntrack_helper は現在値を報告し、実行時に 1 にするとヘルパーが再び利用可能になる。設計が許すなら、ホストの sysctl 設定で値を永続化する。

自動割り当てはトラフィックに暗黙の期待を作らせる、だからこそ既定値は変更された。信頼できるホストに限定するか、ヘルパーを対象とする明示的な規則を使う。

確認 4: コンテナ側の経路を別に見る

読み込まれたヘルパーは経路を作らない。コンテナには PPTP サーバーへの経路が必要で、ホストの転送は両方向の GRE を許可しなければならない:iptables -L FORWARD -v -n は 47 プロトコルが受け入れられているか、カウンターが動いているかを示す。Docker の masquerade 規則は TCP と UDP を変換し、47 プロトコルは変換しないので、ブリッジ接続のコンテナには明示的な forward 許可と、ホストによっては独自の変換規則が必要になることがある。コンテナがイメージの想定する PPP デバイス対応を持っていることも確認する。

規則の文言ではなくカウンターを読む。存在しても何にも一致しない規則は飾りである。

確認 5: GRE が追跡されていることを証明し、それから PPTP を判断する

クライアントを動かしたまま追跡テーブルを読む。PPTP 制御エントリが確立し、関連する GRE エントリがあれば、ヘルパーがフローを結び付けており、残る不具合は別の場所にある。GRE エントリのない制御エントリは、ペイロードが破棄されるか追跡されていないことを意味し、確認 2 と 3 に戻ることになる。

PPTP 自体が旧式で、MS-CHAPv2 は弱い。サーバーが L2TP/IPsec、IKEv2、WireGuard も提供するなら、いずれかに移ればこの種の問題は丸ごと消える。

PPTP を残さざるを得ない場合、クライアントをコンテナ内ではなくホストで動かすことで NAT とフィルタリングの層が一つ減り、多くの場合そちらの方が安い判断になる。

関連記事