Bash script para configurar un túnel SSH temporal

132

En Cygwin, quiero un script Bash para:

  1. Cree un túnel SSH a un servidor remoto.
  2. Haga un trabajo local que use el túnel.
  3. Luego cierra el túnel.

La parte de apagado me tiene perplejo.

Actualmente, tengo una solución poco convincente. En un shell ejecuto lo siguiente para crear un túnel:

# Create the tunnel - this works! It runs forever, until the shell is quit.
ssh -nNT -L 50000:localhost:3306 jm@sampledomain.com

Luego, en otra ventana de shell, hago mi trabajo:

# Do some MySQL stuff over local port 50000 (which goes to remote port 3306)

Finalmente, cuando termino, cierro la primera ventana de shell para matar el túnel.

Me gustaría hacer todo esto en un script como:

# Create tunnel
# Do work
# Kill tunnel

¿Cómo hago un seguimiento del proceso del túnel, para saber cuál matar?

jm.
fuente
Escribí un script que ayudaría a hacer un túnel ssh, puede consultarlo en: github.com/gdbtek/ssh-tunneling.git
Nam Nguyen

Respuestas:

324

Puede hacerlo limpiamente con un ssh 'control socket'. Para hablar con un proceso SSH que ya se está ejecutando y obtener su pid, matarlo, etc. Utilice el 'socket de control' (-M para master y -S para socket) de la siguiente manera:

$ ssh -M -S my-ctrl-socket -fnNT -L 50000:localhost:3306 jm@sampledomain.com
$ ssh -S my-ctrl-socket -O check jm@sampledomain.com
Master running (pid=3517) 
$ ssh -S my-ctrl-socket -O exit jm@sampledomain.com
Exit request sent. 

Tenga en cuenta que my-ctrl-socket será un archivo real creado.

Recibí esta información de una respuesta muy RTFM en la lista de correo de OpenSSH .

Chris McCormick
fuente
66
Esta es la mejor respuesta que he visto sobre el tema hasta ahora. Muchas gracias, debería ser el aceptado. Lo uso para conectarme a mi VM Vagrant y ejecutar un script de actualización de FlywayDB.
Christian
2
Aparentemente, los enchufes de control no funcionan en todas partes. Por ejemplo, me Operation not permittedsubo al entorno de integración continua drone.io:muxserver_listen: link mux listener ssh-ctrl-socket.wsASkszgSBlK7kqD => ssh-ctrl-socket: Operation not permitted
Mikko Ohtamaa
Entonces, ¿qué pasa con el my-ctrl-socketarchivo después de ejecutar esto? Cuando lo hago ls -laen la carpeta actual ya no puedo ver el archivo.
sachinruk
2
Si lo usa en un script, debe esperar a que el socket de control esté disponible durante unos segundos. Mi solución:while [ ! -e $ctrl_socket ]; do sleep 0.1; done
Adam Wallner
cuando hago esto me sale open failed: administratively prohibited: open failedy no creo que esté abriendo el túnel
Andy Ray
21

Puede decirle a SSH que se ejecute en segundo plano con la opción -f, pero no obtendrá el PID con $ !. Además, en lugar de que su script duerma una cantidad arbitraria de tiempo antes de usar el túnel, puede usar -o ExitOnForwardFailure = yes con -f y SSH esperará a que todos los puertos remotos se establezcan con éxito antes de colocarse en segundo plano. Puede grep la salida de ps para obtener el PID. Por ejemplo, puedes usar

...
ssh -Cfo ExitOnForwardFailure=yes -NL 9999:localhost:5900 $REMOTE_HOST
PID=$(pgrep -f 'NL 9999:')
[ "$PID" ] || exit 1
...

y asegúrese de obtener el PID deseado

Maine Guy
fuente
Puede que no te des cuenta, pero esto es una especie de genio. Estaba buscando una manera de rastrear los PID del túnel SSH y casi terminé usando los scripts de servicio systemd. Ya no: puedo manipular el proceso SSH que necesito usando el nombre del túnel. Esta idea de alguna manera me ha omitido por completo. ¡Muchas gracias!
aexl
19
  • Puede indicar sshque vaya al fondo &y no cree un shell en el otro lado (solo abra el túnel) con una bandera de línea de comando (veo que ya lo hizo -N).
  • Guarde el PID con PID=$!
  • Haz tus cosas
  • kill $PID

EDITAR: fijo $? a $! y agregó el &

ZeissS
fuente
3
Si mi script muere en algún lugar antes de llegar al KILL, tengo que tener cuidado de manejar eso.
jm.
2
@jm: trap 'kill $PID' 1 2 15cubrirá muchos casos de falla de script.
Norman Ramsey
3
Para que esto funcione de manera confiable, tuve que "dormir" un poco DESPUÉS de crear el túnel, pero antes de usarlo.
jm.
@ NormanRamsey Creo que te refieres trap "kill $PID", porque bash solo interpolará variables dentro de cadenas dobles entre comillas
JuanCaicedo
1
@JuanCaicedo La distinción solo sería importante si la PIDvariable se redefiniera más adelante. La variable se expande cuando trapse llama al incorporado (el enfoque del OP) o cuando se ha captado una señal (su enfoque); ambos enfoques producen el mismo resultado aquí.
Witiko
4

Prefiero lanzar un nuevo shell para tareas separadas y a menudo uso la siguiente combinación de comandos:

  $ sudo bash; exit

o algunas veces:

  $ : > sensitive-temporary-data.txt; bash; rm -f sensitive-temporary-data.txt; exit

Estos comandos crean un shell anidado donde puedo hacer todo mi trabajo; cuando termino, presiono CTRL-D y el shell principal se limpia y sale también. Puede lanzar fácilmente bash;su script de túnel ssh justo antes de la killparte para que cuando cierre la sesión del shell anidado su túnel se cierre:

#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID
demasiado php
fuente
Muy interesante. Esto puede manejar mejor el problema de la "trampa". Tendré que intentarlo.
jm.
2

Puede iniciar el sshcon un &final, para ponerlo en segundo plano y tomar su identificación cuando lo haga. Entonces solo tiene que hacer una killde esa identificación cuando haya terminado.

Valentin Rocher
fuente
Tenga en cuenta si utiliza el signo "(" & "). Es un enfoque feo ya que tendrá que determinar la conexión real establecida por usted mismo. Puede provocar que se ejecute más código que no está esperando que la conexión real se establezca por completo. Además, la conexión no se eliminará automáticamente si el script se rompe.
Jonathan