NAT を行う VPN ゲートウェイの背後で FTP の一覧表示が止まる理由
NAT がヘッダーだけを書き換えると、ログインと PWD は成功するのに LIST と RETR が停止します。FTP ヘルパーをどこに置くべきか、パッシブモードをどう直すかを解説します。
ログインは成功し、PWD も成功しますが、その後 LIST はクライアントが諦めるか別のデータモードに切り替えるまで止まったままになります。この分かれ方が特徴で、ポート 21 への制御接続は健全なのに、データ接続は相手側が到達できないアドレスやポートから作られています。NAT も行う VPN ゲートウェイの背後では、通常の原因はペイロードに 10.8.0.6 のようなトンネルアドレスを記す FTP コマンドです。ヘッダーの変換はその値に一切触れません。
以下では、その値がどこから来るのか、そしてどの機器がそれを変更してよいのかを説明します。
壊れているのはトンネルではなくペイロードです
FTP はデータの端点を制御ストリームの中に入れます。アクティブモードではクライアントが自分のアドレスとポートを含む PORT を送り、サーバーがその組に呼び返します。パッシブモードではサーバーが組とともに 227 で応答し、クライアントはその代わりにその組へ呼びかけます。
どちらの形式も同じ種類の宣言、つまりこのアドレスにこのポートで接続せよという宣言を含みます。IP と TCP のヘッダーだけを書き換える機器はその宣言をそのまま残すため、一方の端はトンネルの向こう側にしか存在しないアドレスへ到達しようとし続けます。制御接続がすでに健全だと宣言された後で、データ接続が止まります。
三つのモード、三つのペイロード
下の表は、どちら側に呼び出しを命じているのか、そしてヘッダーだけを書き換える NAT がそれに対して何ができるのかを示しています。
最後の列は最初に失敗するものとして読んでください。アクティブモードではサーバーがプライベートアドレスへ呼びかけ、パッシブモードではクライアントが呼びかけ、FTPS ではペイロードが暗号文なので中間機器の手がかりはさらに少なくなります。
| Mode | Value in the control stream | Who dials whom | Header-only NAT leaves |
|---|---|---|---|
| Active (PORT) | client address and port | server dials the client | a server dialing an unreachable tunnel address |
| Passive (227) | server address and port | client dials the server | a client dialing an address it cannot route to |
| FTPS | the same values, inside TLS | as above | ciphertext, so no rewrite happens at all |
ヘルパーは変換が起きる場所に置きます
Linux ではペイロードの書き換えを nf_conntrack_ftp ヘルパーが行います。制御接続を監視し、折衝されたポートを学習し、データフローに対する期待を作り、NAT を行う機器ではペイロード内のアドレスとポートを書き換えます。これが助けになるのは クライアントのアドレスを変換する機器の上だけです。クライアントに読み込ませても、フローを転送するだけのルーターに読み込ませても、セッションは何も変わりません。
次の三つのコマンドは、モジュールがあるか、接続されているか、セッションが開いている間に期待が存在するかを示します:
modprobe nf_conntrack_ftp
lsmod | grep nf_conntrack_ftp
conntrack -L expect | grep ftp
モジュールを読み込むことは、それを接続することと同じではありません。4.7 以降のカーネルは net.netfilter.nf_conntrack_helper=0 を伴って提供されるため、ヘルパーはたとえば iptables -t raw -A PREROUTING -p tcp --dport 21 -j CT --helper ftp のような明示的なルールを待ちます。転送を試みている間に期待の一覧が空のままなら、確認すべきはモジュールではなく接続のほうです。
パッシブモード: サーバーに何を告知するか伝えます
パッシブモードは決定的にしやすく、227 の応答に何を入れるかをサーバーに伝えられます。vsftpd では pasv_address を pasv_min_port と pasv_max_port と組み合わせ、ProFTPD は MasqueradeAddress と PassivePorts を使います。正しく告知されたアドレスがあればその側で何かを書き換える必要がなくなるため、これらを先に設定してください。
次に、そのポート範囲だけをゲートウェイに通し、たとえば -A FORWARD -i tun0 -p tcp --dport 30000:30100 -j ACCEPT として、ルールがサーバーの告知と一致するようにします。
二つの設定は一致していなければなりません。30000-30100 を告知するよう言われたサーバーに対し、ゲートウェイが 40000-40100 を許可していると、制御の会話は健全に見えるのにデータ接続がすべて死ぬという、最も役に立たない形で失敗します。他の何かを調べる前に、数字を比べてください。
二つの故障の兆候と、FTPS が取り除くもの
LIST での停止はセッション最初のデータ接続を指し、そこではアドレスの書き換えと期待の両方が働く必要があります。LIST が成功した後の RETR での停止は転送そのもの、つまりパッシブ範囲かトンネルの MTU を指します。この 二つ目の兆候はペイロードの問題ではなく、ヘルパーの設定では直りません。
TLS 上の FTP は、ヘルパーが読むペイロードを取り除きます。中間機器は復号できない PORT や PASV の値を書き換えられないため、残る選択肢は TLS を終端するプロキシ、限られた範囲で到達可能なアドレスを告知するサーバー、別の転送プロトコルのいずれかです。
短い確認の順序
症状から外側へ進めます。PWD が成功する一方で LIST が失敗することを確かめ、PORT または 227 のペイロード内のアドレスを捕捉し、どの機器がそのアドレスを変換しているかを問い合わせます。そこにヘルパーを接続し、一度の転送の間に期待の一覧を観察し、その後サーバー側でパッシブモードを直し、ゲートウェイのルールに範囲を反映します。
データ経路の故障と一括転送の故障は、行き着く設定が異なるため分けて扱ってください。