He estado buscando respuestas aquí en superusuario, sin embargo, ninguna respuesta que encontré aborda mi caso de uso.
Quiero encadenar dos comandos que uso y que me permiten atravesar un túnel:
ssh -L 22:hostB:22 user@hostA
y
ssh -L 10002:localhost:10001 user@localhost
Ahora mismo lo que hago es ingresar el primer comando, abrir otra terminal e ingresar el segundo. Si hago algo como
ssh … && ssh …
el segundo comando se ejecuta sólo después de que "salgo" de la primera conexión.
Las opciones ssh más avanzadas como jumphost no funcionan debido a la configuración del servidor en el hostA, así que estoy bastante seguro de que esta es la única opción para tener acceso al puerto 10001 de esta manera.
¿Hay alguna manera de encadenar esos dos comandos en un alias o usar algún script bash simple con tiempos de espera, etc.?
Respuesta1
Supongo que no puedes hacerlo sólo ssh -L 10002:hostB:10001 user@hostA
porque
- o cualquier cosa que escuche en B
10001
se une solo a la interfaz local, - o el firewall en B no permite la conexión
hostB:10001
desde A, - o lo que sea.
ssh … && ssh …
el segundo comando se ejecuta sólo después de que "salgo" de la primera conexión.
Sí, esto es normal. La parte anterior &&
debe terminar y devolver el estado de salida antes de que se ejecute el siguiente comando (o no, según el estado).
Esto ejecutará el segundo ssh
casi inmediatamente pero tiene errores:
(ssh…&) && ssh…
Es defectuoso porque el primero ssh
no tiene impacto sobre el segundo. El segundo probablemente correrá mucho antes que el primero establezca el túnel. Pedir contraseñas también puede resultar problemático.
Para este caso de uso ssh
tiene -f
la opción. Deman 1 ssh
:
-f
Solicitassh
pasar a segundo plano justo antes de la ejecución del comando. […]
Entonces el comando básico puede ser:
ssh -fNL 22:hostB:22 user@hostA && ssh -NL 10002:localhost:10001 user@localhost
Depende de usted si desea utilizar -N
el segundo ssh
o no. Ahora el truco son las primeras ssh
bifurcaciones en segundo plano después de solicitar la contraseña (si corresponde) y después de establecer el primer túnel, por lo que temporalmente hay dos "primeros" ssh
procesos. El que está al fondo comienza a manejar el túnel; el que está en primer plano sale, esto permite &&
trabajar.
Probablemente quieras -o ExitOnForwardFailure=yes
. Si no se puede establecer el primer túnel, el primero ssh
saldrá sin éxito y el segundo no funcionará. El comando mejorado es:
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
user@hostA &&
ssh -NL 10002:localhost:10001 user@localhost
Tenga en cuenta que cuando el segundo ssh
sale, el primero (en segundo plano) permanece. La ventaja es que puedes ejecutar el único segundo ssh
después. La desventaja es que tendrás que eliminar el primero manualmente si no quieres que permanezca. Teniendo esto en cuenta, considere el siguiente enfoque:
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
user@hostA
ssh -NL 10002:localhost:10001 user@localhost
Tenga en cuenta que no hay &&
. Ahora bien, si el antiguo primero ssh
todavía se está ejecutando, el nuevo fallará porque no podrá conectarse al puerto. El segundo ssh
funcionará independientemente, utilizará el primer túnel y no importa la antigüedad del túnel.
Puede pasar que no haya ningún viejo ssh
y el primero ssh
falle por los motivos que sean. Si es así, el segundo se ejecutará y probablemente fallará (conexión rechazada) porque no hay nada escuchando en el puerto 22
.
Aun así, es posible que no quieras dejar un ssh
proceso "inactivo", sino que quieras finalizarlo automáticamente después de que ssh
salga el segundo. Evidentemente killall ssh
puede provocar daños colaterales. Busquemos algo más sutil:
-M
Coloca alssh
cliente en modo "maestro" para compartir la conexión. […][…]
-O ctl_cmd
Controlar un proceso maestro de multiplexación de conexión activa. […][…]
-S ctl_path
Especifica la ubicación de un zócalo de control para compartir conexión […]
#!/bin/sh
socket=/tmp/hostA.socket
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
-MS "$socket"
user@hostA &&
{
ssh -NL 10002:localhost:10001 user@localhost
ssh -S "$socket" -O exit dummy
}
Después de que termine el segundo ssh
, el tercero le dirá al primero que salga. En el tercer ssh
comando lo que importa es el socket, no el nombre del host; aún así necesitas proporcionar algún nombre de host, por lo tanto dummy
. El primero ssh
quitará el enchufe antes de salir, no debe quedar basura.
Todavía hay algunas preocupaciones:
- Si se interrumpe el guión,
ssh
quedará el primero. O suelte&&
o envíe-O exit
desde el principio por si acaso (sería el cerossh
). Si el primero
ssh
muere abruptamente, el encaje puede permanecer. Hará que cualquier nuevo primerssh
fracaso. Una buena idea puede ser expandir el cerossh
de esta manera:ssh -S "$socket" -O exit dummy || rm "$socket"
/tmp/hostA.socket
Puede que no sea la mejor ubicación para el enchufe. Enssh_config
el equivalente de-S
la opción de línea de comando esControlPath
. Mira quéman 5 ssh_config
dice al respecto:Se recomienda que cualquier
ControlPath
uso para compartir conexiones oportunistas incluya al menos%h
,%p
y%r
(o alternativamente%C
) y se coloque en un directorio en el que otros usuarios no puedan escribir.Un "directorio en el que otros usuarios no pueden escribir" puede ser
/run/user/$UID
, si su sistema operativo lo admite (creo que essystemd
cosa de ).