Ejecución remota de comandos contra máquinas Unix desde .Net

Estoy involucrado en un interesante proyecto que tiene por objetivo demostrar que VSTS for Tester es una plataforma interesante a la hora de testear arquitecturas no centradas en plataforma Microsoft, arquitecturas muy heterogéneas. Un primer post relacionado este proyecto fue: Pruebas web de Team System usando Firefox. Y seguro que habrá más.


Uno de los primeros pasos que tengo que dar es obtener de manera remota información del comportamiento, desde el punto de vista del rendimiento, de una máquina Unix/Linux. La familia de los Unix cuenta con una serie de comandos que nos permiten saber como se está comportando la máquina, el más relevante de ellos quizás sea vmstat que da información sobre varios aspectos relacionados con el rendimiento. El afán final es poder exponer información de máquinas Unix como contadores de rendimiento de Windows para poderlos recolectar desde pruebas de carga de VSTS, ya que esta es la única fuente de información sobre el rendimiento que Team System integra en las pruebas (aunque es probable que esto cambie con Rosario)… pero no llegaré tan lejos hoy, hoy solo voy a comentar como ejecutar comandos remotamente desde .Net (en concreto con C#) contra máquinas Unix.


La manera habitual de ejecutar comandos Unix de manera remota es utilizar ssh (Secure Shell). En la siguiente imagen podéis ver el resultado de ejecutar el comando vmstat utilizando PuTTY como cliente ssh contra una máquina Linux(corriendo Fedora) desde una máquina Windows:


image


Ahora la pregunta es obvia, ¿existe alguna manera de ejecutar un comando usando ssh desde código .net y recoger la salida del mismo? La respuesta es sí gracias a SharpSSH una librería de código abierto creada para este cometido. No cuenta con mucha documentación pero su uso es simple, a menos para lo que yo necesito. Basta poner una referencia a la libreria Tamir.SharpSSH.dll y utilizar la clase SshExec para ejecutar el comando. Os dejo el código comentado a continuación:



      //Establecemos la conexión SSH


      const string host = “192.168.1.65”;


      SshExec sshExec = new SshExec(host, “root”, “pass+w0rd”);


      sshExec.Connect();


 


      //Usamos el comando vmstat para obtener la información de rendimiento


      //Usamos el comando sed para quedarnos solo con la línea que nos interesa


      //de la salida de vmstat


      string result = sshExec.RunCommand(@”vmstat 1 2 | sed ‘4!d'”);


 


      //vmstat da una salida en columnas separadas por espacios


      //obtenemos el valor de cada una de esas columnas


      Regex re = new Regex(@”(d*?)s+”);


      MatchCollection matches = re.Matches(result);


 


      //Mostramos por consola los resultado junto con una descripción


      Console.WriteLine(“——-Host: {0}———-“, host);


      Console.WriteLine(“———–Procs———-“);


      Console.WriteLine(“Number of processes waiting for run time: {0}”, matches[1]);


      Console.WriteLine(“Number of processes in uninterruptible sleep: {0}”, matches[2]);


      Console.WriteLine(“———–Memory———-“);


      Console.WriteLine(“Amount of virtual memory used: {0}”, matches[3]);


      Console.WriteLine(“Amount of free memory: {0}”, matches[4]);


      Console.WriteLine(“Amount of memory used as buffers: {0}”, matches[5]);


      Console.WriteLine(“Amount of memory used as cache: {0}”, matches[ 6]);


      Console.WriteLine(“———–Swap———-“);


      Console.WriteLine(“Amount of memory swapped in from disk (/s): {0}”, matches[7]);


      Console.WriteLine(“Amount of memory swapped to disk (/s): {0}”, matches[ 8]);


      Console.WriteLine(“———–IO———-“);


      Console.WriteLine(“Blocks received from a block device (blocks/s): {0}”, matches[9]);


      Console.WriteLine(“Blocks sent to a block device (blocks/s): {0}”, matches[10]);


      Console.WriteLine(“———–System———-“);


      Console.WriteLine(“Number of interrupts per second, including the clock: {0}”, matches[11]);


      Console.WriteLine(“Number of context switches per second: {0}”, matches[12]);


      Console.WriteLine(“———–CPU———-“);


      Console.WriteLine(“Time spent running non-kernel code. (user time, including nice time): {0}”, matches[13]);


      Console.WriteLine(“Time spent running kernel code. (system time): {0}”, matches[14]);


      Console.WriteLine(“Time spent idle: {0}”, matches[15]);


      Console.WriteLine(“Time spent waiting for IO: {0}”, matches[16]);


      Console.WriteLine(“Time stolen from a virtual machine: {0}”, matches[17]);


La salida de la ejecución del este programa es la siguiente:


image


Con lo que doy por cumplido mi objetivo, al menos de momento… os seguiré contando mis avances.

17 comentarios en “Ejecución remota de comandos contra máquinas Unix desde .Net”

  1. ¿Por qué no usar SNMP?

    Siguiendo el método que propones, en el momento en que necesites aumentar la información a capturar vas a tener que crear aplicaciones específicas para cada comando remoto que se ejecute.

    Si sólo tienes que capturar uno o dos comandos, entiendo que sea factible, pero cuando tengas que capturar muchos creo que es mejor usar herramientas de monitorización remota específicas ¿no?

    Alejandro

  2. SNMP es una opción que he valorado y he descartado porque no me proporciona suficiente información. Prácticamente solo me permite obtener información fiable sobre temas relacionados con la red.

    Sobre tu segundo comentario, no voy a tener que hacer ni un solo programa especifico. Gracias a la potencia de los shell de Linux con una línea de comandos (más o menos compleja y combinado sed, grep, tr, awk y demás) puedo obtener toda la información que necesito. Y si no fuese así, invocar a un shell script será suficiente para cosas más pregrinas; como por ejemplo obtener los scan de tablas completas en una base de datos u otros temas de ese estilo.

    La idea es usar vmstat, top o mejor aun nmon más pequeñas manipulaciones de texto para obtener los datos que me interesan.

    En cualquier caso la idea es sacar los comandos que se ejecuten en cada caso a un archivo de configuración.

    La idea no es monitorizar máquinas unix, para eso si que sería mejor usar una herramienta específica como ngios o similar, sino exponer datos de rendimiento de máquinas unix como contadores de rendimiento de windows.

    Gracias por tus comentarios!!! Cualquier idea o sugerencia que tengas sobre el tema será bienvenida!!!

  3. Hombre, no sé qué información detallada quieres sacar y supongo que lo que has descrito es sólo un ejemplo, pero me extraña que no encuentres MIBs de SNMP que no te den el resultado que necesites. De todas formas dependiendo de hasta donde quieras llegar, configurar el SNMP puede ser bastante tedioso con lo que igual no te compensa.

    De todas formas creo que sé por dónde vas. Crear contadores del performance monitor personalizados, registrados en un equipo que tiene una aplicación que monitoriza los unix que quieras ¿no? Puede quedar bastante bien, si.

    Por otro lado también sería interesante integrar esta funcionalidad en scriptlets de PowerShell, eso daría un montón de combinaciones posibles para el análisis de los datos después.

  4. Hola Rodrigo, ¿habría alguna manera de ocultar un poco la contraseña?

    A parte, la mayoría de sshd suelen estar configurados para que no se pueda acceder directamente como root 😛

    Un saludo desde León 🙂

  5. Vicente, buena puntualización!!!
    Esto solo es una prueba de concepto. En una aplicación real lo suyo es poner la contraseña en una sección del archivo de configuración que luego encriptariamos.

    Sobre los de conectarme como root, es necesario para obtener cierta información de rendimiento en algunas distribuciones. Depende un poco de si estamos en Solaris, en HP-UX o en Linux. De todas formas el usuario del SSH es algo que también sacaré al archivo de configuración cuando este código vaya ha ser para producción y no una prueba de concepto rápida.

    Gracias por tus comentarios!!!!

  6. Hay otras maneras entonces, puedes crear un usuario y un grupo que, simplemente, sea para ver el rendimiento, vamos, con permisos de ejecución sobre algunos comandos. Habría entonces que ser capaz de crear usuarios en la máquina objetivo, pero veo que acceder como root no es problema 😛

    A mí me parece que conectar como root no es adecuado. Cosa que no depende de la variante de Unix o Linux utilizada, eso se configura en el /etc/sshd.conf.

    Un saludo y gracias por tus post 🙂

  7. Vicente, excelente apreciación de nuevo. Creo que con sacar el usuario con el que SSH se conecta a configuración abro la puerta a que quien configure el entorno de pruebas pueda aplicar el principio de privilegios mínimos a voluntad.

    Lo que trataba de decir en mi anterior comentario no es que sea en ningún caso recomendable conectarse como root (sea por seguridad o sea por evitar disparos en el pie). Lo que trataba de decir es que tengo que dejar la puerta abierta pues no se que entornos ni que administradores me voy ha encontrar.

    Habrá quien me diga conectate como root, que esto es la red de pruebas y quien diga como root no se conecta aquí ni dios.

    Ahora, en mis pruebas, me ando conectando como root por simple comodidad, ya que como comenté, no todas las distribuciones dejan ver información sobre el rendimiento a cualquier usuario panchito.

    Resumiendo, yo solo quiero poder recolectar información usando comandos como vmstat, nmon, top y demás… logícamente, quien me proporcione el usuario espero que me proporcione uno solo con los mínimos privilegios necesarios.

    Gracias por comentar el tema, da gusto ver que hay gente que se toma la seguridad en serio.

    Un saludo!!!

  8. Saludos Rodrigo tengo una inquietud, la libreria Tamir.SharpSSH.dll se la puede utilzar en un Unix UX ? actualmente estoy probando la factibilidad de crear usuarios via Telnet o Ssh.
    Gracias por tu orientacion.

  9. Hola Daniel!!!

    Aunque no lo he probado, debería funcionar perfectamente. SSH es un estándar y la librería SharpSSH simplemente es un cliente.

    Un saludo!

  10. He probado la librería y la verdad que no me ha dado problemas, pero en ejecución de algunos comandos como useradd y otros la función RunCommand de SharpSsh no devuelve ningún estatus si la ejecución del comando se hizo correctamente, lo cual ha sido una decepción.

  11. Daniel, creo recordar que hay alguna otra función o incluso alguna sobrecarga de la función RunCommmand que si que devuelve información sobre el resultado de la ejecución.

  12. Hola,
    Podrian indicarme por que motivo se genera este error?

    Session.connect: System.Security.Cryptography.CryptographicException: No se pudo adquirir un proveedor de servicios criptográficos (CSP) CryptoAPI para esta implementación. at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize) at System.Security.Cryptography.RSACryptoServiceProvider..ctor() at Tamir.SharpSsh.jsch.jce.SignatureRSA.verify(Byte[] sig) at Tamir.SharpSsh.jsch.DHG1.next(Buffer _buf) at Tamir.SharpSsh.jsch.Session.connect(Int32 connectTimeout)
    Descripción: Excepción no controlada al ejecutar la solicitud Web actual. Revise el seguimiento de la pila para obtener más información acerca del error y dónde se originó en el código.

    Esto se presenta cuanto intento ejecutar el metodo

    sshExec.Connect();

    Gracias

  13. Estoy usando la librería Tamir.SharpSSH desde una aplicación .Net para recuperar la salida de un comando que ejecuta un shell, un .cmd, que compila una lista de fuentes, entre ellos procedimientos almacenados de Sybase y programas .SQR, pero el resultado que me retorna muestra información diferente a la que me muestra si lo hiciera por línea de comando en el Unix, la libreria me trae un log como si intentara compilar por mas de una vez la lista de programas, es decir comienza a compilar y llega hasta el sexto fuente de 15 y luego comienza por el primero nuevamente hasta que lo hace completamente, alguna sugerencia

  14. Jimmy, a mi me pasa algo parecido. El problema es que cuando tu ejecutas un comando desde la libreria, este se ejecuta desde la el directorio base del usuario. Por lo tanto, si tienes algo en el comando que referencia de manera relativa a otros directorios, la ejecución será diferente que si lo ejecutas en la máquina en algún otro directorio.

    Me he explicado fatal, pero espero que se entienda. Si alguien sabe la manera de solucionarlo seria de agreder.

    Gracias!

  15. Hola. el scrip propuesto servira para ejecutar un comando que en realidad estara controlando por ejemplo un libreria de cintas conectada al servidor.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *