JavaとApache MINAでお手軽にSSHサーバ作成

# はじめに

[Apache MINA](https://mina.apache.org/)を使うとJavaで簡単にSSHサーバを作ることができます。
以下は[Embedding SSHD in 5 minutes](https://mina.apache.org/sshd-project/embedding_ssh.html)を元にしたSSHサーバのソースです。

# ソース

```java:org/example/ssh/Server.java
package org.example.ssh;

import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.io.mina.MinaServiceFactory;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.server.Command;
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.shell.ProcessShellFactory;
import org.apache.sshd.sftp.subsystem.SftpSubsystem;

import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;

import static org.apache.sshd.server.shell.ProcessShellFactory.TtyOptions;

public class Server {
    public static void main(String[] args) {
        SshServer sshd = SshServer.setUpDefaultServer();
        sshd.setPort(10022);

        sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));

        sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
            @Override
            public boolean authenticate(
                    String username, String password, ServerSession session) {
                // ここでユーザ認証を行う
                return true;
            }
        });

        // sshのログインシェルを設定
        EnumSet<TtyOptions> options;
        String[] command;
        if (OsUtils.isWin32()) {
            options = EnumSet.of(TtyOptions.ONlCr, TtyOptions.Echo, TtyOptions.ICrNl);
            command = new String[]{"cmd"};
        } else {
            options = EnumSet.of(TtyOptions.ONlCr);
            command = new String[]{"/bin/sh", "-i", "-l"};
        }
        sshd.setShellFactory(new ProcessShellFactory(command, options));

        // scpの設定
        sshd.setCommandFactory(new ScpCommandFactory());

        // sftpの設定
        sshd.setSubsystemFactories(new ArrayList<NamedFactory<Command>>(1) {
            {
                add(new SftpSubsystem.Factory());
            }
        });

        sshd.setIoServiceFactory(new MinaServiceFactory());

        try {
            sshd.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

```build.gradle
apply plugin: 'application'

compileJava.options*.encoding = 'UTF-8'

repositories {
    mavenCentral()
}

mainClassName = 'org.example.ssh.Server'

dependencies {
    compile (
        'org.apache.sshd:apache-sshd:0.9.0',
    )
}


// IntelliJ IDEA用の設定
import org.gradle.plugins.ide.idea.model.*
apply plugin: 'idea'
idea {
    pathVariables 'GRADLE_USER_HOME': gradle.gradleUserHomeDir
    project {
        jdkName = '1.7'
        languageLevel = '1.7'
    }
}


// Eclipse用の設定
apply plugin: 'eclipse'
eclipse {
    pathVariables 'GRADLE_USER_HOME': gradle.gradleUserHomeDir
}
```

```text:ファイル配置
build.gradle
src/
  main/
    java/
      org/
        example/
          ssh/
            Server.java
```

# ビルドと実行

Gradleでビルドとサーバを実行できます。
`Building 75%`で止まりますが、サーバ起動済みなので10022ポートにsshで接続できます。
ユーザとパスワードはチェックしていないので何でも通ります。
scpやsftpも同様に接続できます。

```shell-session:ビルド
$ gradle build
:compileJava
Download http://repo1.maven.org/maven2/org/apache/sshd/apache-sshd/0.9.0/apache-sshd-0.9.0.pom
Download http://repo1.maven.org/maven2/org/apache/sshd/sshd/0.9.0/sshd-0.9.0.pom
Download http://repo1.maven.org/maven2/org/apache/sshd/sshd-core/0.9.0/sshd-core-0.9.0.pom
Download http://repo1.maven.org/maven2/org/apache/sshd/sshd-sftp/0.9.0/sshd-sftp-0.9.0.pom
Download http://repo1.maven.org/maven2/org/apache/sshd/sshd-pam/0.9.0/sshd-pam-0.9.0.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-simple/1.6.4/slf4j-simple-1.6.4.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-parent/1.6.4/slf4j-parent-1.6.4.pom
Download http://repo1.maven.org/maven2/bouncycastle/bcprov-jdk15/140/bcprov-jdk15-140.pom
Download http://repo1.maven.org/maven2/tomcat/tomcat-apr/5.5.23/tomcat-apr-5.5.23.pom
Download http://repo1.maven.org/maven2/tomcat/tomcat-parent/5.5.23/tomcat-parent-5.5.23.pom
Download http://repo1.maven.org/maven2/org/apache/mina/mina-core/2.0.7/mina-core-2.0.7.pom
Download http://repo1.maven.org/maven2/org/apache/mina/mina-parent/2.0.7/mina-parent-2.0.7.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.4/slf4j-api-1.6.4.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.pom
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-parent/1.6.6/slf4j-parent-1.6.6.pom
Download http://repo1.maven.org/maven2/org/apache/sshd/sshd-core/0.9.0/sshd-core-0.9.0.jar
Download http://repo1.maven.org/maven2/org/apache/sshd/sshd-sftp/0.9.0/sshd-sftp-0.9.0.jar
Download http://repo1.maven.org/maven2/org/apache/sshd/sshd-pam/0.9.0/sshd-pam-0.9.0.jar
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-simple/1.6.4/slf4j-simple-1.6.4.jar
Download http://repo1.maven.org/maven2/bouncycastle/bcprov-jdk15/140/bcprov-jdk15-140.jar
Download http://repo1.maven.org/maven2/tomcat/tomcat-apr/5.5.23/tomcat-apr-5.5.23.jar
Download http://repo1.maven.org/maven2/org/apache/mina/mina-core/2.0.7/mina-core-2.0.7.jar
Download http://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 30.948 secs
```

```shell-session:実行
$ gradle run
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run
216 [main] INFO org.apache.sshd.common.util.SecurityUtils - Trying to register BouncyCastle as a JCE provider
704 [main] INFO org.apache.sshd.common.util.SecurityUtils - Registration succeeded
> Building 75% > :run
```

ね、簡単でしょう？