FTPパッシブモードで一覧が止まる原因と対策
FTPにログインできるのにディレクトリ一覧で停止する原因を解説します。データ接続に必要なパッシブポート範囲の指定方法と、対応するファイアウォールルールを確認できます。
FTP にはログインできるのにディレクトリ一覧の取得が停止する理由
FTP のパッシブモードは、FTP が1本ではなく2本の TCP 接続を使用するため、ファイアウォールで機能しなくなります。21番ポートへの接続はログインとコマンドを運ぶため、ユーザー名とパスワードは受け付けられ、ファイアウォールの設定も正しいように見えます。最初の ls の後、別のポートで2本目の接続が必要になります。ファイアウォールではその接続が許可されていないため、クライアントはタイムアウトするまで待機します。
この問題を解決するには、データ接続用のポート範囲を固定し、同じ範囲を許可するファイアウォールルールを追加します。NAT (network address translation) 配下のサーバーでは、正しいアドレスを通知するためにもう1つ設定が必要です。以前は、Connection tracking helper がこの処理を代わりに行っていました。現在はそうではありません。古いガイドをそのまま使う前に、その理由を理解しておく必要があります。
制御チャネルとデータチャネル
FTP(ファイル転送プロトコル)は RFC 959 で規定されており、NAT とステートフルファイアウォールが登場する前から存在します。セッションでは TCP ポート 21 への制御接続を 1 本確立し、セッション全体を通じて維持します。コマンドは平文で送信されます。応答は 3 桁のコードとテキスト行で返ります。この接続でファイルの内容を転送することはありません。
データはすべて、個別の TCP 接続で転送されます。ディレクトリ一覧には 1 本(LIST)、各ダウンロードには 1 本(RETR)、各アップロードには 1 本(STOR)を使用します。接続は確立して 1 回だけ使用し、閉じます。認証は完全に制御チャネル上で行われるため、データ経路が壊れている場合は常に同じように見えます。ログインは成功した後、処理が停止します。クライアントが 230 応答を表示した後、一覧取得で停止する場合は、認証情報ではなくデータチャネルを確認してください。
アクティブモード: サーバーがクライアントへ接続を戻す
アクティブモードでは、クライアントがポートを選択して待ち受け、その接続先をサーバーに通知します。
PORT 192,168,1,50,195,80最初の4つの数字はクライアントの IP アドレスです。最後の2つはポート番号を2バイトで表したもので、195 * 256 + 80 = 50000 です。サーバーはその後、自身のポート20からクライアントのポート50000へデータ接続を確立します。
この接続は、クライアントから見ると外部から開始された予期しない受信接続です。そのため、クライアントのファイアウォールによって破棄されます。クライアントが家庭用ルーターの背後にある場合、PORT コマンドに含まれるアドレスはプライベートアドレスです。サーバーからは、そのアドレスへ接続できません。FTP が動作しないという評判を得た原因は、アクティブモードにあります。
パッシブモード: クライアントが両方の接続を開く
パッシブモードでは、データ接続の方向が逆になります。クライアントが PASV を送信すると、サーバーは自身のアドレスとポートを返します。
227 Entering Passive Mode (203,0,113,10,195,80)エンコード方式は同じなので、クライアントは 203.0.113.10 のポート 50000 に接続します。これでクライアントが両方の接続を開くため、パッシブモードはクライアント側の NAT を越えて動作します。現在のクライアントが最初にパッシブモードを要求するのはこのためです。
問題が解消されたのではなく、移動しただけです。今度は、要求していない受信接続がサーバーに到達します。接続先は転送のたびに変わる高位ポートです。そのファイアウォールはあなたが管理するため、問題もあなたの側に残ります。
EPSV (拡張パッシブモード、RFC 2428) は同じ仕組みですが、応答がより簡潔です。
229 Entering Extended Passive Mode (|||50000|)応答にアドレスはありません。クライアントは、制御接続ですでに使用しているアドレスを再利用します。これにより IPv6 経由でも動作し、NAT に関するバグの一種を完全に取り除けます。curl のマニュアルには、curl は通常 EPSV を PASV より先に試すと記載されています。ポートは実行時に選択されるため、EPSV を設定してもファイアウォールのルールは変わりません。
通常のファイアウォールルールでデータチャネルを許可できない理由
ルールを作成する時点では、ポート番号がまだ存在しないためです。サーバーは転送ごとにポートを選択します。デフォルトのままでは、vsftpd の pasv_min_port と pasv_max_port は 0、つまり「任意のポートを使用する」と記載されています。そのため、データ接続は 1023 より大きい任意のポートに到着する可能性があります。sudo ufw allow 21/tcp で許可されるのは制御チャネルだけです。これが、ログインは成功する一方でディレクトリ一覧の取得に失敗する構成を生みます。サービスが 1 つの固定ポートで待ち受けるという考え方がまだ明確でない場合は、Linux でポートと待ち受けソケットが動作する仕組みを参照してください。
ステートフルファイアウォールは接続を追跡できます。カーネルは、既存の接続に関連する新しい接続を RELATED として許可できます。FTP では、そのために制御ストリームを読み取り、227 または PORT の行からポート番号を取り出す処理が必要です。デフォルトでは、その処理を行うものはありません。
FTP 接続追跡ヘルパーが、もはや解決策ではない理由
古いガイドで案内されているのが、カーネルモジュール nf_conntrack_ftp です。このモジュールは平文の制御チャネルを読み取り、通知されたポートを見つけて expectation を登録します。そのため、ポートを指定するルールがなくてもデータ接続が許可されます。しかし、これらのガイドが書かれてから、4 つの点が変わりました。
自動ヘルパー割り当ては無効になっており、現在のカーネルでは再び有効にできません。カーネルは 2016 年に、これをデフォルトで無効にしました。その後、Pablo Neira Ayuso のコミット「netfilter: remove nf_conntrack_helper sysctl and modparam toggles」(2022 年 8 月)によって、この切り替え自体が削除されました。このコミットでは、「ユーザーは明示的なルールセットによる conntrack helper 設定に依存する必要がある」と説明されています。nf_conntrack_helper sysctl は Linux 5.19 に存在しますが、Linux 6.0 以降では削除されています。カーネルの conntrack sysctl ドキュメントにも、現在は記載されていません。モジュールをロードするだけでは何も起こりません。
使用しているカーネルの種類を確認できます。sysctl net.netfilter.nf_conntrack_helper を実行してください。sysctl: cannot stat /proc/sys/net/netfilter/nf_conntrack_helper: No such file or directory という応答は、自動ヘルパー割り当てを有効に戻す機能がカーネルに残っていないことを示します。代わりに数値が返る場合は、Linux 6.0 より前のカーネルを使用しています。この切り替えはまだ存在し、デフォルト値は 0 です。
ファイアウォールのフロントエンドも、この方式を非推奨にしています。Ubuntu 24.04 の man ufw-framework は、/etc/default/ufw 内の IPT_MODULES 行について、「この方法で connection tracking modules(nf_conntrack_*)を無条件にロードすることは非推奨です」と説明しています。また、ヘルパールールは「RULES FILES で管理する必要がある」としています。firewalld は firewalld.conf で AutomaticHelpers を「非推奨。このオプションは無視され、使用されなくなりました」と説明しています。現在ヘルパーを割り当てるには、CT target を使った明示的なルールを手動で記述する必要があります。これは以下の対策より手間がかかり、TLS を有効にした時点で機能しなくなります。Ubuntu の iptables と nftablesでは、これらのルールが実際に配置される場所を説明しています。
TLS を使うと、この方法は成立しません。ヘルパーは制御チャネルをテキストとして読み取って動作します。そのチャネルを暗号化すると、ヘルパーから見えるのは暗号文だけになり、ポートを見つけられません。これを解決する方法はありません。また、解決すべきでもありません。制御チャネルを読み取れる中間装置は、パスワードも読み取れる中間装置だからです。
サーバーでパッシブポート範囲を指定する
すべての FTP サーバーでは、選択した範囲からパッシブポートを選ぶように設定できます。オプション名はサーバーごとに異なるため、実際に使用しているサーバーのドキュメントを確認してください。
vsftpd の場合は、/etc/vsftpd.conf に設定します。
pasv_enable=YES
pasv_min_port=30000
pasv_max_port=30099pasv_enable はデフォルトで YES になっています。2 つのポートオプションのデフォルト値は 0 です。これは前述した「任意のポートを使用する」動作です。sudo systemctl restart vsftpd で設定を適用し、systemctl status vsftpd でサービスが復帰したことを確認します。vsftpd は解析できない設定行を無視せず、起動を拒否します。再起動に失敗した場合は、journalctl -u vsftpd -n 20 を読み、入力したオプションを示す 500 OOPS: 行を確認してください。
ProFTPD の場合は、proftpd.conf に設定します。
PassivePorts 30000 30099ProFTPD は、ここでのデフォルト値を文書化していません。ディレクティブを指定しない場合、ポートは kernel が選択します。ProFTPD のドキュメントには、指定した範囲内に空きポートがない場合、サーバーが kernel の割り当てたポートにフォールバックし、そのことをログに記録すると記載されています。そのため、範囲が狭すぎると明確に失敗せず、ときどき失敗する状態になります。これは診断が非常に困難です。特権ポートではない 1024 以上のポートを使用してください。
Pure-FTPd は -p first:last というフラグを受け取ります。man pure-ftpd では、このフラグを「パッシブモードのダウンロードでは、first から last までの範囲内のポートだけを使用する」と説明しており、「pure-ftpd を packet filter とより互換性のあるものにする」としています。パッケージ版では通常、このフラグを設定ファイルで指定します。ファイル名を推測せず、使用している distribution のドキュメントを確認してください。
必要なポート数はどれくらいでしょうか。使用中のデータ接続 1 本につき 1 ポート必要です。TCP ポートを閉じても、再利用できるようになるまで数分間 TIME_WAIT に残ります。そのため、予想されるピーク時の接続数の数倍を確保してください。数人のユーザーなら 100 ポートあれば余裕があります。公開サーバーで利用者が多い場合は、さらに多く必要です。ここでは vsftpd のほうが ProFTPD より柔軟ではありません。vsftpd の source は範囲内から少数のランダムなポートを試します。すべて使用中の場合は、範囲外のポートを借りずに 500 OOPS: vsf_sysutil_bind でセッションを終了します。どちらのサーバーでも、範囲が狭すぎると最も利用が集中する時間帯に転送が失敗します。
範囲はどこに設定すべきでしょうか。まず sysctl net.ipv4.ip_local_port_range を実行します。標準的な Ubuntu では、32768 60999 と表示されます。これは kernel が送信接続に割り当てるポートです。この範囲内にパッシブポート範囲を設定すると、すでにポートを保持している送信接続と衝突する可能性があります。そのため、この範囲より下に設定してください。デフォルトの環境では 30000 から 30099 までが空いています。表示された値をそのまま信用せず、実際の環境で確認してください。
ファイアウォールで同じ範囲を開く
ufw ではポート範囲をコロンで指定します。マニュアルには、範囲またはリストを「複数のポートの指定にも使用でき、その場合はプロトコルが必要」と記載されています。
sudo ufw allow 21/tcp
sudo ufw allow 30000:30099/tcp
sudo ufw status verboseufw status verbose には、両方のエントリが表示されるはずです。/tcp を付けない同じコマンドは、tcp または udp を指定するよう求めるエラーになって拒否されます。ufw はプロトコルを推測しないためです。VPS での ufw ルール構文で、その他の構文を確認できます。
firewalld ではポート範囲をハイフンで指定し、reload が必要です。
sudo firewall-cmd --permanent --add-service=ftp
sudo firewall-cmd --permanent --add-port=30000-30099/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-all--add-service=ftp は 21/tcp を開き、提供されている service 定義で指定された ftp helper を要求します。パッシブ範囲は開かないため、これだけでは状況は最初と変わりません。VPS での firewalld ゾーンとサービスで、全体像を確認できます。
input chain 内で直接 nftables を使う場合は、次のようにします。
tcp dport { 21, 30000-30099 } acceptもう 1 つ確認すべきファイアウォールがあります。多くのプロバイダーは、オペレーティングシステムの外側にあるコントロールパネルでネットワークファイアウォールを運用しています。サーバー上のルールが正しく、それでもパケットがまったく到達しない場合は、そこで同じ範囲も開いてください。
NAT 配下でサーバーに公開アドレスを通知する
サーバーで ip -4 addr show を実行します。インターフェースのアドレスがクライアントの接続先である場合は、このセクションを飛ばしてください。インターフェースにプライベートアドレス(10.x、172.16 から 172.31.x、192.168.x)が設定され、プラットフォームがそのアドレスに公開アドレスを割り当てている場合、サーバーは自身の公開アドレスを認識できません。vsftpd のドキュメントでは、pasv_address のデフォルトを「アドレスは、接続中の受信ソケットから取得される」と説明しています。そのため、227 の応答にはプライベートアドレスが含まれ、クライアントは到達できない宛先へ送られます。
FileZilla では、この設定を次の名前で表示します。
Server sent passive reply with unroutable address. Using server address instead.FileZilla はこの問題を修正して処理を続行します。ほかの多くのクライアントは修正しません。10.0.0.5 に接続しようとして停止します。
テストに curl を使う場合、curl はこの問題を隠すため注意が必要です。curl のマニュアルでは、--ftp-skip-pasv-ip は「デフォルトで有効(7.74.0 で追加)」と説明されています。そのため、curl は 227 の応答に含まれるアドレスを無視し、制御接続のアドレスを再利用します。curl で動作する転送でも、この理由だけでグラフィカルクライアントでは失敗することがあります。
アドレスを明示的に設定します。vsftpd では pasv_address=203.0.113.10 を指定します。ホスト名を使用する場合は、pasv_addr_resolve=YES(デフォルトは NO)も指定します。ProFTPD では MasqueradeAddress を指定できます。この設定には、アドレス、DNS 名、またはインターフェース名を指定できます。Pure-FTPd では -P を指定します。これは「サーバーがマスカレード(NAT)ボックスの背後にある」場合の設定として記載されています。EPSV では応答にアドレスフィールドがないため、この問題全体を回避できます。ただし、クライアントが送信するコマンドを決めるため、この動作に依存することはできません。
TLS による変更点
FTPS は TLS(transport layer security)上で動作する FTP です。クライアントは通常どおり port 21 に接続し、AUTH TLS を送信して制御チャネルを保護します。続いて PROT P を送信し、データチャネルも暗号化します。プレーン FTP ではパスワードが読み取り可能なテキストとしてネットワーク上を流れるため、FTP を使い続ける必要がある場合は FTPS を使用します。vsftpd は ssl_enable を出荷時の設定として提供し、既定値は NO です。
ここから2つの点が分かります。1つ目は、接続追跡ヘルパーが機能しないことです。これは、前述の点を別の側面から見たものです。2つ目は、sudo tcpdump -nAi any 'tcp port 21' では 227 の応答を確認できなくなることです。サーバーが通知したアドレスとポートを確認する必要がある場合は、ネットワーク上の通信ではなく、サーバー自身のログを確認してください。
外部から変更をテストする
別のマシンから次のコマンドを実行します。サーバー自身からテストすると、修正対象のファイアウォールを経由しません。
sudo ss -ltnp | grep :21
curl -v --disable-epsv --user ftpuser:secret ftp://example.com/
nc -vz example.com 30000ss では、FTP デーモンが port 21 で待ち受けていることを確認できます。サーバーがアイドル状態のときに passive range で待ち受けるプロセスはありません。これらのソケットは転送時に作成され、転送後に閉じられるためです。
--disable-epsv は curl に PASV 経由の接続を強制します。この経路で address の問題が表面化します。trace には、サーバーの応答に続いて、curl が接続する address と port が表示されます。
< 227 Entering Passive Mode (203,0,113,10,117,52)117 * 256 + 52 = 30004 であり、宣言した範囲内です。その行に private address が表示される場合、pasv_address が未設定です。port が指定範囲外の場合、サーバーは設定変更を読み込んでいません。実行中の service が使用している file を編集したか確認してください。
nc だけで firewall の問題に回答できます。即座に Connection refused になる場合、packet はサーバーに到達したものの、待ち受けているものがありません。アイドル状態の passive port ではこれが正しい結果であり、rule は機能しています。nc が諦めるまで接続が停止する場合、何かが packet を黙って破棄しています。これは firewall の問題です。サーバー上の firewall か、provider の panel にある firewall のどちらかです。この違いは SSH で拒否される場合とタイムアウトする場合 で説明したものと同じで、すべての port に適用されます。
FTP を使い続けるべきか
新規の構成では、使うべきではありません。SFTP(SSH file transfer protocol)は、port 22 の単一の SSH 接続内で動作します。OpenSSH がすでに提供しているため、2 本目の channel、パッシブ範囲、NAT の設定、追加で保護する daemon は必要ありません。ファイル転送サービスを設定していないサーバーでも sftp user@example.com は動作します。ファイルだけを相手に提供し、それ以外を許可しない場合は、sshd_config に ForceCommand internal-sftp と ChrootDirectory を指定します。この directory は root が所有し、user が書き込み可能であってはなりません。そうでない場合、sshd はセッションを拒否し、bad ownership or modes for chroot directory 行をログに記録します。
接続先を変更できない場合、FTP を使う理由はまだあります。scanner や multifunction printer には、FTP しか話さない firmware が搭載されています。研究室や産業用の機器では、誰も再認証しない固定 image が動作していることがよくあります。取引先が FTPS の drop を公開し、1 社の supplier のために protocol を追加しない場合もあります。このような場合は、パッシブ範囲と対応する firewall rule を設定するだけで作業は完了します。実行するのは plain FTP ではなく FTPS にしてください。2 channel の設計は 1980 年の判断であり、RFC 959 が引き継ぐ 5 年前にはすでに RFC 765 に記載されていました。現在は、想定されていなかった環境で使われています。この経緯については、ファイル転送プロトコルの歴史で説明しています。
FAQ
FTP ではログインできるのに、ディレクトリ一覧の取得が停止するのはなぜですか?
ログインでは、ファイアウォールが許可している port 21 の制御接続だけを使用します。ディレクトリ一覧の取得には、別の port への 2 本目の TCP 接続が必要で、この接続がブロックされています。FTP server で passive port range を指定し、同じ range を firewall で開放すると、一覧を取得できるようになります。停止する原因は data channel の問題であり、password の問題ではありません。
FTP の passive mode では、どの port を開放する必要がありますか?
制御 channel 用の port 21 と、passive data connection 用に設定した range が必要です。標準の range はありません。使用する range は自分で選択するためです。たとえば 30000 から 30099 のような range が使用できます。最大同時転送数に合わせて大きさを決め、kernel の送信 port range と重ならないようにします。この range は sysctl net.ipv4.ip_local_port_range で確認できます。provider の control panel に network firewall がある場合は、そこでも同じ range を開放します。
それでも nf_conntrack_ftp は必要ですか?
いいえ。現在の kernel では、nf_conntrack_ftp に依存できません。automatic helper assignment はデフォルトで無効になっています。また、Linux 6.0 以降は net.netfilter.nf_conntrack_helper switch が削除されているため、sysctl はファイルが存在しないと報告します。ufw の manual では、これらの module を無条件に読み込む方法は deprecated とされています。firewalld も AutomaticHelpers を完全に無視します。さらに、helper は制御 channel を plain text として読み取る必要があるため、FTPS を有効にすると動作しなくなります。代わりに passive port range を指定してください。
FTP client が、passive reply に到達不能な address が含まれていると表示するのはなぜですか?
server は PASV に、自身の interface で認識している address を返しました。その address は private です。これは、platform が public address を private address に対応付ける場合に発生します。public address を明示的に設定してください。vsftpd では pasv_address、ProFTPD では MasqueradeAddress、Pure-FTPd では -P を設定します。FileZilla は、すでに接続に使用した address を再利用してこの問題を回避し、「Using server address instead」と記録します。そのため、設定に誤りがあっても動作する client と停止する client があります。
FTPS と SFTP のどちらを使用すべきですか?
両端を管理できる場合は SFTP を使用してください。port 22 の SSH 上で 1 本の接続を使用し、開放する data channel がなく、すでに動作しているためです。FTPS は TLS 上の FTP です。そのため、2 channel の設計と、それに伴うすべての firewall 問題が残ります。相手側が他の方式に対応していない場合に選択してください。インターネット経由で plain FTP を使用してはいけません。password が読み取り可能な text として network 上を通過するためです。