Implementando conexiones SSH en tus aplicaciones

codeCualquiera pensaría que, viendo la cantidad de librerías disponibles para diversos tipos de clientes, implementar un cliente SSH en una aplicación debería ser casi trivial.

De hecho prácticamente lo es por ejemplo en python, donde gracias a paramiko podemos tener una aplicación pygtk con esta funcionalidad en cuestión de minutos.

El problema es que la versión Windows de la aplicación con la que he estado liado últimamente la estoy desarrollando en Delphi (por costumbre desde que me aficioné al object pascal en la facultad), y ahí la cosa se complica.

En teoría disponemos de una libraría llamada Synapse que aunque en principio no soporta SSH sí que se puede usar junto con la librería LibCrypt para establecer una conexión SSHv2.

Será porque soy inútil o por cualquier otra razón pero tras algunos intentos infructuosos con Synapse al final me decanté por tirar de Plink, que viene a ser como una versión de linea de comandos de Putty.

A fin de cuentas no necesito nada interactivo, sólo lanzar un comando remoto en un servidor, obtener la respuesta y salir.

Así que manos a la obra, lanzo el plink con los parámetros correspondientes usando un CreateProcess en Delphi y capturando la salida… y primer batacazo: resulta que hay un error (o complicación, no se si es realmente un bug) con Plink al lanzarlo desde otra aplicación, obteniendo esto:

Unable to read from standard input: The handle is invalid.

Algo relacionado con que Plink espera algún tipo de redirección del standard input.

En fin, que de nuevo decido esquivar el tema para no perder tiempo y voy a por la siempre socorrida solución de meter desde la aplicación el comando Plink con todos los parámetros y una redirección de la salida a un fichero dentro de un .bat “temporal” (que se borrará después de ser ejecutado), y lanzar el bat con un CreateProcess esperando a que termine. Una vez terminado leo el fichero de salida y todos felices.

O igual no, porque aparece un nuevo obstáculo: la primera vez que conectas con una máquina por SSH te suele aparecer un aviso preguntando si quieres aceptar el host key del servidor… cosa que no puedes responder si estás ejecutando plink en modo batch. En este caso lo que hace plink es simplemente no conectar.

Ante un nuevo problema, otro workaround “apañao”: antes de establecer la primera conexión “útil” con el host (donde se lanzaría el comando del que queremos leer la respuesta) hacemos una conexión de prueba con cualquier comando simple (por ejemplo un “pwd”) pero quitando el modo batch y en su lugar pasándole la respuesta “yes” para que acepte la key:

echo yes | plink -l usuario -pw password  pwd

Con esto ya tenemos aceptada la key, y en las sucesivas conexiones al mismo host podemos lanzar plink en modo batch (plink -batch) y funcionará correctamente.

La razón de usar un comando cualquiera de prueba en esa primera conexión es que la salida no será únicamente la respuesta del comando si no también todo el párrafo relativo al host key, con lo que se hace más tedioso de parsear en nuestro programa.