DockerとPythonでFTPサーバーを構築し接続する方法【匿名ユーザー対応】
はじめに
FTPサーバーをコンテナで起動し、Pythonで接続する方法を習得しました。
匿名ユーザーでの利用を想定し、環境構築をする中でいくつか躓きポイントがあったので
共有します。本記事が皆さんの参考になりますと幸いです。
想定読者🧑🦲
- FTPとは何か知りたい方
- FTPサーバーを簡単に立てて、Pythonで利用したい方
- FTPを匿名ユーザで利用する方法が知りたい方
FTPとは
「File Transfer Protocol」の名前通り、ファイルを転送するプロトコルです。
あるコンピュータから別のコンピュータへファイルを送る・受け取る際に利用します。
FTPクライアントはサーバーへ要求をし、FTPサーバーは要求に対して答えます。
このクライアントとサーバーの2点を押さえておけば、理解が進みます。
例としてクライアントがAファイルの内容を知りたいと要求したら、サーバーはAファイルを
転送します。

FTPクライアントとサーバーの接続にはアクティブモードとパッシブモードの二種類があります。
さらにやり取りをするために、制御用とデータ転送用の2種類のコネクションを確立します。
順にそれぞれ整理していきます。
アクティブモード
サーバ側からデータ転送用の線をクライアントに繋いで接続する方式。21番ポートが制御に使用されることが多いです。
サーバーからクライアントに繋ぐため、ファイアウォールが働いて繋がらないことがあります。

パッシブモード
ファイアウォール問題を解決するために生まれた手法で、クライアント側から制御、データ転送用両方の接続をします。

おさらい
| 接続方式 | 制御用の接続 | データ転送用の接続 | 特徴 |
|---|---|---|---|
| アクティブモード | クライアント → サーバー | サーバー → クライアント | クライアント側のファイアウォールでブロックされることがある |
| パッシブモード | クライアント → サーバー | クライアント → サーバー | ファイアウォールの問題を回避 |
今回はコンテナ環境での設定を容易にするためパッシブモードを利用します。
認証について
FTPサーバーにアクセスするには認証が必要です。
大別すると通常ユーザーと匿名ユーザーがあります。
| 認証方式 | 概要 | 主な用途・注意点 |
|---|---|---|
| 通常ユーザー😊 | IDとパスワードで認証する方式 |
特定のメンバー間でのファイル共有に利用。 一定のセキュリティが担保。 |
| 匿名ユーザー😶🌫️ | 認証なしで誰でもアクセスできる仕組み |
不特定多数へのファイル配布などに利用。 誰でもアクセスできるためセキュリティリスクがある。 |
注意点
このサンプルでは通信が暗号化されないため、ローカル環境での実行が前提ということを
ご留意ください。暗号化が必要な場合はFTPS/SFTPなどの利用を検討してください。
また、匿名ユーザーを許可すると誰でもアクセス出来てしまうため、本番環境での利用は慎重に検討する必要があります。
コンテナでFTPサーバーを構築・操作する(実践)
システム構成
今回は図の環境で学習をしました。

FTPサーバーの立て方
記事を参考にdocker-composeファイルを作成し、コンテナを起動します。
今回使用したFTPサーバーのイメージでは匿名ログインを許可するか、しないかのオプションが
ありました。
下記に匿名モードのサンプルコードを記載します。
ftpd_server:
image: stilliard/pure-ftpd
ports:
- "21:21"
- "30000-30009:30000-30009"
volumes:
- "./data:/home/testuser"
- "./passwd:/etc/pure-ftpd/passwd"
environment:
PUBLICHOST: "localhost"
FTP_USER_NAME: testuser
FTP_USER_PASS: test123
FTP_USER_HOME: /home/testuser
ADDED_FLAGS: -e # 匿名ユーザーを許可するオプション
コンテナ起動後、ftpユーザーを追加し、ホームフォルダを作成します。この手順が抜けているとサーバーにログインが出来ません。
useradd -d /var/ftp -s /sbin/nologin ftp
mkdir /var/ftp
FTPサーバーへの接続方法
匿名ユーザーでFTPサーバーへ接続する場合、以下情報が必要です。
- 接続先のホスト名(IPアドレス)
- ポート番号
※通常ユーザーの場合、ログインユーザ名、パスワードが必要になります。
これらの情報を使ってクライアントからアクセスします。
FTPクライアントはいくつか選択肢がありますが、本記事ではPython標準モジュールのftplibを
利用します。
同一PC内ならlocalhostで接続可能です。
接続確認用のコードは以下です。
# 匿名でログイン
import ftplib
ftp = ftplib.FTP()
ftp.connect('localhost', port=21, timeout=60)
ftp.login()
print("サーバーメッセージ:", ftp.getwelcome())
ftp.quit()
レスポンスが来たら接続成功です。
# 匿名ログインの場合
サーバーメッセージ: 220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 1 of 5 allowed.
220-Local time is now 06:16. Server port: 21.
220-Only anonymous FTP is allowed here
220-IPv6 connections are also welcome on this server.
220 You will be disconnected after 15 minutes of inactivity.
# ユーザーログインの場合
サーバーメッセージ: 220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 1 of 5 allowed.
220-Local time is now 06:22. Server port: 21.
220-This is a private system - No anonymous login # 差分
220-IPv6 connections are also welcome on this server.
220 You will be disconnected after 15 minutes of inactivity.
ftpユーザー、フォルダがない場合は次のようなエラーが発生します。
ユーザー追加や、フォルダ構成を見直してください。
ftplib.error_temp: 421 Unable to set up secure anonymous FTP
ファイルのアップロード、ダウンロード、削除
サンプルコードを記載します。
ファイルの操作権限エラーが発生する場合は、サーバー側の権限を見直してみてください。
# upload
with open('test.txt', "rb") as f:
ftp.storbinary('STOR test.txt', f)
# download
with open('test.txt', 'wb') as f:
ftp.retrbinary('RETR text.txt', f.write)
# delete
ftp.delete('test.txt')
その他コードは下記ソースをご参照ください。
おわりに
今回はFTPについて学習しました。FTPを意識せずにクライアントソフト(FileZillaなど)で利用していたので良い機会になりました。
サーバーの設定で苦労しましたが、ネットワークやDocker、Linuxについての知識が合わせて
身に付きました。
ネット上に匿名ユーザー環境についての情報が少なかったので参考になれば幸いです。
参考
Discussion