Conéctate a un Servidor SSH con Java Usando JSch y Apache MINA SSHD

Conectarse a un Servidor SSH con Java: Usando JSch y Apache MINA SSHD

1. Introducción

SSH, conocido como Secure Shell o Secure Socket Shell, es un protocolo de red que permite que una computadora se conecte de forma segura a otra computadora a través de una red no segura. En este tutorial, mostraremos cómo establecer una conexión a un servidor SSH remoto con Java utilizando las bibliotecas JSch y Apache MINA SSHD.

A través de ejemplos prácticos, primero abriremos la conexión SSH, luego ejecutaremos un comando, leeremos la salida y la escribiremos en la consola, y finalmente cerraremos la conexión SSH. Mantendremos el código de ejemplo lo más simple posible para facilitar su comprensión.

2. JSch

JSch es la implementación de Java de SSH2 que permite conectarse a un servidor SSH y utilizar el reenvío de puertos, reenvío de X11 y transferencia de archivos. Además, está licenciado bajo la licencia de estilo BSD y nos proporciona una manera sencilla de establecer una conexión SSH con Java.

Primero, añadamos la dependencia de JSch en Maven a nuestro archivo pom.xml:

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

2.1. Implementación

Para establecer una conexión SSH utilizando JSch, necesitamos un nombre de usuario, una contraseña, la URL del host y el puerto SSH. El puerto SSH por defecto es el 22, pero es posible que el servidor esté configurado para utilizar otro puerto para las conexiones 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();
        }
    }
}

Como podemos ver en el código, primero creamos una sesión de cliente y la configuramos para la conexión a nuestro servidor SSH. Luego, creamos un canal de cliente utilizado para comunicarnos con el servidor SSH, donde proporcionamos un tipo de canal – en este caso, exec, lo que significa que estaremos pasando comandos de shell al servidor.

También debemos establecer el flujo de salida para nuestro canal donde se escribirá la respuesta del servidor. Después de establecer la conexión utilizando el método channel.connect(), se pasa el comando y la respuesta recibida se escribe en la consola.

Veamos cómo utilizar diferentes parámetros de configuración que JSch ofrece:

  • StrictHostKeyChecking – Indica si la aplicación comprobará si la clave pública del host se puede encontrar entre los hosts conocidos. Los valores disponibles son ask, yes, y no, donde ask es el valor predeterminado. Si establecemos esta propiedad en yes, JSch nunca añadirá automáticamente la clave del host al archivo known_hosts. Si se establece en no, JSch añadirá automáticamente una nueva clave del host a la lista de hosts conocidos.
  • compression.s2c – Especifica si se utilizará compresión para el flujo de datos del servidor a nuestra aplicación cliente.
  • compression.c2s – Especifica si se utilizará compresión para el flujo de datos en la dirección cliente-servidor.

Es importante cerrar la sesión y el canal SFTP después de la comunicación con el servidor para evitar fugas de memoria.

3. Apache MINA SSHD

Apache MINA SSHD proporciona soporte SSH para aplicaciones basadas en Java. Esta biblioteca se basa en Apache MINA, una biblioteca de IO asíncrona escalable y de alto rendimiento.

Añadamos la dependencia de Apache MINA SSHD en Maven:

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

3.1. Implementación

Veamos un ejemplo de código para conectarse al servidor SSH utilizando Apache MINA SSHD:

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();
    }
}

Al trabajar con Apache MINA SSHD, tenemos una secuencia de eventos muy similar a la de JSch. Primero, establecemos una conexión con un servidor SSH utilizando la instancia de la clase SshClient. Si la inicializamos con SshClient.setupDefaultClient(), podremos trabajar con la instancia que tiene una configuración predeterminada adecuada para la mayoría de los casos de uso.

Después de eso, creamos un ClientChannel y adjuntamos el ByteArrayOutputStream a él para utilizarlo como flujo de respuesta. Como podemos ver, SSHD requiere que se definan tiempos de espera para cada operación y también permite definir cuánto tiempo esperará para la respuesta del servidor después de pasar el comando utilizando el método Channel.waitFor().

Es importante notar que SSHD escribirá toda la salida de la consola en el flujo de respuesta. JSch solo lo hará con el resultado de la ejecución del comando.

La documentación completa sobre Apache Mina SSHD está disponible en el repositorio oficial de GitHub.

4. Conclusión

Este artículo mostró cómo establecer una conexión SSH con Java utilizando dos de las bibliotecas disponibles: JSch y Apache MINA SSHD. También se demostró cómo pasar el comando al servidor remoto y obtener el resultado de la ejecución.

Consejos Prácticos para Programadores Especializados en Java:

  • Manejo de Errores: Asegúrate de implementar un manejo de errores adecuado para cada operación SSH, incluyendo la conexión, autenticación y ejecución de comandos. Esto te ayudará a detectar y manejar cualquier fallo en la comunicación con el servidor.
  • Seguridad: Evita establecer StrictHostKeyChecking en no en producción. En vez de eso, configura correctamente las claves conocidas para fortalecer la seguridad de tus conexiones.
  • Optimiza el Rendimiento: Considera el uso de compresión si estás transmitiendo grandes cantidades de datos entre el cliente y el servidor. Esto puede mejorar notablemente la eficiencia de la comunicación.
  • Documentación: Asegúrate de familiarizarte con las documentaciones de JSch y Apache MINA SSHD para aprovechar al máximo las funcionalidades que ofrecen.

Mediante la comprensión y el uso efectivo de estas bibliotecas, podrás integrar capacidades SSH en tus aplicaciones Java con facilidad, lo que abre un mundo de posibilidades en la administración remota y la automatización.

¡Esperamos que este artículo te haya ayudado a conectarte exitosamente a servidores SSH usando Java!