TL;DR
- Pure-FTPd はセキュリティを第1に考えているとのこと
- Pure-FTPd ではアトミックなアップロードを実現できる
- vsftpd, ProFTPD で実現可能か把握できていない(デフォルト設定では不可)
- アップロード成功時に後処理を実行させる事ができる
- MySQL や PostgreSQL を使ってアカウントを管理する事ができる
- chroot 先ディレクトリをログイン時に自動で作る機能があるので、アカウント追加作業はレコード作成だけで良い
Pure-FTPd とは
Security First が合い言葉(?)な FTPd です。
Pure-FTPd is a free (BSD), secure, production-quality and standard-conformant FTP server. It doesn't provide useless bells and whistles, but focuses on efficiency and ease of use. It provides simple answers to common needs, plus unique useful features for personal users as well as hosting providers.
Pure-FTPd - About
CVE への報告数も最小だとか。
何ができるのか
機能リストをざっと見た感じではありますが、FTPd に望まれることは大抵できるでしょう。FTPd の運用経験が豊富なわけではありませんが…。
以下、機能リスト(Pure-FTPd - About)から一部を抜粋しました。
- Linux カウントを FTP アカウントとして利用できる(指定 UID 未満の利用を禁止という設定も可)
- 認証を PAM 経由で行える
- デフォルトで chroot する
- FTP アカウントは Linux アカウントではない、独立したデータベースで管理されたものにできる(バーチャルユーザ)
- LDAP サポート
- 複数の認証方式を好みの順序でチェインさせることができる
- quota サポート
- 帯域制限サポート
- ユーザ単位での細かな制限
- アップロード成功後にシェルスクリプトを実行させることができる
- ホームディレクトリを on-demand に作成することができる
- 同一ホストで複数のバーチャル FTP サーバを運用できる
- chroot 外へのシンボリックリンクをフォローする
- アップロードは atomic
CentOS7 の VM を準備
いくつかの動作検証のために CentOS 7 の VM を用意します。
CentOS である必要性はありません。
※ 以後のコマンド実行記録にて SELinux に関する話は曖昧にさせていただきます(Permissive だった、とだけ言っておきましょう)。
Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
# config.ssh.insert_key = false
config.vm.box = "bento/centos-7.1"
config.vm.define 'pure-ftpd' do |c|
c.vm.network "private_network", ip: "192.168.33.10"
c.vm.hostname = 'pure-ftpd'
end
config.vm.define 'pro-ftpd' do |c|
c.vm.network "private_network", ip: "192.168.33.11"
c.vm.hostname = 'pro-ftpd'
end
config.vm.define 'vsftpd' do |c|
c.vm.network "private_network", ip: "192.168.33.12"
c.vm.hostname = 'vsftpd'
end
end
Atomic なアップロード?
アップロード用に大きめの Sparse file を作成しておきます。
% dd if=/dev/zero of=./hoge.dat seek=1000 bs=1048576 count=1
vsftpd は?
vsftpd を最小の手間でセットアップしました。
[vagrant@vsftpd]$ sudo yum install vsftpd
[vagrant@vsftpd]$ sudo systemctl start vsftpd
[vagrant@vsftpd]$ sudo systemctl enable vsftpd
アップロードします。
% ftp vagrant@192.168.33.12
> put ./hoge.dat
アップロード途中のファイルが作られる事を確認しました。
[vagrant@vsftpd ~]$ ll
合計 131008
-rw-r--r--. 1 vagrant vagrant 85738976 10月 9 17:41 hoge.dat
[vagrant@vsftpd ~]$ ll
合計 524224
-rw-r--r--. 1 vagrant vagrant 515505800 10月 9 17:41 hoge.dat
[vagrant@vsftpd ~]$ ll
合計 1025024
-rw-r--r--. 1 vagrant vagrant 1049624576 10月 9 17:41 hoge.dat
アップロード途中のファイルへ適当な出力をリダイレクトしても上書きされない事を確認しました。また、 rm
コマンドでファイルを削除できる事を確認しました。
オプション lock_upload_files
のデフォルト値が YES
なので、アップロード途中のファイルは write ロックされる様です。
ざっと設定値の一覧を確認した限りでは、Atomic なアップロードには対応していない様に見えます。
ProFTPD
最小の手間でセットアップしました。
[vagrant@pro-ftpd]$ sudo yum install epel-release
[vagrant@pro-ftpd]$ sudo yum install proftpd
[vagrant@pro-ftpd]$ sudo systemctl start proftpd
[vagrant@pro-ftpd]$ sudo systemctl enable proftpd
アップロードします。
% ftp vagrant@192.168.33.11
> put ./hoge.dat
アップロード途中のファイルが作られる事を確認しました。
[vagrant@pro-ftpd ~]$ ll
合計 524224
-rw-r--r--. 1 vagrant vagrant 290361320 10月 9 17:54 hoge.dat
[vagrant@pro-ftpd ~]$ ll
合計 1048512
-rw-r--r--. 1 vagrant vagrant 586870048 10月 9 17:54 hoge.dat
[vagrant@pro-ftpd ~]$ ll
合計 1025024
-rw-r--r--. 1 vagrant vagrant 1049624576 10月 9 17:55 hoge.dat
アップロード途中のファイルへ適当な出力をリダイレクトしても上書きされない事を確認しました。また、 rm
コマンドでファイルを削除できる事を確認しました。
ざっと設定値の一覧を確認した限りでは、Atomic なアップロードには対応していない様に見えます。
Pure-FTPd
最小の手間でセットアップしました。
$ sudo yum install epel-release
$ sudo yum install pure-ftpd pure-ftpd-selinux
$ sudo systemctl start pure-ftpd
$ sudo systemctl enable pure-ftpd
アップロードします。
% ftp vagrant@192.168.33.10
> put ./hoge.dat
アップロード途中のファイルが作られる事を確認しました。
[vagrant@pure-ftpd ~]$ ll
合計 262080
-rw-r--r--. 1 vagrant vagrant 204178848 10月 9 17:59 hoge.dat
[vagrant@pure-ftpd ~]$ ll
合計 1048512
-rw-r--r--. 1 vagrant vagrant 611263808 10月 9 17:59 hoge.dat
[vagrant@pure-ftpd ~]$ ll
合計 1025024
-rw-r--r--. 1 vagrant vagrant 1049624576 10月 9 17:59 hoge.dat
Atomic なアップロードはオプションの様なので、設定を変更して再度テストします。
[vagrant@pure-ftpd ~]$ sudo sed -i.orig -e 's/# NoTruncate/NoTruncate/' /etc/pure-ftpd/pure-ftpd.conf
[vagrant@pure-ftpd ~]$ diff -u /etc/pure-ftpd/pure-ftpd.conf.orig /etc/pure-ftpd/pure-ftpd.conf
[vagrant@pure-ftpd ~]$ sudo systemctl stop pure-ftpd
[vagrant@pure-ftpd ~]$ sudo systemctl start pure-ftpd
[vagrant@pure-ftpd ~]$ rm ~/hoge.dat
改めてアップロードします。
% ftp vagrant@192.168.33.10
> put ./hoge.dat
アップロードが完了してからファイルが作られました。
[vagrant@pure-ftpd ~]$ ll
合計 0
[vagrant@pure-ftpd ~]$ ll
合計 1025024
-rw-r--r--. 1 vagrant vagrant 1049624576 10月 9 18:19 hoge.dat
Pure-FTPd でもデフォルトはアップロード途中のファイルが作られる様になっている事が分かりました。
Pure-FTPd のアップロード後処理
アップロード後に任意の処理を実行させるための設定 CallUploadScript
です。
アップロード後の処理を実行するためには、 CallUploadScript
を yes
にした上で FTPd である pure-ftpd
とは別のプロセスである pure-uploadscript
と呼ばれるデーモンを稼働させる必要があります。
pure-uploadscript
デーモンは、起動時に指定された実行ファイルの第1引数にアップロードファイルのパスを与えます。
また、以下の様な環境変数も使える様にします。
UPLOAD_SIZE : the size of the file, in bytes.
UPLOAD_PERMS : the permissions, as an octal value.
UPLOAD_UID : the uid of the owner.
UPLOAD_GID : the group the file belongs to.
UPLOAD_USER : the name of the owner.
UPLOAD_GROUP : the group name the file belongs to.
UPLOAD_VUSER : the full user name, or the virtual user name. (127 chars max)
pure-uploadscript
の詳細については man pure-uploadscript
や download.pureftpd.org/pub/pure-ftpd/doc/README を参照してください。
後処理スクリプト
アップロード後に実行するスクリプトを書きます。
今回は、動く事の確認だけしたいので、入力をプリントするだけのスクリプトとします。
[vagrant@pure-ftpd ~]$ cat <<\SCRIPT > /home/vagrant/pure-uploadscript.sh
# !/bin/sh
cat <<EOF >> /home/vagrant/pure-uploadscript.log
\$1 = $1
UPLOAD_SIZE = $UPLOAD_SIZE
UPLOAD_PERMS = $UPLOAD_PERMS
UPLOAD_UID = $UPLOAD_UID
UPLOAD_GID = $UPLOAD_GID
UPLOAD_USER = $UPLOAD_USER
UPLOAD_GROUP = $UPLOAD_GROUP
UPLOAD_VUSER = $UPLOAD_VUSER
EOF
SCRIPT
[vagrant@pure-ftpd ~]$ chmod 0755 /home/vagrant/pure-uploadscript.sh
pure-uploadscript
pure-uploadscript
を起動します。
実行には root 権限が必要です。
[vagrant@pure-ftpd]$ sudo pure-uploadscript -r /home/vagrant/pure-uploadscript.sh
補足
ドキュメントに書かれていた以下の文を、「先に pure-ftpd
を起動して、その後で pure-uploadscript
を動かさないとだめよ。」と読んでその順序で試みたのですが、 systemctl start pure-ftpd
がタイムアウトしてしまいまいた。
YOU MUST START PURE-FTPD FIRST and THEN START PURE-UPLOADSCRIPT. THE REVERSE ORDER WON'T WORK.
英語が読めないので、上手くいった逆順の方で動作確認しています。
pure-ftpd
設定 CallUploadScript
を有効にします。
[vagrant@pure-ftpd ~]$ sudo sed -i.notruncate -e 's/#CallUploadScript/CallUploadScript/' /etc/pure-ftpd/pure-ftpd.conf
[vagrant@pure-ftpd ~]$ sudo systemctl stop pure-ftpd
[vagrant@pure-ftpd ~]$ sudo systemctl start pure-ftpd
[vagrant@pure-ftpd ~]$ ls -l /var/run/pure-*
pure-uploadscript
を利用する際は CustomerProof
を yes
としておく事が強く推奨されています(デフォルトの設定ファイルで有効になっていました)。
CustomerProof
を有効にすることで、ユーザによる不用意な(誤った) chmod
の実行を防ぎます。
アップロード
% ftp vagrant@192.168.33.10
> put Vagrantfile
[vagrant@pure-ftpd]$ cat pure-uploadscript.log
$1 = /home/vagrant/Vagrantfile
UPLOAD_SIZE = 541
UPLOAD_PERMS = 644
UPLOAD_UID = 1000
UPLOAD_GID = 1000
UPLOAD_USER = vagrant
UPLOAD_GROUP = vagrant
UPLOAD_VUSER = vagrant
PureFTPd でバーチャルユーザ
PostgreSQL を使って FTP アカウントを作成してみます。
PostgreSQL のセットアップ
インストール
[vagrant@pure-ftpd]$ sudo yum install postgresql postgresql-server
[vagrant@pure-ftpd]$ sudo postgresql-setup initdb
[vagrant@pure-ftpd]$ sudo systemctl start postgresql
[vagrant@pure-ftpd]$ sudo systemctl enable postgresql
データベース作成
=# CREATE DATABASE pureftpd;
テーブル作成
=# \c pureftpd;
=# CREATE TABLE "users" (
(# "User" TEXT NOT NULL,
(# "Password" TEXT NOT NULL,
(# "Uid" INTEGER NOT NULL default '-1',
(# "Gid" INTEGER NOT NULL default '-1',
(# "Dir" TEXT NOT NULL,
(# PRIMARY KEY ("User")
(# ) WITHOUT OIDS;
ついでにアカウントを登録しておきます。
=# INSERT INTO users VALUES ('user', 'password', 1000, 1000, '/home/vagrant/user');
接続ユーザ
=# CREATE USER pureftpd PASSWORD 'password';
=# GRANT SELECT ON users TO pureftpd;
接続設定
PostgreSQL のデフォルト設定でローカル接続が制限されているため、任意のユーザ/パスワードで接続できる様にします。
[vagrant@pure-ftpd ~]$ sudo cp /var/lib/pgsql/data/pg_hba.conf /var/lib/pgsql/data/pg_hba.conf.orig
[vagrant@pure-ftpd ~]$ sudo vi /var/lib/pgsql/data/pg_hba.conf
[vagrant@pure-ftpd ~]$ sudo diff -u /var/lib/pgsql/data/pg_hba.conf.orig /var/lib/pgsql/data/pg_hba.conf
--- /var/lib/pgsql/data/pg_hba.conf.orig 2015-10-10 08:43:26.851201925 +0000
+++ /var/lib/pgsql/data/pg_hba.conf 2015-10-10 08:46:53.410201925 +0000
@@ -77,6 +77,7 @@
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
+local pureftpd pureftpd password
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 ident
[vagrant@pure-ftpd ~]$ sudo systemctl reload postgresql
[vagrant@pure-ftpd ~]$ psql -U pureftpd -d pureftpd
ユーザ pureftpd のパスワード:
psql (9.2.13)
"help" でヘルプを表示します.
pureftpd=> SELECT * FROM users;
User | Password | Uid | Gid | Dir
------+----------+-----+-----+-----
(0 行)
これだと、 -h localhost -p 5432
を付けたときに接続できないのでもう一つ設定を加えます。
[vagrant@pure-ftpd ~]$ sudo cp /var/lib/pgsql/data/pg_hba.conf /var/lib/pgsql/data/pg_hba.conf.local-pureftpd
[vagrant@pure-ftpd ~]$ sudo vi /var/lib/pgsql/data/pg_hba.conf
[vagrant@pure-ftpd ~]$ sudo diff -u /var/lib/pgsql/data/pg_hba.conf.local-pureftpd /var/lib/pgsql/data/pg_hba.conf
--- /var/lib/pgsql/data/pg_hba.conf.local-pureftpd 2015-10-10 08:49:27.841201925 +0000
+++ /var/lib/pgsql/data/pg_hba.conf 2015-10-10 08:52:27.383201925 +0000
@@ -80,8 +80,10 @@
local pureftpd pureftpd password
local all all peer
# IPv4 local connections:
+host pureftpd pureftpd 127.0.0.1/32 password
host all all 127.0.0.1/32 ident
# IPv6 local connections:
+host pureftpd pureftpd ::1/128 password
host all all ::1/128 ident
# Allow replication connections from localhost, by a user with the
# replication privilege.
[vagrant@pure-ftpd ~]$ sudo systemctl reload postgresql
[vagrant@pure-ftpd ~]$ psql -U pureftpd -d pureftpd -h localhost -p 5432
ユーザ pureftpd のパスワード:
psql (9.2.13)
"help" でヘルプを表示します.
pureftpd=>
設定
デフォルトで作成されている PostgreSQL 用の設定ファイルを見てみましょう。
[vagrant@pure-ftpd ~]$ grep '^PGSQL' /etc/pure-ftpd/pureftpd-pgsql.conf
PGSQLServer localhost
PGSQLPort 5432
PGSQLUser postgres
PGSQLPassword rootpw
PGSQLDatabase pureftpd
PGSQLCrypt cleartext
PGSQLGetPW SELECT Password FROM users WHERE User='\L'
PGSQLGetUID SELECT Uid FROM users WHERE User='\L'
PGSQLGetGID SELECT Gid FROM users WHERE User='\L'
PGSQLGetDir SELECT Dir FROM users WHERE User='\L'
この設定ファイルはデフォルトだと pure-ftpd
の設定ファイルからは読み込まれていません。
[vagrant@pure-ftpd ~]$ grep 'PG' /etc/pure-ftpd/pure-ftpd.conf
# Postgres configuration file (see README.PGSQL)
# PGSQLConfigFile /etc/pure-ftpd/pureftpd-pgsql.conf
今回のケースにあわせて設定を変更し反映させましょう。
[vagrant@pure-ftpd ~]$ sudo cp /etc/pure-ftpd/pureftpd-pgsql.conf /etc/pure-ftpd/pureftpd-pgsql.conf.orig
[vagrant@pure-ftpd ~]$ sudo vi /etc/pure-ftpd/pureftpd-pgsql.conf
[vagrant@pure-ftpd ~]$ sudo diff /etc/pure-ftpd/pureftpd-pgsql.conf.orig /etc/pure-ftpd/pureftpd-pgsql.conf
19c19
< PGSQLUser postgres
---
> PGSQLUser pureftpd
22c22
< PGSQLPassword rootpw
---
> PGSQLPassword password
46c46
< PGSQLGetPW SELECT Password FROM users WHERE User='\L'
---
> PGSQLGetPW SELECT "Password" FROM users WHERE "User"='\L'
51c51
< PGSQLGetUID SELECT Uid FROM users WHERE User='\L'
---
> PGSQLGetUID SELECT "Uid" FROM users WHERE "User"='\L'
56c56
< #PGSQLDefaultUID 1000
---
> PGSQLDefaultUID 1000
61c61
< PGSQLGetGID SELECT Gid FROM users WHERE User='\L'
---
> PGSQLGetGID SELECT "Gid" FROM users WHERE "User"='\L'
66c66
< #PGSQLDefaultGID 1000
---
> PGSQLDefaultGID 1000
71c71
< PGSQLGetDir SELECT Dir FROM users WHERE User='\L'
---
> PGSQLGetDir SELECT "Dir" FROM users WHERE "User"='\L'
PostgreSQL 認証を使える様に設定を変更します。
[vagrant@pure-ftpd ~]$ sudo cp /etc/pure-ftpd/pure-ftpd.conf /etc/pure-ftpd/pure-ftpd.conf.uploadscript
[vagrant@pure-ftpd ~]$ sudo vi /etc/pure-ftpd/pure-ftpd.conf
[vagrant@pure-ftpd ~]$ sudo diff /etc/pure-ftpd/pure-ftpd.conf.uploadscript /etc/pure-ftpd/pure-ftpd.conf
121c121
< # PGSQLConfigFile /etc/pure-ftpd/pureftpd-pgsql.conf
---
> PGSQLConfigFile /etc/pure-ftpd/pureftpd-pgsql.conf
137c137
< PAMAuthentication yes
---
> PAMAuthentication no
143c143
< # UnixAuthentication yes
---
> UnixAuthentication no
343c343
< #CreateHomeDir yes
---
> CreateHomeDir yes
[vagrant@pure-ftpd ~]$ sudo systemctl stop pure-ftpd
[vagrant@pure-ftpd ~]$ sudo systemctl start pure-ftpd
PostgreSQL と直接関係ありませんが、レコード挿入だけでログインできる様にするため、ディレクトリはログイン時に作成する様に CreateHomeDir
を yes
としています。
ログイン
ログインに成功し、ホームディレクトリも自動的に作成されました。
% ftp user@192.168.33.10
Connected to 192.168.33.10.
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 1 of 50 allowed.
220-Local time is now 09:46. Server port: 21.
220-IPv6 connections are also welcome on this server.
220 You will be disconnected after 15 minutes of inactivity.
331 User user OK. Password required
Password:
230 OK. Current restricted directory is /
Remote system type is UNIX.
Using binary mode to transfer files.
まとめ
Pure-FTPd の特徴的なだなと感じた機能を試しました。
相変わらずのアサチャプ(浅瀬でチャプチャプ)メソッドなので、本当に完全なアトミックなアップロードが可能なのか、実は vsftpd や ProFTPD でも可能なのではないか、などは深掘りできていません。
また、アップロード後処理についても、これを利用して運用を安定させるための工夫など、検証が足りていません。
PostgreSQL 認証についても、UID, GID, パスワード暗号化などは適当です。
色々とやり残しはありますが、かなり良い感触をつかむことができたのでかなり満足しています。
実は他と比べた Pure-FTPd の致命的なデメリットがあるのかもしれませんが、個人的(直近で見えている用途的)にはかなりの高評価です。