Apache MINA sshdでオレオレsshサーバを作ろう

GitBucket 1.12ではssh経由でのリポジトリアクセスをサポートしていますが、これに使用しているのがApache MINAのsshdです。
https://mina.apache.org/sshd-project/index.html

Apache MINAはNIOを使用したTCP/IPUDP/IPベースのネットワークサーバアプリケーションのためのフレームワークで、sshdはMINAを使用して実装されたsshサーバということになります。MINAのsshdは比較的簡単に再利用できるようになっており、Javaアプリケーションでsshを使用した暗号化通信を行いたい場合に使用することができます。

Javaを使用したOSSsshdの実装を提供しているのは僕の知る限りMINAだけであり(クライアントはいくつか実装がありますが)、JenkinsやGerritなどもこれを使ってsshサーバの機能を実現しています。

というわけで、とりあえず手始めにターミナルでコマンドを叩ける簡単なsshサーバの実装方法を紹介したいと思います。

Mavenプロジェクトの作成

まずはIntelliJでおもむろにMavenプロジェクトを作成し、pom.xmlに以下の依存関係を追加します。IntelliJの場合、依存関係を追加した後に右クリック→「Maven」→「Reimport」ってやるとクラスパスが更新されるみたいです。

<dependencies>
  <dependency>
    <groupId>org.apache.sshd</groupId>
    <artifactId>apache-sshd</artifactId>
    <version>0.10.0</version>
  </dependency>
</dependencies>

サーバの実装

サーバ本体はこんな感じ。認証処理はPasswordAuthenticatorとPublickeyAuthenticatorで実装するのですが、以下の例ではとりあえず何が来ても認証を通すようにしてあります。

ラムダ式で書いていますが、ラムダ式を使わなくても実装しないといけないコードのテンプレートをIDEが展開してくれるのでコードを書くときの手間は変わらないかなぁ、なんて思います。まあ、コードの見た目がすっきるするのはいいですね。

import org.apache.sshd.SshServer;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.shell.ProcessShellFactory;
import java.util.EnumSet;

public class Main {

  public static void main(String[] args) throws Exception {
    SshServer server = SshServer.setUpDefaultServer();
    server.setPort(8080);
    server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser"));
    server.setPasswordAuthenticator((userName, password, serverSession) -> true);
    server.setPublickeyAuthenticator((userName, publicKey, serverSession) -> true);

    server.setShellFactory(new ProcessShellFactory(new String[]{"cmd.exe"},
      EnumSet.of(
        ProcessShellFactory.TtyOptions.Echo,
        ProcessShellFactory.TtyOptions.ONlCr,
        ProcessShellFactory.TtyOptions.ICrNl)));

    server.start();
  }
}

Windowsで実行しているのでProcessShellFactoryではcmd.exeを叩くようにしていますが、Linuxの場合は/bin/shを叩くようにすればいいような気がします。

実行してみる

このプログラムを実行し、TeraTermからlocalhost:8080にアクセスすると以下のように普通にコマンドを叩くことができます(TeraTerm側の文字コードの設定はSJISに変更してます)。


この後は?

PasswordAuthenticatorとPublickeyAuthenticatorできちんと認証処理を実装したり、ShellFactoryやCommandFactoryで必要な処理を実装していくことになります。GitBucketではCommandFactoryでGitのUploadPackとReceivePackに対応したコマンドを生成して処理するようにしています。

Scalaのコードですが、GitBucketでのsshサポートは以下のパッケージにありますので興味のある方は見ていただければと思います。
https://github.com/takezoe/gitbucket/tree/master/src/main/scala/ssh

いや〜、こんなニッチな用途にもPure JavaOSS実装があるJavaって本当に素晴らしいですね〜♪