誰かが 1723 を転送し、クライアントがサーバーに到達し、認証が始まり、そして何のエラーもなくすべてが止まる。当然の結論はパスワード違いなので、パスワードが三度入力し直される。転送は問題なく、資格情報も問題なかった。壊れていたのは、転送が決してカバーしなかった二つ目のプロトコルと、それへの補助を静かにやめたカーネルの既定値である。

転送は最初から問題ではなかった

ポート転送のルールは TCP または UDP のトラフィックを記述する。セッションの途中で別の IP プロトコルに切り替わるペイロードは記述できず、PPTP はまさにそうする。

だから接続は停止する瞬間まで健全に見える。ポートのルールが管理できる部分は成功し、管理できない部分こそが失敗した部分である。

制御は TCP、データは GRE

呼は TCP 1723 でネゴシエートされる。確立するとペイロードは GRE、IP プロトコル 47 に移り、これはポート番号をまったく運ばない。ポートに依存するルールには照合するものが何もない。

GRE が運ぶのはヘッダー内の呼識別子であり、NAT 変換器はそのフィールドを両方向で一貫して書き換えなければならない。さもなければセッションの両半分が噛み合わなくなる。片方向だけ書き換えると、認証は通るのにすべてのデータパケットを捨てるセッションになり、症状の形はまさにこれである:成功、そして沈黙。

PPTP なしの素の GRE にはそのような書き換えが不要であり、だからこそ同じ経路上の PPTP クライアントで失敗するルーターを、素の GRE トンネルは通過できる。

四つのモジュールと、それらを無効にした sysctl

二つのモジュールがフレーミングを扱い、nf_conntrack_proto_gre と nf_nat_proto_gre である。さらに二つが呼識別子を扱い、nf_conntrack_pptp と nf_nat_pptp である。最後のものをロードすると依存関係で残りも引き込まれるので、モジュール一覧には四つすべてが表示されるはずだ。

古い手順では 2.6 時代の ip_ 接頭辞を使う。これらのコマンドは何もロードしていないのに実行されているように見えることがあるので、終了ステータスではなくロード済みの一覧を確認すること。

ロードされているだけではもう足りない。Linux 4.7 以降、net.netfilter.nf_conntrack_helper sysctl の既定値は 0 であり、新しいフローへのヘルパーの自動割り当てが無効になる。ルーターにすべてのモジュールがあっても何も変換されないことがある。モジュール一覧を再確認する前に、実際の値を確認すること。

自動割り当てを全体で再有効化するか、必要なフローにヘルパーをアタッチして他はそのままにしておく:

iptables -t raw -A PREROUTING -p tcp --dport 1723 -j CT --helper pptp

優先すべきは二つ目の選択肢である。ルーター全体で自動割り当てを再有効化する代わりに、動作を PPTP トラフィックに限定する。

ポートを持たないプロトコルを開ける

1723 を許可するファイアウォールルールは、プロトコル 47 について何も語らない。firewalld ではクライアント側のゾーンでプロトコルを許可する。素の iptables では入力チェーンで gre プロトコルを許可する。

そのうえで両側でキャプチャする。クライアントを出るパケットに応答が返ってこないなら戻り経路がフィルタリングされており、それは資格情報の拒否とは別の障害である。

接続後のサイズ制限

GRE はヘッダーを挿入し、その中の PPP がさらに追加するので、動作するリンクの MTU は 1500 をかなり下回る。小さい応答は読み込め、大きいものは止まり、これは最初の問題が解けた瞬間に現れる無関係な二つ目の問題のように見える。

クライアントの PPP アダプターで MTU を読み、ルーター自身の計算と突き合わせる 何かが動くまで数値を下げるのではなく。フルサイズのパケットが出て何も戻らないなら、それは変換ではなくこの制限を指している。

経路を直せば利用者は作業できるようになるが、PPTP が良い選択になるわけではない。MS-CHAPv2 と RC4 はとうに非推奨なので、モジュールのロードは一時的な修正として扱い、維持されているプロトコルへの移行を計画すること。

関連記事