FTP サーバーを使う前に
FTP(RFC 959)は制御用とデータ用の2本の TCP 接続を使います。制御接続はサーバーの 21 番で、ここにコマンドと応答が流れます。ファイルの中身とディレクトリ一覧は、そのつど張り直す別の接続を通ります。データ接続をどちら側から張るかで2通りあり、パッシブモードではサーバーが待ち受けたポートへクライアントが接続し、アクティブモードではサーバーからクライアントへ接続します。
| パッケージ | 版 | 説明 |
|---|---|---|
vsftpd | 3.0.5 | main にある。この記事で使う |
proftpd-core | 1.3.9 | universe |
pure-ftpd | 1.0.50 | universe |
この記事で編集するファイルは /etc/vsftpd.conf の1つだけです。 公開用のディレクトリ(/srv/ftp-users/)は自分で作ります。
導入状況を確認する
サーバーは入っていません。 設定ファイルも 21 番の待ち受けもありません。
kazulog@sv1:~$ apt-cache policy vsftpd | head -3
vsftpd:
Installed: (none)
Candidate: 3.0.5-0.4
kazulog@sv1:~$ ls -l /etc/vsftpd.conf /srv/ftp 2>&1
ls: cannot access '/etc/vsftpd.conf': No such file or directory
ls: cannot access '/srv/ftp': No such file or directory
kazulog@sv1:~$ sudo ss -tlnp 'sport = :21'; echo "ss rc=$?"
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ss rc=0クライアントの ftp コマンドは最初から入っています(実体は tnftp)。curl も FTP と FTPS を話せます。
kazulog@sv2:~$ which ftp curl
/usr/bin/ftp
/usr/bin/curlFTP サーバーを導入する
sudo apt install -y vsftpd証明書を作る ssl-cert が一緒に入ります(後半の FTPS で使います)。
Installing:
vsftpd
Installing dependencies:
ssl-cert
Summary:
Upgrading: 0, Installing: 2, Removing: 0, Not Upgrading: 96
Download size: 141 kB
Space needed: 385 kB / 63.1 GB available導入した時点で有効化され、起動します。
Setting up ssl-cert (1.1.3ubuntu2) ...
Created symlink '/etc/systemd/system/multi-user.target.wants/ssl-cert.service' → '/usr/lib/systemd/system/ssl-cert.service'.
Setting up vsftpd (3.0.5-0.4) ...
Created symlink '/etc/systemd/system/multi-user.target.wants/vsftpd.service' → '/usr/lib/systemd/system/vsftpd.service'.
/usr/lib/tmpfiles.d/vsftpd.conf:1: Line references path below legacy directory /var/run/, updating /var/run/vsftpd/empty → /run/vsftpd/empty; please update the tmpfiles.d/ drop-in file accordingly.21 番を待ち受けます。表示が *:21 なのは IPv6 のソケットで IPv4 も受けているためです(listen=NO と listen_ipv6=YES)。
kazulog@sv1:~$ systemctl status vsftpd --no-pager | head -6 | cat
● vsftpd.service - vsftpd FTP server
Loaded: loaded (/usr/lib/systemd/system/vsftpd.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-09-12 10:16:30 JST; 7s ago
Invocation: 6e752ef8a1d14ad8895116d39f89c1bf
Process: 2759 ExecStartPre=/bin/mkdir -p /var/run/vsftpd/empty (code=exited, status=0/SUCCESS)
Main PID: 2761 (vsftpd)
kazulog@sv1:~$ sudo ss -tlnp 'sport = :21'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 32 *:21 *:* users:(("vsftpd",pid=2761,fd=3)) 設定ファイル(/etc/vsftpd.conf)
設定は /etc/vsftpd.conf の1ファイルです。コメントを除いた有効行は導入直後で13行あり、匿名接続は無効、システムの利用者はログインできる状態です。
kazulog@sv1:~$ grep -vE '^(#|$)' /etc/vsftpd.conf
listen=NO
listen_ipv6=YES
anonymous_enable=NO
local_enable=YES
dirmessage_enable=YES
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
secure_chroot_dir=/var/run/vsftpd/empty
pam_service_name=vsftpd
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
ssl_enable=NOこの記事で触るのは次の4か所です。write_enable と local_umask と chroot_local_user は # で無効にされた状態で置かれているので、その行を探して # を外すか、値を書き換えます。
kazulog@sv1:~$ grep -n 'write_enable\|local_umask\|chroot_local_user\|ssl_enable' /etc/vsftpd.conf
31:#write_enable=YES
35:#local_umask=022
44:#anon_mkdir_write_enable=YES
112:# the possible risks in this before using chroot_local_user or
114:#chroot_local_user=YES
117:# directory. If chroot_local_user is YES, then this list becomes a list of
122:#chroot_local_user=YES
151:ssl_enable=NO| 設定 | 既定 | 行 | 意味 |
|---|---|---|---|
anonymous_enable | NO | 有効行 | 匿名(ftp 利用者)での接続 |
local_enable | YES | 有効行 | システムの利用者でのログイン |
write_enable | コメント | 31 | 書き込み系のコマンド全般。無いとアップロードできない |
local_umask | コメント(=077) | 35 | アップロードされたファイルの権限 |
chroot_local_user | コメント | 114 と 122 | 利用者を自分のディレクトリに閉じ込める |
ssl_enable | NO | 151 | FTPS |
chroot_local_user のコメント行は 114 行目と 122 行目の2か所にあります。 どちらを外しても同じですが、この記事ではファイルの末尾に追記する方式にします(関連する3行をまとめて置けるため)。
変更したら sudo systemctl restart vsftpd で反映します。なお /etc/ftpusers に書かれた利用者(root など)は FTP でログインできません。
ログインしてファイルを取得する
検証用の利用者を作ります。このパスワードは平文で流れることを示すための検証専用の文字列で、実際の運用では使いません。
sudo useradd -m -s /bin/bash ftpdemo
echo 'ftpdemo:FtpIsPlaintext' | sudo chpasswd
echo "hello from ftp server sv1" | sudo -u ftpdemo tee /home/ftpdemo/hello.txtkazulog@sv1:~$ sudo useradd -m -s /bin/bash ftpdemo
kazulog@sv1:~$ echo 'ftpdemo:FtpIsPlaintext' | sudo chpasswd
kazulog@sv1:~$ id ftpdemo
uid=1001(ftpdemo) gid=1001(ftpdemo) groups=1001(ftpdemo)
kazulog@sv1:~$ echo "hello from ftp server sv1" | sudo -u ftpdemo tee /home/ftpdemo/hello.txt別のサーバーから ftp でログインします。現在地はホームの /home/ftpdemo で、get は成功し、put は 550 Permission denied. で失敗します(write_enable が無いため)。
kazulog@sv2:~$ cd /tmp
kazulog@sv2:/tmp$ echo "uploaded from sv2" > upload.txt
kazulog@sv2:/tmp$ ftp 192.168.100.10
Connected to 192.168.100.10.
220 (vsFTPd 3.0.5)
Name (192.168.100.10:kazulog): ftpdemo
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pwd
Remote directory: /home/ftpdemo
ftp> ls
229 Entering Extended Passive Mode (|||59134|)
150 Here comes the directory listing.
-rw-r--r-- 1 1001 1001 26 Sep 12 10:16 hello.txt
226 Directory send OK.
ftp> get hello.txt
local: hello.txt remote: hello.txt
229 Entering Extended Passive Mode (|||53233|)
150 Opening BINARY mode data connection for hello.txt (26 bytes).
100% |***********************************************************************************************************************************************************| 26 60.88 KiB/s 00:00 ETA
226 Transfer complete.
26 bytes received in 00:00 (9.26 KiB/s)
ftp> put upload.txt
local: upload.txt remote: upload.txt
229 Entering Extended Passive Mode (|||47977|)
550 Permission denied.
ftp> bye
221 Goodbye.229 Entering Extended Passive Mode はパッシブモードの応答で、括弧の中の数字がデータ接続のポートです。
アップロードを許可する(31行目)
sudo vi /etc/vsftpd.conf31 行目の # を外します。
write_enable=YESkazulog@sv1:~$ grep -n 'write_enable\|local_umask' /etc/vsftpd.conf
31:write_enable=YES
35:#local_umask=022
44:#anon_mkdir_write_enable=YES
kazulog@sv1:~$ sudo systemctl restart vsftpdアップロードできるようになりますが、作られたファイルは -rw-------(600)です。 local_umask が無いときの既定が 077 のためです。
kazulog@sv2:~$ cd /tmp
kazulog@sv2:/tmp$ ftp 192.168.100.10
Connected to 192.168.100.10.
220 (vsFTPd 3.0.5)
Name (192.168.100.10:kazulog): ftpdemo
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> put upload.txt
local: upload.txt remote: upload.txt
229 Entering Extended Passive Mode (|||52014|)
150 Ok to send data.
100% |***********************************************************************************************************************************************************| 18 29.15 KiB/s 00:00 ETA
226 Transfer complete.
18 bytes sent in 00:00 (3.69 KiB/s)
ftp> ls
229 Entering Extended Passive Mode (|||53689|)
150 Here comes the directory listing.
-rw-r--r-- 1 1001 1001 26 Sep 12 10:16 hello.txt
-rw------- 1 1001 1001 18 Sep 12 10:20 upload.txt
226 Directory send OK.
ftp> bye
221 Goodbye.アップロードされたファイルの権限を決める(35行目)
他の利用者にも読ませるなら 35 行目の # を外します。
sudo vi /etc/vsftpd.conflocal_umask=022kazulog@sv1:~$ sudo ls -l /home/ftpdemo/
total 8
-rw-r--r-- 1 ftpdemo ftpdemo 26 Sep 12 10:16 hello.txt
-rw------- 1 ftpdemo ftpdemo 18 Sep 12 10:20 upload.txt
kazulog@sv1:~$ grep -n 'write_enable\|local_umask' /etc/vsftpd.conf
31:write_enable=YES
35:local_umask=022
44:#anon_mkdir_write_enable=YES
kazulog@sv1:~$ sudo systemctl restart vsftpd設定後にアップロードしたファイルは -rw-r--r--(644)になります。先にアップロードしたファイルの権限は変わりません。
kazulog@sv2:~$ cd /tmp
kazulog@sv2:/tmp$ ftp 192.168.100.10
Connected to 192.168.100.10.
220 (vsFTPd 3.0.5)
Name (192.168.100.10:kazulog): ftpdemo
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> put upload.txt upload2.txt
local: upload.txt remote: upload2.txt
229 Entering Extended Passive Mode (|||6067|)
150 Ok to send data.
100% |***********************************************************************************************************************************************************| 18 43.94 KiB/s 00:00 ETA
226 Transfer complete.
18 bytes sent in 00:00 (4.41 KiB/s)
ftp> ls
229 Entering Extended Passive Mode (|||21507|)
150 Here comes the directory listing.
-rw-r--r-- 1 1001 1001 26 Sep 12 10:16 hello.txt
-rw------- 1 1001 1001 18 Sep 12 10:20 upload.txt
-rw-r--r-- 1 1001 1001 18 Sep 12 10:21 upload2.txt
226 Directory send OK.
ftp> bye
221 Goodbye.利用者を自分のディレクトリに閉じ込める(末尾に追記)
chroot_local_user=YES にすると、ログインした利用者は公開場所より上へ行けなくなります。ただし閉じ込め先の最上位ディレクトリに本人の書き込み権があると、vsftpd はログインを拒みます(500 OOPS: vsftpd: refusing to run with writable root inside chroot())。ホームをそのまま使うと本人が書けてしまうので、最上位を root の持ち物にし、その下に書き込み用のディレクトリを置くのが定石です。
local_root で公開場所を変え、user_sub_token で利用者ごとに分けます。
sudo vi /etc/vsftpd.conf# 利用者を自分のディレクトリに閉じ込める
chroot_local_user=YES
user_sub_token=$USER
local_root=/srv/ftp-users/$USER公開場所を作ります。最上位は root のまま、upload だけ本人の持ち物にします。
sudo mkdir -p /srv/ftp-users/ftpdemo/upload
sudo chown ftpdemo:ftpdemo /srv/ftp-users/ftpdemo/upload
echo "hello from ftp server sv1" | sudo tee /srv/ftp-users/ftpdemo/hello.txtkazulog@sv1:~$ tail -5 /etc/vsftpd.conf
#utf8_filesystem=YES
# 利用者を自分のディレクトリに閉じ込める
chroot_local_user=YES
user_sub_token=$USER
local_root=/srv/ftp-users/$USER
kazulog@sv1:~$ sudo ls -laR /srv/ftp-users/
/srv/ftp-users/:
total 12
drwxr-xr-x 3 root root 4096 Sep 12 10:22 .
drwxr-xr-x 4 root root 4096 Sep 12 10:22 ..
drwxr-xr-x 3 root root 4096 Sep 12 10:22 ftpdemo
/srv/ftp-users/ftpdemo:
total 16
drwxr-xr-x 3 root root 4096 Sep 12 10:22 .
drwxr-xr-x 3 root root 4096 Sep 12 10:22 ..
-rw-r--r-- 1 root root 26 Sep 12 10:22 hello.txt
drwxr-xr-x 2 ftpdemo ftpdemo 4096 Sep 12 10:22 upload
/srv/ftp-users/ftpdemo/upload:
total 8
drwxr-xr-x 2 ftpdemo ftpdemo 4096 Sep 12 10:22 .
drwxr-xr-x 3 root root 4096 Sep 12 10:22 ..
kazulog@sv1:~$ sudo systemctl restart vsftpdログイン後の現在地は / になり、上位には出られません。最上位には置けず(553)、書き込み用のディレクトリには置けます。
kazulog@sv2:~$ cd /tmp
kazulog@sv2:/tmp$ ftp 192.168.100.10
Connected to 192.168.100.10.
220 (vsFTPd 3.0.5)
Name (192.168.100.10:kazulog): ftpdemo
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pwd
Remote directory: /
ftp> ls
229 Entering Extended Passive Mode (|||47435|)
150 Here comes the directory listing.
-rw-r--r-- 1 0 0 26 Sep 12 10:22 hello.txt
drwxr-xr-x 2 1001 1001 4096 Sep 12 10:22 upload
226 Directory send OK.
ftp> cd /home
550 Failed to change directory.
ftp> put upload.txt
local: upload.txt remote: upload.txt
229 Entering Extended Passive Mode (|||51762|)
553 Could not create file.
ftp> cd upload
250 Directory successfully changed.
ftp> put upload.txt
local: upload.txt remote: upload.txt
229 Entering Extended Passive Mode (|||31305|)
150 Ok to send data.
100% |***********************************************************************************************************************************************************| 18 36.02 KiB/s 00:00 ETA
226 Transfer complete.
18 bytes sent in 00:00 (3.83 KiB/s)
ftp> ls
229 Entering Extended Passive Mode (|||14734|)
150 Here comes the directory listing.
-rw-r--r-- 1 1001 1001 18 Sep 12 10:23 upload.txt
226 Directory send OK.
ftp> pwd
Remote directory: /upload
ftp> bye
221 Goodbye.暗号化する(FTPS、151行目)
151 行目を YES にすると FTPS(FTP over TLS、RFC 4217)になります。証明書は導入時に入った snakeoil のものが 149・150 行目で既に指定されています。
sudo vi /etc/vsftpd.confssl_enable=YESkazulog@sv1:~$ grep -n 'ssl_enable\|rsa_cert_file\|rsa_private_key_file' /etc/vsftpd.conf
149:rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
150:rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
151:ssl_enable=YES
kazulog@sv1:~$ sudo systemctl restart vsftpd平文のログインは拒否されます。 tnftp は TLS に対応していないので、パスワードを聞かれる前に切られます。
kazulog@sv2:~$ cd /tmp
kazulog@sv2:/tmp$ ftp 192.168.100.10
Connected to 192.168.100.10.
220 (vsFTPd 3.0.5)
Name (192.168.100.10:kazulog): ftpdemo
530 Non-anonymous sessions must use encryption.
ftp: Login failed
ftp> byeFTPS のクライアントには curl を使います。--ssl-reqd で暗号化を必須にし、-k は snakeoil 証明書の検証を省くためのものです。
kazulog@sv2:~$ curl -sS -u ftpdemo:FtpIsPlaintext ftp://192.168.100.10/hello.txt; echo "rc=$?"
curl: (67) Access denied: 530
rc=67
kazulog@sv2:~$ curl -sS --ssl-reqd -k -u ftpdemo:FtpIsPlaintext ftp://192.168.100.10/hello.txt; echo "rc=$?"
hello from ftp server sv1
rc=0
kazulog@sv2:~$ curl -sS --ssl-reqd -k -T /tmp/upload.txt -u ftpdemo:FtpIsPlaintext ftp://192.168.100.10/upload/curl.txt; echo "rc=$?"
rc=0設定ファイルの最終状態
ここまでの設定が入った状態です。触ったファイルは /etc/vsftpd.conf の1つだけで、導入直後の13行に5行が加わって18行になっています。
kazulog@sv1:~$ grep -vE '^(#|$)' /etc/vsftpd.conf
listen=NO
listen_ipv6=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
secure_chroot_dir=/var/run/vsftpd/empty
pam_service_name=vsftpd
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
ssl_enable=YES
chroot_local_user=YES
user_sub_token=$USER
local_root=/srv/ftp-users/$USER自分で作った公開場所と、サービスの状態です。最上位は root:root、upload だけが ftpdemo:ftpdemo になっています。
kazulog@sv1:~$ sudo ls -laR /srv/ftp-users/
/srv/ftp-users/:
total 12
drwxr-xr-x 3 root root 4096 Sep 12 10:22 .
drwxr-xr-x 4 root root 4096 Sep 12 10:22 ..
drwxr-xr-x 3 root root 4096 Sep 12 10:22 ftpdemo
/srv/ftp-users/ftpdemo:
total 16
drwxr-xr-x 3 root root 4096 Sep 12 10:22 .
drwxr-xr-x 3 root root 4096 Sep 12 10:22 ..
-rw-r--r-- 1 root root 26 Sep 12 10:22 hello.txt
drwxr-xr-x 2 ftpdemo ftpdemo 4096 Sep 12 10:24 upload
/srv/ftp-users/ftpdemo/upload:
total 16
drwxr-xr-x 2 ftpdemo ftpdemo 4096 Sep 12 10:24 .
drwxr-xr-x 3 root root 4096 Sep 12 10:22 ..
-rw-r--r-- 1 ftpdemo ftpdemo 18 Sep 12 10:24 curl.txt
-rw-r--r-- 1 ftpdemo ftpdemo 18 Sep 12 10:23 upload.txt
kazulog@sv1:~$ systemctl is-enabled vsftpd; systemctl is-active vsftpd
enabled
active
kazulog@sv1:~$ sudo ss -tlnp 'sport = :21'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 32 *:21 *:* users:(("vsftpd",pid=5460,fd=3)) 平文で流れることを確かめる
sv1 とスイッチの間のリンクでキャプチャーすると、利用者名とパスワードがそのまま読めます。 最後の2回(No.277・No.292)は FTPS を有効にした後なので、USER だけで切られて PASS が出ていません。
$ tshark -r ftp-server.pcap -n -Y 'ftp.request.command == "USER" || ftp.request.command == "PASS"' -T fields -e frame.number -e ip.src -e ftp.request.command -e ftp.request.arg
6 192.168.100.20 USER ftpdemo
9 192.168.100.20 PASS FtpIsPlaintext
77 192.168.100.20 USER ftpdemo
80 192.168.100.20 PASS FtpIsPlaintext
133 192.168.100.20 USER ftpdemo
136 192.168.100.20 PASS FtpIsPlaintext
189 192.168.100.20 USER ftpdemo
192 192.168.100.20 PASS FtpIsPlaintext
277 192.168.100.20 USER ftpdemo
292 192.168.100.20 USER ftpdemoパスワードのパケットを16進で見ても、文字列がそのまま入っています。
$ tshark -r ubuntu-ftp-no9.pcap -n -x
0000 52 54 00 30 7e c3 52 54 00 78 56 37 08 00 45 10 RT.0~.RT.xV7..E.
0010 00 49 d0 db 40 00 40 06 20 54 c0 a8 64 14 c0 a8 .I..@.@. T..d...
0020 64 0a b8 84 00 15 af 9b d2 31 5a d4 22 ec 80 18 d........1Z."...
0030 00 fb e7 1e 00 00 01 01 08 0a 2d 4d d3 94 eb a6 ..........-M....
0040 1a d8 50 41 53 53 20 46 74 70 49 73 50 6c 61 69 ..PASS FtpIsPlai
0050 6e 74 65 78 74 0d 0a ntext..データ接続が別の TCP 接続であることも追えます。229 の応答で示された 53233 番へクライアントが接続し(No.42〜44)、そこをファイルの中身が流れて閉じます。
$ tshark -r ftp-server.pcap -n -Y 'frame.number >= 40 && frame.number <= 52' -T fields -e frame.number -e ip.src -e tcp.srcport -e ip.dst -e tcp.dstport -e _ws.col.info
40 192.168.100.20 47236 192.168.100.10 21 Request: EPSV
41 192.168.100.10 21 192.168.100.20 47236 Response: 229 Entering Extended Passive Mode (|||53233|)
42 192.168.100.20 60442 192.168.100.10 53233 60442 → 53233 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM TSval=2184570297 TSecr=0 WS=256
43 192.168.100.10 53233 192.168.100.20 60442 53233 → 60442 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1460 SACK_PERM TSval=2456829945 TSecr=2184570297 WS=256
44 192.168.100.20 60442 192.168.100.10 53233 60442 → 53233 [ACK] Seq=1 Ack=1 Win=64256 Len=0 TSval=2184570299 TSecr=2456829945
45 192.168.100.20 47236 192.168.100.10 21 Request: RETR hello.txt
46 192.168.100.10 21 192.168.100.20 47236 Response: 150 Opening BINARY mode data connection for hello.txt (26 bytes).
47 192.168.100.10 53233 192.168.100.20 60442 FTP Data: 26 bytes (EPASV) (RETR hello.txt)
48 192.168.100.10 53233 192.168.100.20 60442 53233 → 60442 [FIN, ACK] Seq=27 Ack=1 Win=65280 Len=0 TSval=2456829947 TSecr=2184570299
49 192.168.100.20 60442 192.168.100.10 53233 60442 → 53233 [ACK] Seq=1 Ack=27 Win=64256 Len=0 TSval=2184570301 TSecr=2456829947
50 192.168.100.20 60442 192.168.100.10 53233 60442 → 53233 [FIN, ACK] Seq=1 Ack=28 Win=64256 Len=0 TSval=2184570302 TSecr=2456829947
51 192.168.100.10 53233 192.168.100.20 60442 53233 → 60442 [ACK] Seq=28 Ack=2 Win=65280 Len=0 TSval=2456829950 TSecr=2184570302
52 192.168.100.10 21 192.168.100.20 47236 Response: 226 Transfer complete.パッシブモードのポートは毎回変わります。 範囲を決めたい場合は pasv_min_port と pasv_max_port を指定します。
$ tshark -r ftp-server.pcap -n -Y 'ftp.response.code == 229' -T fields -e frame.number -e ftp.response.arg
23 Entering Extended Passive Mode (|||59134|)
41 Entering Extended Passive Mode (|||53233|)
57 Entering Extended Passive Mode (|||47977|)
94 Entering Extended Passive Mode (|||52014|)
110 Entering Extended Passive Mode (|||53689|)
150 Entering Extended Passive Mode (|||6067|)
166 Entering Extended Passive Mode (|||21507|)
206 Entering Extended Passive Mode (|||47435|)
224 Entering Extended Passive Mode (|||51762|)
236 Entering Extended Passive Mode (|||31305|)
252 Entering Extended Passive Mode (|||14734|)パスワードを含むフレームは平文のログイン4回分だけで、FTPS のログインは含まれません。
$ tshark -r ftp-server.pcap -n -Y 'frame contains "FtpIsPlaintext"' -T fields -e frame.number
9
80
136
192TLS になった後のパケットを16進で見ても、読める文字列はありません。
$ tshark -r ubuntu-ftp-no346.pcap -n -x
0000 52 54 00 30 7e c3 52 54 00 78 56 37 08 00 45 00 RT.0~.RT.xV7..E.
0010 00 7e d6 cf 40 00 40 06 1a 3b c0 a8 64 14 c0 a8 .~..@.@..;..d...
0020 64 0a ab d0 5a c3 e3 d3 c3 78 be 6f 14 d9 80 18 d...Z....x.o....
0030 00 fb 1f fb 00 00 01 01 08 0a db 9a d4 4d 67 1f .............Mg.
0040 1a ce 17 03 03 00 45 7d 26 01 1c 7d 16 17 0b 95 ......E}&..}....
0050 1f 8b de fa fa e4 77 8e 2c 45 83 69 76 18 ef 4f ......w.,E.iv..O
0060 b5 d3 ac 11 c2 71 ec 56 a2 a1 c5 06 ce 27 d8 a8 .....q.V.....'..
0070 8a b9 19 27 2d b6 28 27 dc c9 1f ad d0 28 c6 10 ...'-.('.....(..
0080 37 c1 ef 9d 08 14 90 d4 30 00 73 6d 7.......0.smキャプチャーでは AUTH SSL に 234 Proceed with negotiation. が返り、以降は TLS になります。
$ tshark -r ftp-server.pcap -n -Y 'ftp.request.command == "AUTH" || ftp.response.code == 234' -T fields -e frame.number -e ip.src -e ftp.request.arg -e ftp.response.arg
303 192.168.100.20 SSL
305 192.168.100.10 Proceed with negotiation.
369 192.168.100.20 SSL
371 192.168.100.10 Proceed with negotiation.ルータの設定を保存する
IOS XE のルータ R1 から copy コマンドで running-config をアップロードします。ルータ側は平文の FTP しか話さないので、151 行目を ssl_enable=NO に戻してから行います。
sudo vi /etc/vsftpd.confkazulog@sv1:~$ grep -n 'ssl_enable' /etc/vsftpd.conf
151:ssl_enable=NO
kazulog@sv1:~$ sudo systemctl restart vsftpdルータ側は利用者名とパスワードを ip ftp username / ip ftp password で設定します(確認の問い合わせを省くため file prompt quiet も入れています)。
file prompt quiet
ip ftp username ftpdemo
ip ftp password FtpIsPlaintext保存先の URL は閉じ込めた後の見え方に合わせます。 local_root=/srv/ftp-users/$USER なので、ルータから見た最上位が /srv/ftp-users/ftpdemo にあたり、書き込めるのは upload/ の下だけです。
R1#copy running-config ftp://192.168.100.10/upload/r1-confg
Writing upload/r1-confg !
4977 bytes copied in 3.419 secs (1456 bytes/sec)サーバー側に届いています。
kazulog@sv1:~$ sudo ls -l /srv/ftp-users/ftpdemo/upload/
total 8
-rw-r--r-- 1 ftpdemo ftpdemo 18 Sep 12 10:24 curl.txt
-rw-r--r-- 1 ftpdemo ftpdemo 18 Sep 12 10:23 upload.txt
kazulog@sv1:~$ sudo ls -l /srv/ftp-users/ftpdemo/upload/
total 16
-rw-r--r-- 1 ftpdemo ftpdemo 18 Sep 12 10:24 curl.txt
-rw-r--r-- 1 ftpdemo ftpdemo 4977 Sep 12 10:30 r1-confg
-rw-r--r-- 1 ftpdemo ftpdemo 18 Sep 12 10:23 upload.txt
kazulog@sv1:~$ sudo head -3 /srv/ftp-users/ftpdemo/upload/r1-confg
!
! Last configuration change at 01:29:39 UTC Sat Sep 12 2026確認できたら、ルータ側の一時的な設定は no ip ftp username / no ip ftp password / no file prompt quiet で戻します。
削除する
sudo apt purge -y vsftpd/etc/vsftpd.conf は消えますが、ftp 利用者と自分で作った /srv/ftp-users/(アップロードされたファイルを含む)は残ります。 依存で入った ssl-cert も残ります。
kazulog@sv1:~$ sudo apt purge -y vsftpd | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
The following package was automatically installed and is no longer required:
ssl-cert
Use 'sudo apt autoremove' to remove it.
REMOVING:
vsftpd*
Summary:
Upgrading: 0, Installing: 0, Removing: 1, Not Upgrading: 96
Freed space: 316 kB
(Reading database ... 91464 files and directories currently installed.)
Removing vsftpd (3.0.5-0.4) ...
Processing triggers for man-db (2.13.1-1build1) ...
(Reading database ... 91413 files and directories currently installed.)
Purging configuration files for vsftpd (3.0.5-0.4) ...
kazulog@sv1:~$ dpkg -l vsftpd 2>&1 | tail -1
dpkg-query: no packages found matching vsftpd
kazulog@sv1:~$ ls -l /etc/vsftpd.conf 2>&1
ls: cannot access '/etc/vsftpd.conf': No such file or directory
kazulog@sv1:~$ sudo ls -ld /srv/ftp /srv/ftp-users/ftpdemo/upload 2>&1
ls: cannot access '/srv/ftp': No such file or directory
drwxr-xr-x 2 ftpdemo ftpdemo 4096 Sep 12 10:30 /srv/ftp-users/ftpdemo/upload
kazulog@sv1:~$ getent passwd ftp
ftp:x:103:110:ftp daemon:/srv/ftp:/usr/sbin/nologin
kazulog@sv1:~$ sudo ss -tlnp 'sport = :21'; echo "ss rc=$?"
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ss rc=0公開場所ごと消すなら sudo userdel -r ftpdemo と sudo rm -rf /srv/ftp-users を別に実行します。
検証環境と実行ログ
CML 上に Ubuntu 26.04 LTS Server 2台と IOS XE のルータ1台を置いて採取しました。Ubuntu のディスクは wipe した直後の状態から始めています(前の検証で入れたパッケージが残っていると手順が再現しないため)。
sv1 sv2 R1
ens3 192.168.100.10/24 ens3 192.168.100.20/24 Gi2 192.168.100.1/24
vsftpd (TCP 21) ftp / curl IOS XE 17.03.08a
| | |
+--------- LAB-SW ----------+---------------------------+
^ このリンクでキャプチャー(sv2 との TCP のみ)キャプチャー全体のファイルです。
FTP と FTPS のキャプチャー(ubuntu-ftp.pcap)..._ftp.txt は sv2 での ftp コマンドの操作記録です。
| 手順 | sv1 | sv2 | R1 |
|---|---|---|---|
| 初期状態(wipe 直後) | show / conf / log | show / conf / log | — |
| 導入(/etc/vsftpd.conf ができる) | show / conf / log | — | — |
| 検証用の利用者を作る | show / conf / log | — | — |
| ログインと取得(put は 550) | — | show / conf / log / ftp | — |
| 31行目 write_enable | show / conf / log | ftp | — |
| 35行目 local_umask | show / conf / log | ftp | — |
| 末尾に追記(chroot と local_root) | show / conf / log | ftp | — |
| 151行目 ssl_enable(FTPS) | show / conf / log | show / conf / log / ftp | — |
| 設定ファイルの最終状態 | show / conf / log | — | — |
| FTPS を戻してルータから保存 | show / conf / log | — | show |
| 削除(最終状態) | show / conf / log | — | — |
| キャプチャーの解析 | tshark | — | — |
参考リンク
Ubuntu Server: Set up an FTP server
RFC 959 - File Transfer Protocol
関連記事
- Ubuntu 26.04 LTS Server のホスト名の変更(hostnamectl)
- Ubuntu 26.04 LTS Server のパッケージ更新(apt update / upgrade)
- Ubuntu 26.04 LTS Server のタイムゾーン設定と時刻同期
- Ubuntu 26.04 LTS Server のユーザー作成と sudo 権限の設定
- Ubuntu 26.04 LTS Server の SSH サーバー設定(公開鍵認証)
- Ubuntu 26.04 LTS Server の telnet サーバー設定
- Ubuntu 26.04 LTS Server の inetd(スーパーサーバー)設定
- Ubuntu 26.04 LTS Server の TFTP サーバー設定(tftpd-hpa)
- Ubuntu 26.04 LTS Server の FTP サーバー設定(vsftpd)
- Ubuntu 26.04 LTS Server の syslog サーバー設定(rsyslog)
- Ubuntu 26.04 LTS Server のサービス管理(systemctl)とログ確認(journalctl)
- Ubuntu 26.04 LTS Server の自動更新設定(unattended-upgrades)
- Ubuntu 26.04 LTS Server の初期設定を自動化する(cloud-init)
- Ubuntu 26.04 LTS Server のカーネルパラメータ設定(sysctl)
- Ubuntu 26.04 LTS Server のネットワーク設定(Netplan)
- Ubuntu 26.04 LTS Server の名前解決設定(systemd-resolved)
- Ubuntu 26.04 LTS Server の NTP 同期先の変更(chrony)
- Ubuntu 26.04 LTS Server のスタティックルート設定(Netplan)
- neovim 公式サイトから最新バージョンをインストールする手順 Ubuntu 26.04 LTS Server