オフィシャルサイト: vsftpd - Secure, fast FTP server for UNIX-like systems

vsftpd

このページでは FTP サーバデーモンとして vsftpd を取り上げる。かつては様々な Linux ディストリビューションが wu-ftpd を標準採用していたものだが、今や vsftpd が主流だ。 vsftpd は、権限毎に分離したプロセス、 tcp-wrappers 対応、 PAM 対応など、セキュリティに重点を置いて開発された FTP サーバ。ただ、その分、設定がやや複雑で、充分に使いこなそうとすると難しい面もある。さらに、ここでは述べないが、暗号化通信や、バーチャルユーザ、バーチャルホストといった、より高度な機能も搭載している(らしい)。

Table of Contents

主設定ファイル vsftpd.conf

インストール行程の説明は省き、vsftpd 主設定ファイルのディレクティブを解説する。 RedHat系でのデフォルトでは /etc/vsftpd/vsftpd.conf がそのファイルだ。設定項目は山ほどあるが、主要なものの内でも特に重要または理解しにくいものだけを取り上げることにする。

主なディレクティブ

基本規則:

ディレクティブ 説明
基本機能
listen=(YES|NO) FTP コントロールポート (21) への接続要求を vsftpd 自身で見張るか。 YES の状態を「スタンドアローンモード」と呼ぶ。 inetd や後述の tcpserver を経由する場合は NO にする。 YES の場合、 listen_port ディレクティブで指定すれば、標準以外のポートナンバーで待機させることも可能。
tcp_wrappers=(YES|NO) tcp-wrappers によるアクセス制限メカニズムを利用可能にするか。 tcpserver 経由の時は NO に。
pam_service_name=vsftpd PAM (Pluggable Authentication Modules) に対して認識させるサービス名。つまり、ここで `vsftpd' と指定した場合、 PAM 認証手順は /etc/pam.d/vsftpd ファイルから読まれることになる。
use_localtime=(YES|NO) これを YES にしないと、ログも、FTP クライアント上でのファイルタイム表示も GMT となり、表示上の時間がずれる。 chroot動作の留意点も参照のこと。
text_userdb_names=(YES|NO) クライアント上のファイルオーナー表示 (UID及びGID) を数字でなく /etc/passwd, /etc/group から解決された名前で表示する。 chroot動作の留意点も参照のこと。
max_clients=10 並列して存在できるクライアントセッションの数。スタンドアローンモード時のみ影響。
connect_from_port_20=(YES|NO) アクティブモード FTP の際に、 FTP_DATA コネクションのサーバ側ポートを常に ftp_data_port での定義ポートに固定して通信する。パッシブ FTP 通信には影響はない。今どきパッシブモードを使いたがらないクライアントは、 FTP クライアントアプリに何か悲しい物語があるか、間にプアーなファイヤーウォールが立ちふさがっているかのどちらかだと思われるので、これは YES にしておくの が良さそう。
ftp_data_port=20 説明は connect_from_port_20 を参照のこと。デフォルトは 20 番ポート。 connect_from_port_20 が YES でない限り、設定しても効果はない。
pasv_min_port=50000 パッシブモード FTP で FTP_DATA コネクションを結ぶ際に、サーバ側ポートとして使用する不特定ポートナンバーの最小値。
pasv_max_port=60000 パッシブモード FTP で FTP_DATA コネクションを結ぶ際に、サーバ側ポートとして使用する不特定ポートナンバーの最大値。
拡張機能
local_enable=(YES|NO) OS 上の /etc/passwd に記述されている UNIX ユーザリストを FTP ユーザとして利用できるようにする。 anonymous ログインはまた別の話。(参照: ローカルとアノニマスを両立させるには)
anonymous_enable=(NO|YES) アノニマス (匿名) FTP ログインを可能にするか。 WEB コンテンツのアップロードやシステム保守などに利用するだけなら要らないので NO にすべき。その場合、コメントアウトした場合のデフォルトは YES なので必ず NO と明示すること。アノニマスログインすると、ftp_username で指定したシステムアカウントのホームディレクトリへ chroot される (参照: ローカルとアノニマスを両立させるには)。
ascii_upload_enable=(NO|YES) クライアントが ASCII モード (テキストモード) でのファイルアップロードを要求した時に本当に ASCII モード処理を行う。 NO (デフォルト) の場合、 vsftpd は ASCII モードを実装しているフリだけして実際には改行コード変換を行わない。
ascii_download_enable=(NO|YES) 同上。クライアントが ASCII モードでのダウンロードを要求した際の設定。
dirmessage_enable=(YES|NO) ひとつの FTP セッション中で初めてのディレクトリに入った時、メッセージを表示する。メッセージ内容はそのディレクトリの .message ファイルから読み取ろうとする。テキストベースの FTP クライアントや、このメッセージを何らかの正当性チェックに利用するクライアントアプリがあるならまだしも、通常は単なるリソースの無駄。
write_enable=(YES|NO) YES にすると、ファイルシステムに変更を加えるコマンド (STOR, DELE, RNFR, RNTO, MKD, RMD, APPE, SITE コマンド) の使用が許可される。つまり、(親ディレクトリのパーミッションや umask が然るべき状態であれば) アップロードや削除などが可能となる。下記 anon_upload_enable にも影響を与える。
anon_upload_enable=(YES|NO) アノニマス接続時のアップロードを可能とするか。これを YES にしたい場合には、上記 write_enableYES でなければ効力は得られない。ローカルとアノニマスを両立させるには も参照のこと。
anon_mkdir_write_enable=(YES|NO) アノニマス接続時のディレクトリ作成を可能とするか。上記 write_enableYES にしておく必要がある。
anon_other_write_enable=(YES|NO) アノニマス接続時にファイルやディレクトリの削除やリネームも許可したければ YES に。セキュリティ的にはあまり良くない。
local_umask=022 アノニマスでない時のアップロード umask。デフォルトは 077
anon_umask=002 アノニマスの時のアップロード umask。デフォルトである 077 は、共有フォルダ的に使う場合には厳しすぎて、アップロードしたファイルをダウンロードすることができない。
ログ関係
xferlog_enable=(YES|NO) FTP セッションログを出力するか。下記の syslog_enable には影響を与えない。
syslog_enable=(YES|NO) ログを syslog 経由で出力し、vsftpd_log_filexferlog_file への直接出力はしなくなる。ログファシリティは FTPD で固定。 xferlog_enable の YES/NO には依存しない。
xferlog_std_format=(NO|YES) ログ出力を wu-ftpd で用いられてきた「標準」フォーマットで行う。出力先ファイルは下記 xferlog_file で指定したものとなる。 NO の時の出力先は vsftpd_log_file で指定したファイル。
dual_log_enable=(NO|YES) 通常の vsftpd_logxferlog_std_format の両方にログを出力する。
vsftpd_log_file=/path/to/file syslog でもなく xferlog_std_format でもない通常の vsftpd 形式ログの出力先。指定しない場合のデフォルトは /var/log/vsftpd.log
xferlog_file=/path/to/file xferlog_std_format に対するログ出力先。デフォルトは /var/log/xferlog
セキュリティ関係
userlist_enable=(YES|NO) ログインを許可または拒否するユーザを userlist_file から読み取る。
userlist_deny=(NO|YES) 上の userlist_enable が YES の場合にだけ評価される。これを YES にすると userlist_file拒否リストとして機能し、 NOの時には許可リストとして機能する。筆者のお薦めは、許可リストとしての NO。というのも Fedora Core のデフォルトでは、別途 PAM が /etc/vsftpd/ftpusers ファイル (バージョンによっては /etc/vsftpd.ftpusers) を拒否リストとして処理するようになっているからだ。無指定時のデフォルトは YES なので注意。拒否リストftpusers ファイルの素早い作り方 でやや詳しく述べる。
userlist_file=/path/to/file 上記ふたつに関して読み取られるユーザリストファイルのパス。デフォルトは /etc/vsftpd/user_list (バージョンによっては /etc/vsftpd.user_list)。
chroot_list_enable=(YES|NO) ログイン直後に各自のホームディレクトリへ chroot jail されるユーザ (またはされないユーザ) を chroot_list_file から読み取る。
chroot_local_user=(NO|YES) OS 上に UNIX ユーザとして存在しているユーザが FTP ログインしてきた際に、ホームディレクトリへ chroot jail するという挙動をデフォルトにしてしまう。
また、これを有効 (YES) にした場合、 chroot_list_file は chroot させないユーザのリストとして機能し、 NO の場合は逆に chroot させるユーザのリストとして機能する。 [注意!]
chroot_list_file=/path/to/file 上記ふたつに関して読み取られるユーザリストファイルのパス。
nopriv_user=nobody vsftpd はセッション毎にふたつの子プロセスを発生させる。 local ユーザの場合、ひとつはそのログインユーザの権限で動くプロセス、もうひとつがこの nopriv_user 権限のプロセスだ。 nopriv_user のプリコンパイル設定は nobody だが、nobody はその名に似合わず比較的重要な他の任務にも使われていることが多いので、新たな専用ユーザを作るべき (作成例)。
ftp_username=ftp アノニマス FTP の際には上記 nopriv_user 権限の子プロセスと、ここで定義したユーザの所有するプロセスが立ち上がる (上記 local ログインユーザプロセスの代わりと考えればいい)。ローカルとアノニマスを両立させるには も参照のこと。

ローカルとアノニマスを両立させるには

vsftpdanonymous という「ユーザ名」でのログイン要求を受けると、それをきっかけとして匿名ログインメカニズムを起動し、ftp_username ディレクティブで指定されたシステムアカウントのホームディレクトリ (デフォルトでは ftp ユーザのホームディレクトリである /var/ftp/) へ chroot した状態で匿名ユーザを受け入れる。よって、アノニマスで /var/ftp/pub/ へのアップロードを可能に (anon_upload_enable=YES) したければ、pub/ ディレクトリのパーミッションは ftp:ftp の 755 または 775 にしておかなければならない。ただし、単に local_enableanonymous_enable を両方 YES にしただけでは、ローカルユーザでのログインだけできて、アノニマスではログインできない状態となる。方策はふたつある。

ひとつは、 userlist_enable=YES, userlist_deny=NO にすることによって、 userlist_file で指定したファイル (最近のデフォルトは /etc/vsftpd/user_list) が許可リストとして働くようにしておき、そのリストファイルに anonymous というユーザも書いておく方法。とても簡単だ。

もうひとつは、vsftpd の起動時に、クライアントの IPアドレスなどに応じて環境変数 VSFTPD_LOAD_CONF をセットし、別々の設定ファイルを読み込ませることだ。ただし、この手はスタンドアローンモード (listen=YES) の時には使えない。具体的な手順は 起動方法のいろいろ で述べる。

拒否リストftpusersファイルの素早い作り方

まず作成以前に、このファイルを拒否リストとして使う場合、 RedHat 系のデフォルト名である ftpusers は混乱の元である。vsftpd.confuserlist_file パラメータで users_deny など分かりやすい名前に変えよう。もちろん、それに伴って /etc/pam.d/vsftpd の中でのファイル定義名も変えなければいけない。

さて本題だが、拒否リストに連ねるユーザは、実際に FTP 接続を要求するログインユーザ以外の全部だ。運用が local ユーザ用にしろアノニマス用にしろその両方にしろ、 vsftpd が機能上必要とするユーザ (ftpftpnobody) も拒否リストに加えてしまって大丈夫だ。つまり、 /etc/passwd ファイルに書かれているユーザのほとんど全部ということになり、ファイルをコピーしてからチマチマと編集するのもまどろっこしい。以下のコマンドなら、ほぼ一発;

root# awk '{ match($0, /^[^:]+/, str); print str[0] }' \
      /etc/passwd >/etc/vsftpd/users_deny

あとは、数少ない実効ユーザを取り除くだけだ。

nopriv_userの作成例

ディレクティブ nopriv_user で使用するユーザは、プロセスを分離するだけの役割しか持たないので、利便性を徹底的に剥奪してしまおう。 ID は 1024 以上、シェルは偽シェル、自身のホームディレクトリにさえ入れなくして構わない。例として ftpsecure というユーザを作成する手順を示そう。

root# groupadd -g 1025 ftpsecure
root# useradd -u 1025 -g ftpsecure -d /var/ftpsecure -s /sbin/nologin \
  -m -k /dev/null ftpsecure
root# chown root:root /var/ftpsecure

chroot動作の留意点

chroot_local_userについて

UNIXユーザには通常の運用ユーザの他に、重要な機能ユーザ (例えば postgres とか named など) も存在するわけで、誰かれ構わず各ホームディレクトリへ導こうというのは危険だ。 chroot_list_file での指定漏れが重大なセキュリティリスクにつながりかねない。 telnet や ssh とは違い、ユーザのシェルが /sbin/nologin といった偽シェルになっていたとしても、それが /etc/shells に正当なものとして書いてある限り、 FTP は成立してしまう。ただし、(少なくとも Fedora Core 4 では) ユーザの指定シェルが /bin/false であれば、 /etc/shells に書いてないので FTP ログインも不可能となる。

jail空間に用意しなければならないファイル

vsftpd によって行われる chroot も通常の chroot と同じで、セッションの中のユーザは、chroot先のルート (/) ディレクトリより上層や外のファイルにはアクセス不能となる。もっと的確な言い方をすれば、外の世界は存在しないことになる。外のファイルを指しているシンボリックリンクも当然、リンク先へ到達することはできない。 [ chroot についてもう少し知りたい人は本稿の chroot jail のページを読んで頂くといいかもしれない。]

これによって、本来、機能的になくてはならないファイルも幾つかアクセスできなくなるので、あらかじめ jail 内に作っておく必要がある。以下のファイルだ;

※ FTP ログを syslog 経由で出力している場合: syslog ソケットも必要かと思ったが、作らなくてもログはきちんと出力された。

ユーザは、故意にしろ偶然にしろ、何をしでかすか分からないと考えるのが正解。よって、これらのファイルも、それを置く JAIL_ROOT/etc ディレクトリも、 root に所有させ、ユーザには書き込めないパーミションにしておこう。 hoge というユーザを chroot する場合の例を示す;

root# mkdir /home/hoge/etc
root# cp /etc/localtime /etc/passwd /etc/group /home/hoge/etc
root# chown -R root:root /home/hoge/etc
root# chmod 755 /home/hoge/etc
root# chmod 644 /home/hoge/etc/*

慎重を期すなら、passwdgroup ファイルはコピーしたそのままでなく、最低限のリストにまで絞っておこう。通常必要なのは、当のユーザの他に、 root, ftp くらい。ディレクトリとユーザの用途によっては apache, nobody, postgres といったユーザも必要な場合がある。 ftpnobody は裏方に徹していて表には出てこないため passwdgroup には必要ない。

なお、 vsftpd はアノニマスログイン時にも chroot 動作を行うので、アノニマス用の chroot ディレクトリにも同様の細工が必要だ。標準では /var/ftp/etc を整備することになる。

起動方法のいろいろ

主設定ファイルの listen ディレクティブが YES か NO かで、 vsftpd の動作は大きくふたつに分かれる。

スタンドアローンモード

listen=YES の状態がスタンドアローンモード。この動作では、 FTP コントロールポート (通常 21、listen_port で変更も可能) が vsftpd の主プロセスにバインドされ、ポートへの接続要求は vsftpd の親プロセスが交通整理を受け持つ。一時に張れるセッション数 (max_clients) の制限も親プロセスの仕事となる。ただ、 FTP セッション毎に nopriv_user 権限の子プロセスとログインユーザ (アノニマスでは ftp_username) 権限の子プロセスが生まれるという構図は、スタンドアローンモードでも非スタンドアローンモードでも同じ。 RedHat 系のバイナリパッケージではこちらの方式の init スクリプトが標準なので、特に悩むことはないだろう。

tcpserver経由での起動

xinetd 経由や tcpserver 経由で稼働させる時には、vsftpdlisten=NO の非スタンドアローンモードに設定しなければならない。 tcpserver 経由の場合はさらに、機能がかぶってトラブルの元になるので tcp_wrappers ディレクティブを NO にするのも忘れてはいけない。

動作実証済みの tcpserver 用 init スクリプト を置いておく。そのまま使うには tcpserverのページで紹介している detectpid という小スクリプトと、 qmail に付属している splogger が必要 (標準の logger でも代用可能だが引数が異なる)。

tcpserver に接続規制の指示を与える cdb データベースも用意しなくてはいけない。上記のスタートアップスクリプト例では /etc/service/tcp.ftp.cdb を指定している。まずテキストファイルに書いてから tcprules コマンドで cdb に変換するのだが、手順は tcpserverのページを参照して頂くとしよう。例えば、サーバ自身のループバックアドレスと 192.168.0.0/24 のサブネットからの接続だけを受け入れるなら、ルールに下のように書く;

127.0.0.1:allow
192.168.0.:allow
:deny

また、クライアントのアドレスに応じて vsftpd の読み込む設定ファイルを変えることもできる。 vsftpd はプロセス毎に環境変数 VSFTPD_LOAD_CONF にセットされている主設定ファイルを読み込む。例えば;

127.0.0.1:allow
192.168.0.:allow
:allow,VSFTPD_LOAD_CONF="/etc/vsftpd/vsftpd.conf.anon"

とすれば、vsftpd はサーバ自身と LAN 内からの要求では標準の設定ファイルをロードし、インターネットからやってくる接続要求に対しては vsftpd.conf.anon に書かれた設定で動作する。標準の設定ファイルでは local_enable=YES, anonymous_enable=NO、 かたや vsftpd.conf.anon では local_enable=NO, anonymous_enable=YES としておけば、ローカルログインとアノニマスログインを安全に両立できるわけだ。他にも、旧式の FTP クライアントアプリとの互換性問題を回避したり、或るホストには特別に厳しい規制を掛けるなど、利用法は使い手次第で広がる。切り替えて使うどの vsftpd 設定ファイルも、他の設定ファイルにマージされるわけではないので、必要なディレクティブはそれぞれの設定ファイルに全て書かなければならない。

このようにして設定ファイルを複数用意しておく場合、ファイルの命名法に注意したほうがいい。というのも、 RedHat 系の RPM パッケージでインストールされる vsftpd init スクリプトは、起動時に /etc/vsftpd/ に存在する *.conf を次々に読み込む仕組みになっているからだ。サブ的な設定ファイルは例えば vsftpd.anon.conf でなく vsftpd.conf.anon という風にしておかないと、おかしなことになる。

xinetd経由での起動

最近の RedHat系ディストリビューションでは、標準では xinetd パッケージはインストールされない。 OSのインストール時に xinetd を含めるには、パッケージ選択時に `レガシーなサーバ' にチェックを付ける必要がある。いずれにしろ xinetd 経由はあまりお勧めできる方法ではないので、詳しくは述べないが、vsftpd の動作の仕組みは基本的に tcpserver 経由の場合と同様。 xinetd 定義ファイルはソースの /EXAPMLE/INTERNET_SITE//xinet.d/ にあるものが参考になる。

tcpserver の cdb のように接続ルールを設けるには、 vsftpd 設定ディレクティブの tcp_wrappers を YES にした上で、 /etc/hosts.deny/etc/hosts.allow にルールを書く。通常はほとんど拒否なポリシーが妥当なので、 hosts.deny は;

vsftpd: ALL

として全拒否にしておき、 hosts.allow の方に、許可したいアドレスだけを書く。 tcpserver経由の起動方法で紹介したのと全く同様の vsftpd 設定ファイル切り替えを実現したいとすると hosts.allow は下のようになる;

vsftpd: 127.0.0.1, 192.168.0.
vsftpd: ALL: setenv VSFTPD_LOAD_CONF /etc/vsftpd/vsftpd.conf.anon

hosts.denyhosts.allow の記述法は man 5 hosts_accessman hosts_options で詳しく見ることができる。

ファイヤーウォールとFTP

FTP は、最初に FTP_CONTROL ポートで接続を 1本確立してから、別の FTP_DATA ポートで実際のデータを送受信するという特殊なプロトコルだ。ちゃんと設定したつもりなのにどうしてもつながらない時には、サーバ自身のファイヤーウォールの設定も疑ってみよう。 iptables が有効になっている場合は、 ip_conntrack_ftp というカーネルモジュールをロードすることによって、 FTP コントロールセッションのペイロード内で遣り取りされるポート決めの話し合いが読み取れるようにしなければならない。詳しくは iptables のページをご覧いただきたい。