FTPパッシブモードで一覧が止まる原因と対策
FTPはログインできるのに directory listing で停止します。データチャネル用の passive port range を固定し、同じ範囲を firewall で許可する設定を解説します。
FTP ではログインできるのにディレクトリ一覧の取得が停止する理由
FTP のパッシブモードは、FTP が 1 本ではなく 2 本の TCP 接続を使用するため、ファイアウォールで問題が発生します。ポート 21 への接続でログインとコマンドを処理するため、ユーザー名とパスワードは受け付けられ、ファイアウォールの設定も正しいように見えます。続いて最初の ls では、別のポートへの 2 本目の接続が必要になります。ファイアウォールでその接続が許可されていないため、クライアントはタイムアウトするまで停止します。
解決するには、データ接続に使用するポート範囲を固定し、同じ範囲を許可するファイアウォールルールを追加します。NAT(network address translation)配下のサーバーでは、正しいアドレスを通知するために、もう 1 つ設定が必要です。以前は、connection tracking helper がこの処理を自動的に行っていました。現在はそうではありません。古いガイドをそのまま使う前に、その理由を理解しておく必要があります。
制御チャネルとデータチャネル
FTP(ファイル転送プロトコル)は RFC 959 で規定されており、NAT とステートフルファイアウォールより前から存在します。セッションでは TCP port 21 への制御接続を 1 本確立し、セッション全体を通して維持します。コマンドは平文で送信されます。応答は 3 桁のコードとテキスト行で返ります。この接続でファイルの内容を転送することはありません。
データごとに専用の TCP 接続が確立されます。ディレクトリ一覧(LIST)に 1 本、ダウンロードごとに 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 です。このモジュールは平文の制御チャネルを読み取り、通知されたポートを見つけて期待値を登録します。そのため、ポートを指定するルールがなくてもデータ接続が許可されます。しかし、これらのガイドが書かれてから、4 つの点が変わりました。
ヘルパーの自動割り当ては無効になっています。カーネルのドキュメントでは、nf_conntrack_helper sysctl を「0 - disabled (default)」と記載し、さらに「無効の場合は、接続にヘルパーを割り当てる iptables ルールを設定する必要がある」と説明しています。モジュールをロードするだけでは何も起こりません。
現在のカーネルでは、この切り替え自体が削除されています。sysctl net.netfilter.nf_conntrack_helper を実行してください。sysctl: cannot stat /proc/sys/net/netfilter/nf_conntrack_helper: No such file or directory という応答は、カーネルに有効化できる自動ヘルパー割り当てが残っていないことを示します。数値が返る場合は、切り替えがまだ存在し、デフォルト値は 0 です。
ファイアウォールのフロントエンドでも、この機能は非推奨になっています。Ubuntu 24.04 の man ufw-framework は、/etc/default/ufw の IPT_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 は、この設定にデフォルト値を定めていません。ディレクティブがない場合は、カーネルがポートを選択します。また、指定した範囲に空きポートがない場合、カーネルが割り当てたポートにフォールバックし、そのことをログに記録するとドキュメントに記載されています。そのため、範囲が狭すぎると明確に失敗せず、時々接続に失敗します。これは原因の特定を難しくします。特権ポートではない 1024 以上のポートを使用してください。
Pure-FTPd では -p first:last フラグを使用します。このフラグは man pure-ftpd で、「パッシブモードのダウンロードでは、first から last までの範囲内のポートだけを最初に使用する」と説明されており、「pure-ftpd をパケットフィルターとより互換性のあるものにする」とされています。パッケージ版では通常、このフラグを設定ファイルで指定します。推測でファイル名を決めず、使用しているディストリビューションのドキュメントでファイル名を確認してください。
必要なポート数はどれくらいでしょうか。稼働中のデータ接続 1 つにつき 1 ポート必要です。閉じた TCP ポートは再利用できるようになるまで数分間 TIME_WAIT に留まるため、予想されるピーク時の接続数の数倍を確保してください。数人のユーザーであれば 100 ポートで十分です。利用者の多い公開サーバーでは、さらに多くのポートが必要です。
範囲はどこに設定すべきでしょうか。まず sysctl net.ipv4.ip_local_port_range を実行してください。標準的な Ubuntu では、32768 60999 と表示されます。これはカーネルが送信接続に割り当てるポート範囲です。この範囲内にパッシブポート範囲を設定すると、すでに送信接続が使用しているポートと競合する可能性があります。そのため、パッシブポート範囲はこの範囲より下に設定してください。デフォルト設定の環境では、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 zone と serviceで、全体像を説明しています。
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 で動作する転送でも、この理由だけで GUI クライアントでは失敗することがあります。
アドレスを明示的に設定します。vsftpd では pasv_address=203.0.113.10 を指定します。ホスト名を使用する場合は、pasv_addr_resolve=YES(デフォルトは NO)も指定します。ProFTPD では MasqueradeAddress を指定します。これはアドレス、DNS 名、またはインターフェース名を受け付けます。Pure-FTPd では -P を指定します。これは「サーバーがマスカレード(NAT)ボックスの背後にある」場合の設定として説明されています。EPSV なら応答にアドレスフィールドがないため、この問題全体を避けられます。ただし、クライアントが送信するコマンドを決めるため、この動作を前提にはできません。
TLS による変更点
FTPS は TLS(トランスポート層セキュリティ)を使用する FTP です。クライアントは通常どおり port 21 に接続し、AUTH TLS を送信して制御チャネルを保護します。その後、PROT P を送信してデータチャネルも暗号化します。通常の FTP ではパスワードが読み取り可能な平文でネットワーク上を流れるため、FTP を使い続ける必要がある場合は FTPS を使用してください。vsftpd では ssl_enable のデフォルト値が NO になっています。
次の2点が発生します。接続追跡ヘルパーは機能しません。これは、上記の点を反対側から見たものです。また、sudo tcpdump -nAi any 'tcp port 21' には 227 の応答が表示されなくなります。サーバーが通知したアドレスと port を確認する必要がある場合は、ネットワーク上の通信ではなく、サーバー自身のログを確認してください。
外部から変更をテストする
別のマシンから、以下を実行します。サーバー自体からテストすると、修正対象のファイアウォールを迂回してしまいます。
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 では何も待ち受けません。これらの socket は転送時に作成され、転送後に閉じられるためです。
--disable-epsv は curl に PASV 経路を強制します。この経路で、アドレスの問題が明らかになります。trace には、サーバーの応答に続いて、curl が接続するアドレスと port が出力されます。
< 227 Entering Passive Mode (203,0,113,10,117,52)117 * 256 + 52 = 30004 で、宣言した範囲内です。その行に private address が表示される場合、pasv_address が未設定です。範囲外の port が表示される場合、サーバーは設定変更を読み込んでいません。実行中のサービスが使用するファイルを編集したか確認してください。
nc だけで、ファイアウォールの問題に答えが出ます。すぐに Connection refused になる場合、パケットはサーバーに到達したものの、待ち受けているものがありません。アイドル状態の passive port ではこれが正しい結果であり、ルールは機能しています。nc が諦めるまで接続が停止する場合、何かがパケットを黙って破棄しています。これはファイアウォールの問題です。サーバー上のファイアウォールか、provider の panel に設定されたファイアウォールのいずれかです。この違いは SSH で refused になる場合と timed out になる場合 で説明したものと同じで、すべての port に当てはまります。
FTPを使い続けるべきか
新しい構成では、使うべきではありません。SFTP(SSH file transfer protocol)は、port 22 の単一の SSH 接続内で動作します。第2のチャネルも、passive range も、NAT 設定も、追加で保護する daemon も必要ありません。OpenSSH がすでに提供しているためです。sftp user@example.com は、file transfer service をまったく設定していないサーバーでも動作します。ファイルだけを相手に提供するには、sshd_config で ForceCommand internal-sftp と ChrootDirectory を指定します。そのディレクトリは root が所有し、ユーザーによる書き込みを許可してはいけません。そうしないと sshd はセッションを拒否し、bad ownership or modes for chroot directory の行をログに記録します。
接続先を変更できない場合は、FTP にも使い道があります。スキャナーや複合機には、FTP だけに対応した firmware が搭載されています。研究機器や産業機器では、誰も再認証しない固定イメージが使われていることがよくあります。取引先が FTPS の受け渡し先を公開しており、1 社の供給元のためにプロトコルを追加しない場合もあります。こうしたケースでは、passive range と対応する firewall rule を設定すれば作業は完了します。使用するのは通常の FTP ではなく、FTPS です。2 チャネル方式は、1985 年の判断を、想定されていなかった現在の環境で使い続けているものです。この経緯については、file transfer protocol の歴史で説明しています。
FAQ
FTP にはログインできるのに、ディレクトリ一覧の取得が停止するのはなぜですか?
ログインでは、ファイアウォールが許可している port 21 の制御接続だけを使用します。ディレクトリ一覧の取得には、別の port への 2 本目の TCP 接続が必要で、この接続がブロックされています。FTP server で passive port の範囲を宣言し、ファイアウォールでも同じ範囲を開放すると、一覧を取得できます。取得が停止する場合、問題はデータチャネルにあり、パスワードが原因ではありません。
FTP の passive mode では、どの port を開放する必要がありますか?
制御チャネル用の port 21 と、passive data 接続用に設定した範囲が必要です。標準の範囲はありません。範囲は自分で選択するためです。たとえば 30000 から 30099 は使用できます。同時転送数のピークに合わせて範囲の大きさを決め、kernel の送信元 port 範囲とは重複させないでください。この範囲は sysctl net.ipv4.ip_local_port_range で確認できます。provider が control panel で network firewall を提供している場合は、そこでも同じ範囲を開放します。
それでも nf_conntrack_ftp は必要ですか?
いいえ。現在の kernel では、これに依存できません。自動 helper 割り当てはデフォルトで無効になっています。最近の kernel では net.netfilter.nf_conntrack_helper switch が削除されているため、sysctl はファイルが存在しないと報告します。ufw の manual では、これらの module を無条件にロードする方法は deprecated とされています。firewalld は AutomaticHelpers を完全に無視します。さらに、helper は制御チャネルを平文として読み取る必要があるため、FTPS を有効にした時点で動作しなくなります。代わりに passive port の範囲を宣言してください。
FTP client が passive reply に routable でない address が含まれていると表示するのはなぜですか?
server は PASV に、自身の interface で認識している address を返しており、その address が private だからです。これは、platform が public address を private address に map すると発生します。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 over FTP であり、2 チャネル構成と、それに伴うすべてのファイアウォール問題を引き継ぎます。相手側が他の方式に対応していない場合に選択してください。インターネット上で plain FTP は使用しないでください。パスワードが読み取り可能なテキストとして network を通過するためです。