先ほど、Apache MINAのFTPサーバをご紹介しました。
Apache MINAで簡単FTPD
http://d.hatena.ne.jp/Kazuhira/20140308/1394254984
今度は、SSHサーバです。
SSHD
http://mina.apache.org/sshd-project/
FTPサーバは組み込みのライブラリとして使うか、スタンドアロンで使うかを選べたみたいですが、こちらはプログラムで組むことが前提みたいです。
では、使ってみましょう。
こちらのチュートリアルを参考に。
Embedding SSHD in 5 minutes
http://mina.apache.org/sshd-project/embedding_ssh.html
プログラムは、Groovyで書きます。
まずは、import文。
@Grab('org.apache.sshd:sshd-core:0.10.1') @Grab('org.apache.sshd:sshd-pam:0.10.1') @Grab('org.apache.sshd:sshd-sftp:0.10.1') import org.apache.sshd.SshServer import org.apache.sshd.server.PasswordAuthenticator import org.apache.sshd.server.command.ScpCommandFactory import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider import org.apache.sshd.server.session.ServerSession import org.apache.sshd.server.sftp.SftpSubsystem import org.apache.sshd.server.shell.ProcessShellFactory
ホントは、Grapeでこう書きたいんですけど
@Grab('org.apache.sshd:sshd:0.10.1')
これが直接引っ張ってくるのは、JARではなくpom.xmlなのでうまくいかない様子。
MavenやGradleなら大丈夫なのかな?
最初に、インスタンスの作成。
// SshServerのインスタンス作成 def sshd = SshServer.setUpDefaultServer()
ポートとKeyProviderの設定。
// ポートとKeyProviderの指定 sshd.port = 10022 sshd.keyPairProvider = new SimpleGeneratorHostKeyProvider("hostkey.ser")
ここで書いている「hostkey.ser」というファイルは、SSHDが勝手に作成するので、起動時になくても大丈夫です。
ちなみに、Groovyで書いているのでここを含めて以降、Javaで
sshd.setPort(10022);
と書くべきところを全部setter経由の呼び出しを端折ってます。
続いて、認証方式の設定。
// 認証方式の設定 sshd.passwordAuthenticator = new MyPasswordAuthenticator()
実は、この認証方式の設定は先ほどのチュートリアルの中には書かれていなくて、別ドキュメントになります。
Configuring Security
http://mina.apache.org/sshd-project/configuring_security.html
公開鍵認証方式とパスワード認証方式がありますが、ここではパスワード認証方式を選択しました。
今回作成した、PasswordAuthenticatorインターフェースの実装は、こんな感じ。
// 実質、誰でも認証 class MyPasswordAuthenticator implements PasswordAuthenticator { @Override public boolean authenticate(String username, String password, ServerSession session) { println("user[$username], password[$password]") true } }
実際は、ちゃんと認証してくださいと…。
続いて、シェルの設定。
// シェルの設定 sshd.shellFactory = new ProcessShellFactory(["/bin/bash", "-i", "-l"].toArray(new String[0]), EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr))
ドキュメント通りに「/bin/sh」と書くと、Ubuntuはデフォルトがdashなのでうまく動きませんでした…。
また、ProcessShellFactory.TtyOptionsを使わないとsshコマンドでリモートログインした時に、改行とか空白が乱れてえらいことになったのですが、Twitterでヒントをいただいて修正することができました。ありがとうございます!
続いて、scpとsftpを有効化します。
// scpの有効化 sshd.commandFactory = new ScpCommandFactory() // sftpの有効化 sshd.subsystemFactories = [new SftpSubsystem.Factory()]
サーバの起動。
// サーバ起動 sshd.start() println("[${new Date()}] Startup. Simple-SSHD")
終了する時は、stopです。
// 終了(コメントアウトを解除すると、終了します) // sshd.stop()
今回は、そのまま通しちゃうと終了してしまうので、コメントアウトしています。
では、動かしてみましょう!実行!
$ groovy simple-sshd.groovy SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. [Sat Mar 08 16:28:45 JST 2014] Startup. Simple-SSHD
別のコンソールから接続。先のパスワード認証方式の実装通り、パスワードは何でも通ります。
$ ssh -p 10022 user@localhost The authenticity of host '[localhost]:10022 ([127.0.0.1]:10022)' can't be established. DSA key fingerprint is d6:c1:fc:dc:43:4b:5d:f9:18:4a:ef:68:f1:54:a1:f8. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[localhost]:10022' (DSA) to the list of known hosts. Password authentication Password: $
無事、接続完了です。
この後、普通に「ls」とかコマンドを実行することができます。
終了。
$ exit
ログアウト
Connection to localhost closed.
scp。クライアントからサーバへコピー。
$ scp -P 10022 scp-client.txt user@localhost: Password authentication Password: scp-client.txt 100% 16 0.0KB/s 00:00
サーバからクライアントへ。
$ scp -P 10022 user@localhost:/path/to/scp-server.txt ./. Password authentication Password: scp-server.txt 100% 16 0.0KB/s 00:00
なんか、Fromがサーバだと、フルパスじゃないと動かなかった??
sftp。
$ sftp -P 10022 user@localhost Password authentication Password: Connected to localhost. sftp>
get。
sftp> get sftp-server.txt Fetching /path/to/sftp-server.txt to sftp-server.txt /path/to/sftp-ser 100% 17 0.0KB/s 00:00
put。
sftp> put sftp-client.txt Uploading sftp-client.txt to /path/to/sftp-client.txt sftp-client.txt 100% 17 0.0KB/s 00:00
終了。
sftp> bye
ちなみに、lsとかはキレイに見れるっていうね。
sftp> ls -l -rw-rw-r-- 1 xxxxx xxxxx 1201 Mar 8 16:12 hostkey.ser -rw-rw-r-- 1 xxxxx xxxxx 16 Mar 8 16:35 scp-client.txt -rw-rw-r-- 1 xxxxx xxxxx 16 Mar 8 16:26 scp-server.txt -rw-rw-r-- 1 xxxxx xxxxx 17 Mar 8 16:37 sftp-client.txt -rw-rw-r-- 1 xxxxx xxxxx 17 Mar 8 16:26 sftp-server.txt -rw-rw-r-- 1 xxxxx xxxxx 1579 Mar 8 16:23 simple-sshd.groovy
なんかscpのサーバからのコピー時が??な気がしますが、使えそうな感じ。
一応、今回書いたコードを載せておきます。
simple-sshd.groovy
// @Grab('org.apache.sshd:sshd:0.10.1') // この形式は指定できない? @Grab('org.apache.sshd:sshd-core:0.10.1') @Grab('org.apache.sshd:sshd-pam:0.10.1') @Grab('org.apache.sshd:sshd-sftp:0.10.1') import org.apache.sshd.SshServer import org.apache.sshd.server.PasswordAuthenticator import org.apache.sshd.server.command.ScpCommandFactory import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider import org.apache.sshd.server.session.ServerSession import org.apache.sshd.server.sftp.SftpSubsystem import org.apache.sshd.server.shell.ProcessShellFactory // SshServerのインスタンス作成 def sshd = SshServer.setUpDefaultServer() // ポートとKeyProviderの指定 sshd.port = 10022 sshd.keyPairProvider = new SimpleGeneratorHostKeyProvider("hostkey.ser") // 認証方式の設定 sshd.passwordAuthenticator = new MyPasswordAuthenticator() // シェルの設定 sshd.shellFactory = new ProcessShellFactory(["/bin/bash", "-i", "-l"].toArray(new String[0]), EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr)) // scpの有効化 sshd.commandFactory = new ScpCommandFactory() // sftpの有効化 sshd.subsystemFactories = [new SftpSubsystem.Factory()] // サーバ起動 sshd.start() println("[${new Date()}] Startup. Simple-SSHD") // 終了(コメントアウトを解除すると、終了します) // sshd.stop() // 実質、誰でも認証 class MyPasswordAuthenticator implements PasswordAuthenticator { @Override public boolean authenticate(String username, String password, ServerSession session) { println("user[$username], password[$password]") true } }