
Escribí un script donde me gustaría que iniciara el ssh-agent
primero 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, bash
puedo 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-agent
está diseñado para hacer. Lo ejecutas ssh-agent
cuando 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-add
o ssh
que 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_SOCK
variable 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_SOCK
variable 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_SOCK
en 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:
O el padre conoce el valor y cuando genera un hijo, lo configura
SSH_AUTH_SOCK
con el valor correcto en el entorno para el niño (un acto de heredar sin cambiosSSH_AUTH_SOCK
del padre puede interpretarse como "el padre configura esto sin hacer nada");o el proceso aprende el valor de alguna otra manera y modifica su propio entorno.
Por lo tanto, ssh-agent
admite dos métodos para iniciarlo:
-
ssh-agent command …
Aquí
ssh-agent
crea un socket y se prepara para servir programas futuros que se conecten al socket. Luego funcionacommand …
como su hijo conSSH_AUTH_SOCK
el 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. Cuandocommand
termina, también lo hacessh-agent
(incluso si hay nietos). -
ssh-agent # but don't use it exactly this way
Aquí
ssh-agent
se 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 queSSH_AUTH_SOCK
se 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
eval
tiene la variable correcta (de hecho: variables) en su entorno y de ahora en adelante, los comandos comossh-add
ejecutar 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 invocarlossh-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 correctoSSH_AUTH_SOCK
es 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
. exec
le dice al shell que interpreta el script que se reemplace con el comando, que es ssh-agent bash
. El shell lo hace y ssh-agent
comienza uno nuevo bash
(es el método 1 de arriba). Tiene bash
el 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 bash
entonces es posible que no se dé cuenta de que ahora se encuentra en un entorno bash
. Puede interpretar la existencia de SSH_AUTH_SOCK
como una confirmación de que ssh-agent
se 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
, sleep
el resto no se ejecutará, porque el shell que interpreta el script se ha reemplazado por ssh-agent
. En cierto sentido eres uno exit
antes del final del guión.
Si su método para ejecutar el script era como ./myscript
, exit
lo devolverá al shell original. Si tu método fuera como. ./myscript
osource myscript
luego exit
actuará como si hubiera salido del shell original, porque el shell original era el shell que interpretaba el script y se reemplazó con ssh-agent
el que está a punto de salir exit
del 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 exec
incluir 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/false
ya 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 abash
ush
otro 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 manualmenteSSH_AUTH_SOCK
en el entorno de su shell; o simplemente lo dejarías ser.false
ya que el asunto impide este escenario inconveniente.[ -n "$SSH_AUTH_SOCK" ]
comprueba si$SSH_AUTH_SOCK
se 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 nuevossh-agent
solo 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-agent
en nuestro script sale cuando el agente (es decir, su hijo en segundo plano) está listo. Puedes hacerlossh-add
de inmediato.ssh-add
está 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 …
.