2017年5月30日火曜日

Amazon EC2 + FTPSサーバ + FTPSClient

Androidアプリ開発で Apache Commons Net の FTPSClient を使って、FTPS機能を作成した時のことをまとめます。


サーバをたてよう


検証できる環境がなかったので、FTPサーバを立てるところから始めます。
FTPサーバを立てたことがなかったですが、調べたら簡単そうだったのでやってみました。

まず、EC2でAmazon Linux AMIを使ってサーバを構築します。
FTPSのテストに使うだけなので、無料枠があれば無料枠内で使える最小の設定で大丈夫です。
こういった環境を簡単に作れる AWS は非常に良いですね。

ついでにセキュリティグループも設定しておきましょう。
インバウンドに次のルールを追加します。

タイプ プロトコル ポート範囲 ソース
カスタムTCPルール TCP 21 0.0.0.0/0
カスタムTCPルール TCP 50021-50040 0.0.0.0/0


ユーザを作ろう


FTPでログインするための適当なユーザを作ります。
ユーザを作り、パスワードを設定するには、次のコマンドを入力します。
$ sudo useradd ftp-user
$ sudo passwd ftp-user
> 新しいパスワードを入力します。
"ftp-user"の部分は、好きなユーザ名で構いません。
ユーザ名とパスワードはメモしておきましょう。


vsftpdをインストールしよう


EC2に入り(WindowsならTeraTermで)、次のコマンドを入力します。
$ sudo yum install vsftpd -y
これでインストールは完了です。簡単です。


証明書を作ろう


FTP over SSL/TLS を実現するために証明書を作ります。

証明書の作り方は割愛します。すみません。
ググったらいっぱい出てきます。

ちなみに私は次のサイトを参考にしました。


完成した鍵ファイルや証明書は /etc/vsftpd 配下に置きます。(どこでもいいですけどね)


FTPサーバを設定しよう


FTPサーバの設定ファイルをいじっていきます。
$ sudo vi /etc/vsftpd/vsftpd.conf
いろんなサイトを参考にコピペしながら頑張ってみましたが、なかなかログインできず苦労しました。
最終的な vsftpd.conf は以下になります。
anonymous_enable=NO
ssl_enable=YES
rsa_cert_file=/etc/vsftpd/ftp.pem
rsa_private_key_file=/etc/vsftpd/server.key
ssl_sslv2=NO
ssl_sslv3=NO
ssl_tlsv1=YES
allow_anon_ssl=NO
require_ssl_reuse=NO
force_local_logins_ssl=YES
force_local_data_ssl=YES
pasv_enable=YES
pasv_address=xxx.xxx.xxx.xxx
pasv_min_port=50021
pasv_max_port=50040
use_localtime=YES
ssl_ciphers=TLSv1
local_enable=YES
write_enable=YES
dirmessage_enable=NO
xferlog_enable=YES
connect_from_port_20=YES
xferlog_std_format=YES
ascii_upload_enable=YES
ascii_download_enable=YES
ls_recurse_enable=YES
listen=YES
pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=NO
調べてもなかなか出てこなかったのが ssl_ciphers=TLSv1 ですね。
この設定がないことで、なかなか繋がらず苦労しました。

pasv_address の xxx.xxx.xxx.xxx には、EC2のIPアドレスを入れてください。
EC2のIPアドレスは再起動するたびに変わるので注意が必要です。(ElasticIPを設定してるなら別ですが)


FTPサーバを起動しよう


FTPサーバを起動します。次のコマンドを入力してください。
$ sudo service vsftpd start
[OK]が表示されれば、起動完了です。
失敗したら、vsftpd.conf に間違いがないかなど調べてみてください。

あとは接続が出来るかどうか FFFTP などのクライアントを使って、接続テストをしてみましょう!


Androidアプリに FTPSClient を組み込もう


通信なので、AsyncTask の中で実装するのが良いですね。
細かいところは省略しますが、次のコードで FTPS でアクセスできます。

FTPSClient ftpsClient = new FTPSClient();
try {
    ftpsClient.setConnectTimeout(10000);
    ftpsClient.setDefaultTimeout(5000);
    ftpsClient.connect("xxx.xxx.xxx.xxx", 21);
    ftpsClient.login("ftp-user", "password")
    ftpsClient.setFileType(FTP.BINARY_FILE_TYPE);
    ftpsClient.enterLocalPassiveMode();
    ftpsClient.execPROT("P");
    ftpsClient.setSoTimeout(5000);
    ftpsClient.setDataTimeout(5000);
    ftpsClient.changeWorkingDirectory(mPath)
    FileInputStream fisupload = new FileInputStream(filename);
    boolean result = ftpsClient.storeFile(filename, fisupload);
    fisupload.close();
} catch (Exception e) {
 Log.d("ftpTest", ftpsClient.getReplyString());
} finally {
    if (ftpsClient.isConnected()) {
        try {ftpsClient.disconnect();} catch (IOException e) {}
    }
    ftpsClient = null;
}

ここでのポイントは ftpsClient.execPROT("P"); ですね。
FTPSClient の Example にもなかったりして、これを見つけるまで苦労しました。

最初はエラーが多いが出るかもしれませんが、 ftpsClient.getReplyString() のメッセージを見ながら頑張りましょう。

寂しいと思えるチーム作り

12月末で一つのプロジェクトが終わりました。 最後のデイリースクラムの時に、一年目のメンバーが「寂しいですね」と言ってくれました。何気ない一言でしたが、チームをまとめている私としてはとても嬉しい言葉でした。お世辞だったかもしれませんが、それでもうれしかったです。 自分としてはちゃ...