Apache FtpServer で FTPS サーバーを組み込み実行

Apache FtpServer を使って Groovy で FTPS (FTP over SSL/TLS) サーバーの組み込み実行を試してみました。

ソースは http://github.com/fits/try_samples/tree/master/blog/20140916/

FTP サーバーの組み込み実行

まずは、普通の FTP サーバーを組み込み実行してみます。

簡単にするため、ユーザー定義をプロパティファイルから読み出すようにし、平文のパスワードを設定するようにします。

プロパティファイルは ftpserver.user.<ユーザー名>.<プロパティ>=<値> のフォーマットで定義します。

一応、下記のような設定を定義すれば動作するようです。 (書き込み不可でよければ writepermission の設定は不要)

user.properties (プロパティファイル)
ftpserver.user.u1.userpassword=p1
ftpserver.user.u1.homedirectory=./data
ftpserver.user.u1.writepermission=true

プロパティファイルからユーザー定義を読み込むには PropertiesUserManagerFactory を使います。

PropertiesUserManagerFactory の passwordEncryptor プロパティへ ClearTextPasswordEncryptor を設定する事で平文のパスワードが使えます。

ftp_server.groovy (FTP サーバー組み込み実行スクリプト
@Grab('org.apache.ftpserver:ftpserver-core:1.0.6')
@Grab('org.slf4j:slf4j-api:1.7.7')
@Grab('org.slf4j:slf4j-simple:1.7.7')
import org.apache.ftpserver.FtpServerFactory
import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory

def factory = new FtpServerFactory()

// user.properties プロパティファイルを使用
factory.userManager = new PropertiesUserManagerFactory(
    file: new File('user.properties'),
    passwordEncryptor: new ClearTextPasswordEncryptor()
).createUserManager()

// サーバー起動
factory.createServer().start()

実行すると FTP サーバーが起動します。

実行例
> groovy ftp_server.groovy

[main] INFO org.apache.ftpserver.impl.DefaultFtpServer - FTP server started

cURL を使って動作確認してみます。

FTP 接続
$ curl -u u1:p1 ftp://localhost/

dr-x------   3 user group            0 Sep 15 20:18 a
dr-x------   3 user group            0 Sep 15 20:18 b

特に問題なく FTP サーバーへ接続できました。

FTPS サーバーの組み込み実行

次に、本題の FTPS (FTP over SSL/TLS) サーバーを組み込み実行してみます。

FTPS 実行にはキーストアが必要となりますので、JDK 付属の keytool コマンドを使って作成しておきます。

keytool -genkey -keypass <パスワード> -storepass <パスワード> -keystore <ファイル名> で最低限のキーストアファイルを作成できます。

「姓名」とか聞かれますが、テスト用に使うだけなら全て Unknown のままで構いません。

キーストアの作成例
> keytool -genkey -keypass sample -storepass -keystore sample.jks

姓名は何ですか。
  [Unknown]:
組織単位名は何ですか。
  [Unknown]:
組織名は何ですか。
  [Unknown]:
都市名または地域名は何ですか。
  [Unknown]:
都道府県名または州名は何ですか。
  [Unknown]:
この単位に該当する2文字の国コードは何ですか。
  [Unknown]:
CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknownでよろしいで
すか。
  [いいえ]:  はい

上記でカレントディレクトリへ sample.jks ファイルが作成されます。

FTPS を使えるようにするには SslConfiguration (上記で作成した sample.jks を keystoreFile へ設定) を設定した ListenerFtpServerFactoryaddListener します。

ftps_server.groovy (FTPS サーバー組み込み実行スクリプト
・・・
import org.apache.ftpserver.listener.ListenerFactory
import org.apache.ftpserver.ssl.SslConfigurationFactory
・・・

def factory = new FtpServerFactory()

def ssl = new SslConfigurationFactory(
    keystoreFile: new File('sample.jks'),
    keystorePassword: 'sample'
)

def listenerFactory = new ListenerFactory(
    sslConfiguration: ssl.createSslConfiguration()
)

factory.addListener('default', listenerFactory.createListener())

factory.userManager = new PropertiesUserManagerFactory(
    ・・・
).createUserManager()

factory.createServer().start()

実行すると FTP・FTPS のどちらでも接続できるサーバーが起動します。

実行例
> groovy ftps_server.groovy

[main] INFO org.apache.ftpserver.impl.DefaultFtpServer - FTP server started

まずは FTP で動作確認してみます。

FTP で接続
$ curl -u u1:p1 ftp://localhost/

dr-x------   3 user group            0 Sep 15 20:18 a
dr-x------   3 user group            0 Sep 15 20:18 b

特に問題なく FTP で接続できました。

次に --ssl -k オプションを追加指定して FTPS 接続してみます。 (正式な証明書を使っていれば -k オプションは不要)

今回は -v オプションも指定して詳細出力してみました。

FTPS で接続
$ curl --ssl -k -u u1:p1 -v ftp://localhost/

・・・
> AUTH SSL
・・・
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / DHE-DSS-AES128-GCM-SHA256
* Server certificate:
*        subject: C=Unknown; ST=Unknown; L=Unknown; O=Unknown; OU=Unknown; CN=Unknown
*        start date: 2014-09-15 11:46:38 GMT
*        expire date: 2014-12-14 11:46:38 GMT
*        issuer: C=Unknown; ST=Unknown; L=Unknown; O=Unknown; OU=Unknown; CN=Unknown
*        SSL certificate verify result: self signed certificate (18), continuing anyway.
・・・
dr-x------   3 user group            0 Sep 15 20:18 a
dr-x------   3 user group            0 Sep 15 20:18 b
・・・

SSL 接続を行っている事が確認できました。