21
FTP の制御用コネクション。コマンドと 3 桁の応答コードが流れるのはこのポートで、ファイルの中身そのものは、ファイアウォールに誰も教えていない別のポートの上を流れます。
自分の環境の行をそのまま実行してください。ポートを掴んでいるプロセス名と、バインドしているアドレスが出ます。たいていはそれだけで答えが分かります。
sudo lsof -nP -iTCP:21 -sTCP:LISTEN待ち受けソケット 1 つにつき 1 行です。COMMAND がプログラム、PID が実際に手を下す番号、USER が動かしているアカウント、そして NAME にバインドしたアドレスが入ります。`127.0.0.1:21` はループバックのみなので、ファイアウォールを何度いじっても他のマシンからは絶対につながりません。`*:21` なら全インターフェイスです。`-n` はアドレスのホスト名への解決を、`-P` は `21` を `ftp` と表示するのを止めます。NAME の列が読みやすいのはこの 2 つのおかげです。`-sTCP:LISTEN` は確立済みのセッションを隠すので、いま誰が転送中かも見たいなら外してください。何も出ず lsof が終了コード 1 で終われば、そのポートは誰も持っていません。`sudo` なしでは自分のプロセスしか見えないので、root で動いているデーモンはそもそも表示されません。
sudo ss -tlnp 'sport = :21'`-t` で TCP、`-l` で待ち受けソケットだけ、`-n` でポートを数値のまま、`-p` で所有プロセスを足します。`sport = :21` は送信元ポートに対する ss のフィルタ式です。まず Local Address:Port の列を読んでください。`0.0.0.0:21` は全 IPv4 インターフェイス、`127.0.0.1:21` はループバックのみで、リモートのクライアントがいつまで経っても来ない理由はたいていこれです。`[::]:21` は IPv6 のワイルドカードで、多くの Linux では IPv4 も一緒に受けます。所有者は最後の列の `users:(("vsftpd",pid=1102,fd=3))`——引用符の中がプログラム名、その次が PID です。`ss` の入っていないマシンなら net-tools 時代の `sudo netstat -tlnp | grep ':21 '` で、`-p` が pid/program を斜線区切りで出します。パターンの中の末尾の空白は残してください。消すと `:2121` や `:21000` にも当たります。どちらの書き方でも、特権がなければソケットは並ぶのにプロセスの列だけが空になります。「何もいない」ように見えますが、そうではありません。
netstat -ano | findstr ":21"`-a` は接続だけでなく待ち受けポートも表示し、`-n` はアドレスとポートを数値のまま出し、`-o` は最後の列に所有 PID を足します。その行がリスナーである条件は State の列が LISTENING であることで、それ以外はクライアント側のセッションです。Local Address がバインド先で、`127.0.0.1:21` ならループバックのみ、`0.0.0.0:21` なら全インターフェイス。PID から名前を引くのは `tasklist /FI "PID eq 1102"` で、この PID フィルタは eq・ne・gt・lt・ge・le を受け付けます。部分一致に注意してください。`findstr` は行のどこに現れても拾うので、`":21"` は `:2121` にも `:21000` にも、21 を含むエフェメラルな送信元ポートにも当たります。何かを kill する前に、番号を最後まで読んでください。
Get-NetTCPConnection -LocalPort 21 | Select-Object LocalAddress,LocalPort,State,OwningProcessPowerShell 版はポートを部分文字列ではなく数値として一致させるので、`netstat` の隣で打つ価値があります。`State` が `Listen` で `LocalAddress` が `127.0.0.1` なら、それはそのマシン自身にだけ答えて他は全部拒む構成そのものです。`OwningProcess` を `Get-Process -Id` に渡せば実行ファイル名が分かります。Windows では、シェルから自分で起動した何かではなく起動時から動いているサービスであることがほとんどです。結果が空なのに bind が失敗し続けるなら、占有ではなく予約です。`netsh interface ipv4 show excludedportrange protocol=tcp` が Hyper-V・WSL2・Docker Desktop が起動時に確保する帯を一覧します。
`nmap -Pn -p 21 files.example.com` は、ホストの外から見て本当に知りたい一点だけを答えます。`-Pn` を付けるのは、ping を無視するマシンをスキャン対象から外させないためです。答えの意味は nmap 自身の定義で決まります。`open` はそのポートで実際にアプリケーションが接続を受け付けているということ。`closed` は到達できて応答も返ったが、待ち受けているアプリケーションがいないということ。`filtered` はパケットフィルタがプローブをポートまで届かせなかったということで、この場合 nmap には前の 2 つのどちらだったかが分かりません。`--reason` を付ければ何が決め手だったか——closed なら返ってきたリセット、filtered なら無反応——が見えますし、`-sV` を付ければ `220` の挨拶バナーを読んで、たいていはデーモン名とバージョンまで教えてくれます。このポート特有の注意が一つ。このスキャンはデータ用コネクションについては何も語りません。パッシブ転送が使うのはスキャンが触れもしない高位ポートだからです。21 番が `open` なのにダウンロードが 0 バイトで死ぬ、という組み合わせは矛盾なく成立します。スキャンは自分が責任を持つホストにだけ。
| 出ているもの | 意味 |
|---|---|
ftp: connect: Connection refused | パケットはホストまで届き、21 番を持つプロセスがないのでカーネルがリセットを返しました。デーモンが止まっているか、起動に失敗しているか、あなたが叩いたアドレスを含まない場所にバインドしています。上のコマンドは手元のノート PC ではなくサーバー側で実行し、行があることを確認するだけでなくバインドアドレスまで読んでください。 |
The client sits silent for tens of seconds, then reports Connection timed out | 何も返ってきていません。パケットフィルタが SYN を黙って捨てています。クラウドのセキュリティグループ、ホスト上のファイアウォール、あるいは経路上のネットワーク ACL のどれかです。拒否なら即座に返り、破棄なら待たされます。この待ち時間そのものが診断結果です。外から `nmap --reason` で確かめてください。`filtered` が破棄、`closed` が拒否です。 |
425 Can't open data connection | 21 番は問題ありません。ログインできていてサーバーもコマンドを受け付けたからこそ、番号付きの応答が返っているのです。失敗しているのは 2 本目のコネクションのほうです。パッシブモードなら、サーバーが指定した高位ポートへあなたのファイアウォールが出してくれなかったか、サーバー側のパッシブ用の範囲が外から開いていないかのどちらか。アクティブモードなら、サーバーがクライアントへ接続を返そうとして途中で塞がれています。モードを切り替えてどちらが通るか見てください。多くのクライアントは `passive` で切り替わりますし、`curl` は `-P -` を渡さない限りパッシブです。 |
500 Illegal PORT command | サーバーがアクティブモードそのものを拒否しています。折り返しの接続がよく知られた悪用経路なので、今はそうする実装が多いのです。パッシブモードを使ってください。NAT が `PORT` コマンドの中のアドレスを書き換えた結果、サーバーから見て制御用コネクションの相手と一致しないアドレスが指定された場合も同じ見た目になります。これはサーバーが意図的に弾いています。 |
227 Entering Passive Mode reports an address you cannot route to | NAT の内側にいるサーバーに外向きのアドレスを教えていないと、自分のプライベートアドレスをそのまま広告してしまいます。クライアントは律儀に 10.0.0.5 のような相手へデータ用コネクションを開こうとして、そのまま固まります。直すのはサーバー側です。広告するべき外部アドレスを設定するか、ポート番号だけを返して制御用コネクションのアドレスを流用する拡張版の `EPSV` を使ってください。 |
530 Login incorrect | ここにポートの問題は 1 つもありません。制御用コネクションは張れていて、サーバーは資格情報を読んだうえで拒否しています。アカウントかパスワードが違うか、アカウントはあるのに FTP だけ拒まれているかのどちらかです。後者はシェルが `/usr/sbin/nologin` になっている、サーバーの拒否リストに名前が載っている、chroot された仮想ユーザーしか許可しない設定になっている、あたりが定番です。 |
500 OOPS: could not bind listening IPv4 socket / bind: Address already in use | `EADDRINUSE` です。既に誰かがソケットを持っていて、デーモンは動き出してから落ちるのではなく起動の途中で終わります。たいていは別パッケージで入った 2 つ目の FTP サーバーか、21 番をホストに公開しているコンテナです。何かを kill する前に上のコマンドで持ち主を特定してください。1、2 分待ってポートが自然に空くなら、前のプロセスが残した `TIME_WAIT` のソケットだったということで、kill する必要は最初からありませんでした。 |
bind: Permission denied while starting the server on 21 | こちらは本当に特権ポートの話です。Unix 系ではポート 1024 未満に root が要り、21 番はその中に入ります。init システム経由で root として起動するか、Linux ならバイナリに `CAP_NET_BIND_SERVICE` を与えるか、高い番号で待ち受けて転送してください。ただし SELinux や AppArmor のホストでは、ポリシーがそのバイナリの bind を拒否したときも同じ文言が出ます。1024 の規則だと決めつける前に監査ログを見てください。 |
IANA は RFC 959 を根拠に 21 番を `ftp`、つまり File Transfer Protocol の制御用コネクションに割り当てていて、隣の 20 番は `ftp-data` として登録されています。TCP と UDP の両方が登録されていますが、実際に使われるのは TCP だけです。このポートで一番役に立つ知識は、コネクションが 2 本に分かれているという一点に尽きます。セッションは 21 番の上に開き、`USER`、`PASS`、`LIST`、`RETR` と打ち込んだ内容も、サーバーが返す数字の応答も、すべて読める ASCII のままそこを流れます。ただしファイルの中身は 1 バイトも流れません。転送のたびに 2 本目の TCP コネクションが張られ、そのときどちら側から接続しに行くかで、ファイアウォールを通るかどうかが決まります。アクティブモードではクライアントが `PORT`(RFC 2428 なら `EPRT`)を送り、サーバーの側からクライアントへ接続を返します。今どきの NAT とクライアント側のファイアウォールは、まずこれを通しません。パッシブモードではクライアントが `PASV` を送り、サーバーが `227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)` と答えます。末尾の 2 つの数字がポート番号で、実際の値は `p1 * 256 + p2` です。あるいは `229 Entering Extended Passive Mode (|||port|)` が返り、クライアントはそこへ接続しに行きます。今はどれもパッシブが既定なので、FTP サーバーには 21 番だけでなくデータ用ポートの範囲もあわせて開けてやる必要があります。そしてもう一つ、多くの人をこのページに連れてくる名前の罠があります。FTPS と SFTP は同じものの別表記ではありません。FTPS は RFC 4217 が FTP に TLS を足したもので、クライアントが同じ 21 番の上で `AUTH TLS` を送り、そのまま暗号化された状態で続きます。最初から暗号化して始まる古い方式のほうには、IANA が 990 番を `ftps` として別に割り当てています。対して SFTP は、どう解釈しても FTP ではありません。22 番の SSH のサブシステムであって、コマンドも応答コードも 2 本目のコネクションも FTP とは何一つ共有していませんし、開けるべきポートは 22 番ひとつだけです。
平文の FTP をインターネットに出さないでください。パスワードは telnet とまったく同じように 21 番の上を平文で流れますし、公開されたリスナーは数時間で全世界スキャナーに見つかり、そのまま匿名アクセスとベンダーの初期設定を順に試されます。FTP サーバーが他より漏れやすいのは、だいたい次の 2 つの習慣のせいです。1 つのファイルを渡すために有効にした匿名読み取りが、そのまま切られずに残っていること。もう 1 つは、ユーザーごとの chroot をしていないせいで、認証を通ったアカウントが自分のディレクトリの外まで歩けてしまうことです。ただ誰かにファイルを渡したいだけなら、443 番の HTTPS のほうが露出はずっと少なくて済みます。対話的にファイルを触りたいなら、22 番の SSH 越しの SFTP なら開けるポートは 1 つ、しかもホスト鍵の検証つきで、コマンドも中身もまとめて暗号化されます。それでも FTP を残さざるを得ないとき——機器の仕様、測定器、変えてくれない取引先——は、`AUTH TLS` を使う明示的な FTPS を必須にしてください。そのうえで、制御用コネクションを守っても転送そのものは守られないことを頭に入れておく必要があります。データ用コネクションが暗号化されるのはクライアントが `PROT P` まで要求したときだけなので、ログインは TLS で守られているのにファイルは平文で流れている、という状態が普通に成立します。パッシブ用のポート範囲はサーバー側の設定で固定して、ファイアウォールの穴を高位ポート全域ではなく狭い窓にしてください。送信元アドレスでも絞り、NAT が隠してくれていると信じる前に、ルーターが実際に何を転送しているかを確認してください。
どれもブラウザ内で完結します。アップロードは発生しません。
21 を調べているとき、ついでに確認することになりがちなポートです。
ほぼ権限の問題です。lsof や ss はソケット自体は出しますが、他ユーザーのプロセスは所有者の列を伏せるので、システムアカウントやコンテナランタイム、launchd が起動したサーバーは sudo を付け直すまで持ち主不明のリスナーに見えます。Windows は逆で、netstat には何も出ないのに bind だけ失敗します。これは Hyper-V や WSL2、Docker Desktop が起動時に予約したポート帯に当たっている状態です。
何なのかを先に見てください。取り残された開発サーバーなら問題ありませんが、書き込み中のデータベースは別ですし、他が依存しているシステムサービスも同様です。持ち主がコンテナならコンテナごと停止してください。ホストから見えるプロセスを kill してもランタイムが再起動するだけです。どうしても止められないものなら、自分のサービスを別のポートへ移すほうが早く片付きます。
0.0.0.0 ではなく 127.0.0.1 にバインドしているからです。各ページのコマンドがプロセス名と並べてバインド先アドレスを出しているのはこのためで、ループバックにバインドしたものはファイアウォールの設定に関係なく自分のマシンからしか届きません。最近は意図的にそれを既定にしているフレームワークも多いので、たいていはバグではなくフラグの問題です。すでに 0.0.0.0 なのに届かないなら、次に疑うのはホストのファイアウォールかクラウドのセキュリティグループです。
ほとんどなりません。インターネット全体を舐めるスキャナーはポート範囲を丸ごと走査し続けているので、稼げるのは数時間であって安全ではなく、代わりに関係者全員がその番号を覚える羽目になります。既定ポートを狙う無差別スキャンのログが減るという効果は確かにありますが、それだけです。結果を変えるのは認証と、送信元アドレスを絞ったファイアウォールと、そもそも外に出さないことです。
ポート 21 でまだ詰まっていますか。ポート一覧をすべて見る。あるいは上に戻って、実際に出たエラー文から追ってください。