incident発端
動作確認済みのvsftpdが、ある日ログインできなくなった
新VPSへの一連の移設作業が完了し、vsftpdもFileZilla・スマートフォンアプリの両方で
FTPS接続を確認済みだった。ところが、その後に進めていたMongoDBまわりの作業――
PHPのPECL拡張をビルドするためにlibssl-devを導入したところ、
依存関係でOpenSSL本体がセキュリティパッチ版に更新された――を境に、
FileZilla・スマホアプリともFTPSでログインできなくなった。
vsftpd自体の設定は変更していない。時系列だけを見れば、OpenSSLの更新が引き金になった 可能性がかなり濃厚に見えるが、これはあくまで状況証拠であり、断定はできない。
decision判断
vsftpdの延命ではなく、Pure-FTPdへの乗り換えを選んだ理由
- SFTP一本化では、鍵認証に対応していないスマホ・タブレットアプリが使えなくなる
- 原因がOpenSSL側の仕様変更だとすると、vsftpdを直しても再発する可能性が残る
- vsftpd自体、開発がほぼ停止していると言われており、今後同様の非互換が起きても 迅速な追従は期待しにくい
乗り換え後もOpenSSLに起因する問題であれば同じ症状が出る可能性はゼロではない。 それでも、待つより動く方を選んだ。
notePure-FTPdの特徴(vsftpdとの主な違い)
- 設定は単一confファイルではなく、
/etc/pure-ftpd/conf/配下に 「1オプション1ファイル」を置く独特の方式 - TLS証明書は秘密鍵・証明書・チェーンを1本のPEMに結合した
/etc/ssl/private/pure-ftpd.pemを読む仕様 - 1.0.45以降、TLSv1.0は標準で拒否される
ChrootEveryoneでユーザーを標準でホームディレクトリにchroot- 癖のある古いFTPクライアントとの互換性維持を重視した設計思想があり、 雑多なスマホ・タブレットアプリとの相性は期待しやすい
setupアンインストール〜インストール
vsftpdを退場させる
# 念のため現行confを退避(TLS/ポート設定の見比べ用)
sudo cp /etc/vsftpd.conf ~/vsftpd.conf.bak.$(date +%Y%m%d)
sudo systemctl stop vsftpd
sudo systemctl disable vsftpd
sudo apt purge vsftpd
sudo apt autoremove
Let's Encrypt証明書はvsftpdとは独立管理なのでそのまま残る。ufwの21番ポート許可ルールも そのまま流用できる(パッシブポート分は別途追加)。
Pure-FTPdを迎える
sudo apt update
sudo apt install pure-ftpd
sudo systemctl status pure-ftpd # standaloneで起動しているか確認
setup設定
TLSを必須化する
echo 2 | sudo tee /etc/pure-ftpd/conf/TLS
2は制御・データ両チャンネルで平文FTPを拒否する設定
(vsftpdのforce_local_logins_ssl/force_local_data_ssl相当)。
TLSv1.0は1.0.45以降デフォルトで拒否されるため、個別設定は不要だった。
証明書はvsftpdと勝手が違う
pure-ftpdは秘密鍵と証明書を1本のPEMに結合した
/etc/ssl/private/pure-ftpd.pemを既定で読む。vsftpdのように
鍵と証明書を別々のパスで指定する方式ではない。
sudo bash -c 'cat /etc/letsencrypt/live/<ドメイン>/fullchain.pem \
/etc/letsencrypt/live/<ドメイン>/privkey.pem > /etc/ssl/private/pure-ftpd.pem'
sudo chmod 600 /etc/ssl/private/pure-ftpd.pem
/etc/letsencrypt/renewal-hooks/deploy/に用意しておかないと、
次回更新時にFTPSが静かに切れる。
その他の基本設定
# ホームディレクトリへのchroot
echo yes | sudo tee /etc/pure-ftpd/conf/ChrootEveryone
# パッシブポート範囲(NAT/ファイアウォール・モバイルアプリ対策)
echo "60000 60100" | sudo tee /etc/pure-ftpd/conf/PassivePortRange
sudo ufw allow 60000:60100/tcp
# 癖のあるクライアントとの互換性
echo yes | sudo tee /etc/pure-ftpd/conf/BrokenClientsCompatibility
# 匿名ログイン無効化
echo yes | sudo tee /etc/pure-ftpd/conf/NoAnonymous
sudo systemctl restart pure-ftpd
ポート21自体やIPスコープの絞り込みは、これまで通りpure-ftpd側ではなくufw側で 対応する方針を踏襲した。
gotchaハマりどころ
暗号スイート指定で event not found
暗号スイートを絞ろうとして、こう打った。
echo "HIGH:!aNULL:!MD5:!3DES" | sudo tee /etc/pure-ftpd/conf/TLSCipherSuite
-bash: !aNULL: event not found
bashのヒストリー展開機能が、ダブルクォート内の!を
「イベント参照」として解釈してしまったのが原因。ダブルクォートは
この展開を止めてくれない。シングルクォートに変えれば!は
リテラル扱いになり、そのまま通る。
echo 'HIGH:!aNULL:!MD5:!3DES' | sudo tee /etc/pure-ftpd/conf/TLSCipherSuite
バックスラッシュでのエスケープ(\!)は効かない点も覚えておきたい
(\ごと残ってしまう)。!を含む文字列を扱うときは、
基本的にシングルクォートを使うのが安全。
result結果
接続テストは一発でOK
設定後、FileZilla・スマートフォン/タブレットアプリともにFTPS接続テストは すんなり通った。SFTP鍵認証に対応していないシンプルな機能のFTPアプリからも、 問題なくサーバーのファイルにアクセスできる状態になった。