🗄️

DockerとPythonでFTPサーバーを構築し接続する方法【匿名ユーザー対応】

に公開

はじめに

FTPサーバーをコンテナで起動し、Pythonで接続する方法を習得しました。
匿名ユーザーでの利用を想定し、環境構築をする中でいくつか躓きポイントがあったので
共有します。本記事が皆さんの参考になりますと幸いです。

想定読者🧑‍🦲

  • FTPとは何か知りたい方
  • FTPサーバーを簡単に立てて、Pythonで利用したい方
  • FTPを匿名ユーザで利用する方法が知りたい方

FTPとは

File Transfer Protocol」の名前通り、ファイルを転送するプロトコルです。
あるコンピュータから別のコンピュータへファイルを送る・受け取る際に利用します。
FTPクライアントはサーバーへ要求をし、FTPサーバーは要求に対して答えます。
このクライアントとサーバーの2点を押さえておけば、理解が進みます。
例としてクライアントがAファイルの内容を知りたいと要求したら、サーバーはAファイルを
転送します。

alt text

FTPクライアントとサーバーの接続にはアクティブモードとパッシブモードの二種類があります。
さらにやり取りをするために、制御用データ転送用の2種類のコネクションを確立します。
順にそれぞれ整理していきます。

アクティブモード

サーバ側からデータ転送用の線をクライアントに繋いで接続する方式。21番ポートが制御に使用されることが多いです。
サーバーからクライアントに繋ぐため、ファイアウォールが働いて繋がらないことがあります。

alt text

パッシブモード

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

alt text

おさらい

接続方式 制御用の接続 データ転送用の接続 特徴
アクティブモード クライアント → サーバー サーバークライアント クライアント側のファイアウォールでブロックされることがある
パッシブモード クライアント → サーバー クライアントサーバー ファイアウォールの問題を回避

今回はコンテナ環境での設定を容易にするためパッシブモードを利用します。

認証について

FTPサーバーにアクセスするには認証が必要です。
大別すると通常ユーザー匿名ユーザーがあります。

認証方式 概要 主な用途・注意点
通常ユーザー😊 IDとパスワードで認証する方式 特定のメンバー間でのファイル共有に利用。
一定のセキュリティが担保。
匿名ユーザー😶‍🌫️ 認証なしで誰でもアクセスできる仕組み 不特定多数へのファイル配布などに利用。
誰でもアクセスできるためセキュリティリスクがある。

注意点

このサンプルでは通信が暗号化されないため、ローカル環境での実行が前提ということを
ご留意ください。暗号化が必要な場合はFTPS/SFTPなどの利用を検討してください。
また、匿名ユーザーを許可すると誰でもアクセス出来てしまうため、本番環境での利用は慎重に検討する必要があります。

コンテナでFTPサーバーを構築・操作する(実践)

システム構成

今回は図の環境で学習をしました。

alt text

FTPサーバーの立て方

https://dev.classmethod.jp/articles/docker-ftp-ssm-python-practice/

記事を参考に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

https://github.com/stilliard/docker-pure-ftpd/issues/43

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')

その他コードは下記ソースをご参照ください。

https://zenn.dev/furimura/articles/a2fb4e91522f2b#python-ftplibのftp操作
https://docs.python.org/ja/3/library/ftplib.html

おわりに

今回はFTPについて学習しました。FTPを意識せずにクライアントソフト(FileZillaなど)で利用していたので良い機会になりました。
サーバーの設定で苦労しましたが、ネットワークやDocker、Linuxについての知識が合わせて
身に付きました。
ネット上に匿名ユーザー環境についての情報が少なかったので参考になれば幸いです。

参考

https://wa3.i-3-i.info/word1137.html
https://www.chuken-engineer.com/entry/2019/07/10/172141

GitHubで編集を提案
株式会社セカンドセレクション

Discussion