1. 序章

SSH は、SecureShellまたはSecureSocket Shellとも呼ばれ、1台のコンピューターが安全でないネットワークを介して別のコンピューターに安全に接続できるようにするネットワークプロトコルです。 このチュートリアルでは、JSchおよびApacheMINASSHDライブラリを使用してリモートSSHサーバーへの接続をJavaで確立する方法を示します。

この例では、最初にSSH接続を開き、次に1つのコマンドを実行し、出力を読み取ってコンソールに書き込み、最後にSSH接続を閉じます。 サンプルコードは可能な限りシンプルにします。

2. JSch

JSch は、SSH2のJava実装であり、SSHサーバーに接続して、ポート転送、X11転送、およびファイル転送を使用できます。 また、BSDスタイルのライセンスの下でライセンスされており、JavaとのSSH接続を確立する簡単な方法を提供します。

まず、JSchMaven依存関係pom.xmlファイルに追加しましょう。

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

2.1. 実装

JSchを使用してSSH接続を確立するには、ユーザー名、パスワード、ホストURL、およびSSHポートが必要です。 デフォルトのSSHポートは22ですが、SSH接続に他のポートを使用するようにサーバーを構成する場合があります。

public static void listFolderStructure(String username, String password, 
  String host, int port, String command) throws Exception {
    
    Session session = null;
    ChannelExec channel = null;
    
    try {
        session = new JSch().getSession(username, host, port);
        session.setPassword(password);
        session.setConfig("StrictHostKeyChecking", "no");
        session.connect();
        
        channel = (ChannelExec) session.openChannel("exec");
        channel.setCommand(command);
        ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
        channel.setOutputStream(responseStream);
        channel.connect();
        
        while (channel.isConnected()) {
            Thread.sleep(100);
        }
        
        String responseString = new String(responseStream.toByteArray());
        System.out.println(responseString);
    } finally {
        if (session != null) {
            session.disconnect();
        }
        if (channel != null) {
            channel.disconnect();
        }
    }
}

コードからわかるように、最初にクライアントセッションを作成し、SSHサーバーに接続するように構成します。 次に、チャネルタイプ(この場合は exec、)を提供するSSHサーバーとの通信に使用するクライアントチャネルを作成します。これは、サーバーにシェルコマンドを渡すことを意味します。

また、サーバーの応答が書き込まれるチャネルの出力ストリームを設定する必要があります。 channel.connect()メソッドを使用して接続を確立した後、コマンドが渡され、受信した応答がコンソールに書き込まれます。

JSchが提供するさまざまな構成パラメーターの使用方法を見てみましょう。

  • StrictHostKeyChecking –アプリケーションが既知のホストの中でホスト公開鍵を見つけることができるかどうかをチェックするかどうかを示します。 また、使用可能なパラメーター値は、 ask yes、、および no です。ここで、askがデフォルトです。 このプロパティをyesに設定すると、JSchはホストキーを known_hosts ファイルに自動的に追加せず、ホストキーが変更されたホストへの接続を拒否します。 これにより、ユーザーはすべての新しいホストを手動で追加する必要があります。 no に設定すると、JSchは既知のホストのリストに新しいホストキーを自動的に追加します
  • Compression.s2c –サーバーからクライアントアプリケーションへのデータストリームに圧縮を使用するかどうかを指定します。 使用可能な値は、zlibおよびnoneで、2番目がデフォルトです。
  • Compression.c2s –クライアント/サーバー方向のデータストリームに圧縮を使用するかどうかを指定します。 使用可能な値は、zlibおよびnoneで、2番目がデフォルトです。

メモリリークを回避するために、サーバーとの通信が終了した後、セッションとSFTPチャネルを閉じることが重要です

3. Apache MINA SSHD

Apache MINA SSHD は、JavaベースのアプリケーションにSSHサポートを提供します。 このライブラリは、スケーラブルで高性能な非同期IOライブラリであるApacheMINAに基づいています。

Apache MinaSSHDMaven依存関係を追加しましょう。

<dependency>
    <groupId>org.apache.sshd</groupId>
    <artifactId>sshd-core</artifactId>
    <version>2.5.1</version>
</dependency>

3.1. 実装

ApacheMINASSHDを使用してSSHサーバーに接続するコードサンプルを見てみましょう。

public static void listFolderStructure(String username, String password, 
  String host, int port, long defaultTimeoutSeconds, String command) throws IOException {
    
    SshClient client = SshClient.setUpDefaultClient();
    client.start();
    
    try (ClientSession session = client.connect(username, host, port)
      .verify(defaultTimeoutSeconds, TimeUnit.SECONDS).getSession()) {
        session.addPasswordIdentity(password);
        session.auth().verify(defaultTimeoutSeconds, TimeUnit.SECONDS);
        
        try (ByteArrayOutputStream responseStream = new ByteArrayOutputStream(); 
          ClientChannel channel = session.createChannel(Channel.CHANNEL_SHELL)) {
            channel.setOut(responseStream);
            try {
                channel.open().verify(defaultTimeoutSeconds, TimeUnit.SECONDS);
                try (OutputStream pipedIn = channel.getInvertedIn()) {
                    pipedIn.write(command.getBytes());
                    pipedIn.flush();
                }
            
                channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 
                TimeUnit.SECONDS.toMillis(defaultTimeoutSeconds));
                String responseString = new String(responseStream.toByteArray());
                System.out.println(responseString);
            } finally {
                channel.close(false);
            }
        }
    } finally {
        client.stop();
    }
}

Apache MINA SSHDを使用する場合、JSchの場合と非常によく似た一連のイベントがあります。 まず、SshClientクラスインスタンスを使用してSSHサーバーへの接続を確立します。 SshClient.setupDefaultClient()、で初期化すると、ほとんどのユースケースに適したデフォルト構成のインスタンスで作業できるようになります。 これには、暗号、圧縮、MAC、鍵交換、および署名が含まれます。

その後、 ClientChannel を作成し、それに ByteArrayOutputStream をアタッチして、応答ストリームとして使用します。 ご覧のとおり、SSHDではすべての操作に対して定義済みのタイムアウトが必要です。 また、 Channel.waitFor()メソッドを使用して、コマンドが渡された後、サーバーの応答を待機する時間を定義することもできます。

次のことに注意することが重要です SSHDは、完全なコンソール出力を応答ストリームに書き込みます。 JSchは、コマンド実行結果でのみそれを行います。

Apache Mina SSHDに関する完全なドキュメントは、プロジェクトの公式GitHubリポジトリで入手できます。

4. 結論

この記事では、利用可能な2つのJavaライブラリ(JSchとApache Mina SSHD)を使用してJavaとのSSH接続を確立する方法について説明しました。 また、コマンドをリモートサーバーに渡して実行結果を取得する方法も示しました。 また、完全なコードサンプルはGitHubから入手できます。