¿Por qué ssh-agent y ssh-add no funcionan juntos en un script bash?

¿Por qué ssh-agent y ssh-add no funcionan juntos en un script bash?

Escribí un script donde me gustaría que iniciara el ssh-agentprimero para ejecutar el agente en segundo plano y configurar las variables de entorno apropiadas para la instancia de shell actual. Sin embargo, en la segunda parte del script también me gustaría agregar mi clave SSH privada para poder conectarme a mi servidor.

Actualmente, ninguno de los comandos del script funciona entre sí. ¿Alguien puede ayudarme a comprender correctamente lo que estoy haciendo mal?

#!/bin/bash

exec ssh-agent bash
sleep 5s
ssh-add /media/MyUSB/.ssh/id_00123 &

Además, mientras utilizo el depurador incorporado, bashpuedo ver que solo funciona la primera sección del script (es decir exec ssh-agent bash).

Respuesta1

Tantos aspectos interesantes en un guión tan pequeño.


Comprensiónssh-agent

Empecemos por lo que ssh-agentestá diseñado para hacer. Lo ejecutas ssh-agentcuando quieres un proceso que se encuentra allí, escucha en algún socket (es un tipo dearchivopara comunicación bidireccional entre procesos) y atiende solicitudes de programas como ssh-addo sshque se conectan al socket. Los programas hablarán con el agente y almacenarán, manipularán o utilizarán claves privadas.

Cualquier programa que quiera utilizar un agente necesita conocer la ruta al socket en el que escucha el agente. Si un programa conoce la ruta, puede utilizar el socket para comunicarse con el agente.

Una vez se tomó una decisión de diseño: cualquier programa que quiera conocer la ruta al socket de un agente de autenticación debe verificar la SSH_AUTH_SOCKvariable en su propio entorno, el valor de la variable es la ruta. Fue una decisión (quiero decir que las cosas podrían diseñarse de alguna otra manera, por ejemplo, los programas podrían diseñarse para aceptar este camino a través de argumentos de línea de comandos cada vez), pero fue una muy buena decisión.

Fue una muy buena decisión porque el entorno se hereda por defecto. Esto significa que necesita configurar la SSH_AUTH_SOCKvariable de entorno para un proceso (por ejemplo, un shell) y todos sus descendientes la heredarán (a menos que algunos de ellos elijan deliberadamente alterar su entorno o crear un hijo con un entorno alterado). A modo de comparación: pasar la ruta como argumento de la línea de comandos cada vez que desee ejecutar algo que debería comunicarse con el agente requiere escritura adicional; y querrás almacenar la ruta en algún lugar, probablemente en una variable de todos modos. Así que aquí lo tienes, el nombre de la variable está estandarizado y los programas interesados ​​lo comprueban automáticamente.

Otra opción era almacenar la ruta en un archivo de texto en una ubicación fija, o incluso crear un socket en una ubicación fija en primer lugar. Pero a veces desea que algunos programas utilicen un agente (un socket) y otros programas utilicen otro agente (otro socket). Hacer que dos programas vean archivos diferentes en la misma ubicación es difícil. Hacer que dos programas vean diferentes variables de entorno es fácil.

Por tanto, los programas interesados ​​deberían comprobarlo SSH_AUTH_SOCKen su entorno. ¿Cómo podemos nosotros o algo establecer esta variable en el valor correcto en el entorno de un proceso? Sin un depurador hay dos maneras:

  1. O el padre conoce el valor y cuando genera un hijo, lo configura SSH_AUTH_SOCKcon el valor correcto en el entorno para el niño (un acto de heredar sin cambios SSH_AUTH_SOCKdel padre puede interpretarse como "el padre configura esto sin hacer nada");

  2. o el proceso aprende el valor de alguna otra manera y modifica su propio entorno.

Por lo tanto, ssh-agentadmite dos métodos para iniciarlo:

  1. ssh-agent command …
    

    Aquí ssh-agentcrea un socket y se prepara para servir programas futuros que se conecten al socket. Luego funciona command …como su hijo con SSH_AUTH_SOCKel valor adecuado en el entorno para el niño. El hijo (o cualquier descendiente que herede la variable) puede encontrar fácilmente el socket, pero otros procesos no tan fácilmente. Cuando commandtermina, también lo hace ssh-agent(incluso si hay nietos).

  2. ssh-agent   # but don't use it exactly this way
    

    Aquí ssh-agentse bifurca hacia el fondo, es decir, crea una copia secundaria de sí mismo y no espera a que salga. El niño se desconecta de los flujos estándar del padre y del terminal, no saldrá por sí solo. El niño será el verdadero agente que permanecerá. El padre saldrá por sí solo, pero antes de que esto suceda, se imprime el código de shell. El código de shell, cuando es evaluado por un shell, hace que el shell modifique su propio entorno, por lo que SSH_AUTH_SOCKse coloca allí el valor correcto.Pero el caparazón tiene queevaluarla salida, no solo ejecutarssh-agent, entonces la forma correcta es como:

    eval "$(ssh-agent)"
    

    Después de esto, el shell que se ejecuta evaltiene la variable correcta (de hecho: variables) en su entorno y de ahora en adelante, los comandos como ssh-addejecutar desde este shell encontrarán el agente porque heredarán la variable. Salir del shell no finaliza el agente, por lo que en algún momento antes de salir del shell es posible que desee invocarlo ssh-agent -k(o, si también desea desarmar las variables: eval "$(ssh-agent -k)"). Un agente para el que no existe un proceso que tenga el valor correcto SSH_AUTH_SOCKes prácticamente inútil.


¿Qué hay de malo en tu guión?

Y ahora –finalmente– a tu guión. Este es tu guión:

#!/bin/bash

exec ssh-agent bash
sleep 5s
ssh-add /media/MyUSB/.ssh/id_00123 &

Lo primero que hace el guión es exec ssh-agent bash. execle dice al shell que interpreta el script que se reemplace con el comando, que es ssh-agent bash. El shell lo hace y ssh-agentcomienza uno nuevo bash(es el método 1 de arriba). Tiene bashel valor correcto de SSH_AUTH_SOCK, es interactivo, imprime un mensaje y le permite ejecutar comandos (incluidos los comandos que necesitan SSH_AUTH_SOCK). Si su caparazón interactivo original era bashentonces es posible que no se dé cuenta de que ahora se encuentra en un entorno bash. Puede interpretar la existencia de SSH_AUTH_SOCKcomo una confirmación de que ssh-agentse ha modificado el entorno de su shell original. No, todavía estás en medio de tu guión.

Bueno, no exactamente en el medio. Si sale de esto bash, sleepel resto no se ejecutará, porque el shell que interpreta el script se ha reemplazado por ssh-agent. En cierto sentido eres uno exitantes del final del guión.

Si su método para ejecutar el script era como ./myscript, exitlo devolverá al shell original. Si tu método fuera como. ./myscriptosource myscriptluego exitactuará como si hubiera salido del shell original, porque el shell original era el shell que interpretaba el script y se reemplazó con ssh-agentel que está a punto de salir exitdel shell actual; esto puede reforzar la impresión de que estaba en (y ahora está saliendo) del caparazón original.


La solución

En la pregunta ha indicado explícitamente su objetivo:

[…] variables de entorno apropiadas para la instancia de shell actual. […]

Para modificar el entorno del shell actual, el script debe utilizar el método 2 anterior. El shell actual tiene que ser el shell de interpretación, es decir, el script debe tener origen. El shell no debe execincluir nada, ya que no desea que el shell sea reemplazado por nada. Solución de ejemplo:

#!/usr/bin/false
[ -n "$SSH_AUTH_SOCK" ] || eval "$(ssh-agent)"
ssh-add /media/MyUSB/.ssh/id_00123

Hay más cosas que se han mejorado:

  • #!/usr/bin/falseya que el shebang garantiza que el script no hará nada y fallará si lo ejecuta (sin darse cuenta) en lugar de obtenerlo. Otras estrategias están aquí:Estrategia para olvidarse de ejecutar un script consource.sin un tingladoo con un shebang apuntando a bashu shotro shell compatible, el script ejecutado (sin fuente) iniciaría un nuevo agente, le agregaría la clave y saldría. Todo esto sin afectar el entorno de su shell actual, por lo que el agente permanecería allí en vano, casi inaccesible. Debería esforzarse un poco en encontrarlo y eliminarlo, o algo de esfuerzo en encontrar su socket y configurarlo manualmente SSH_AUTH_SOCKen el entorno de su shell; o simplemente lo dejarías ser. falseya que el asunto impide este escenario inconveniente.

  • [ -n "$SSH_AUTH_SOCK" ]comprueba si $SSH_AUTH_SOCKse expande a una cadena no vacía. Una cadena vacía indica que no hay ningún agente disponible, mientras que una cadena no vacía indica que probablemente haya un agente. El script comienza uno nuevo ssh-agentsolo si la cadena está vacía. Esta es una precaución básica contra un escenario en el que (sin darse cuenta) obtiene el script por segunda vez, crea un nuevo agente de autenticación y pierde variables asociadas con el agente anterior que seguirá ejecutándose inútilmente.

  • No es necesario sleep. ssh-agenten nuestro script sale cuando el agente (es decir, su hijo en segundo plano) está listo. Puedes hacerlo ssh-addde inmediato.

  • ssh-addestá aquí como un comando sincrónico. Ejecutarlo de forma asincrónica (con &, como intentó hacer) probablemente no le ahorrará mucho tiempo. Puedes probar. Pero lo más probable es que obtenga el script de un shell interactivo con el control de trabajo habilitado y, por lo tanto &(si lo coloca allí), contaminará su terminal con un mensaje como [1]+ Done ….

información relacionada