Script Bash pour configurer un tunnel SSH temporaire

132

Sur Cygwin, je souhaite qu'un script Bash:

  1. Créez un tunnel SSH vers un serveur distant.
  2. Effectuez un travail local qui utilise le tunnel.
  3. Puis fermez le tunnel.

La partie arrêt m'a perplexe.

Actuellement, j'ai une solution boiteuse. Dans un shell, j'exécute ce qui suit pour créer un tunnel:

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

Ensuite, dans une autre fenêtre shell, je fais mon travail:

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

Enfin, lorsque j'ai terminé, je ferme la première fenêtre shell pour tuer le tunnel.

J'aimerais faire tout cela dans un seul script comme:

# Create tunnel
# Do work
# Kill tunnel

Comment puis-je suivre le processus du tunnel pour savoir lequel tuer?

jm.
la source
J'ai écrit un script qui aiderait à faire du tunneling ssh, vous pouvez le vérifier à: github.com/gdbtek/ssh-tunneling.git
Nam Nguyen

Réponses:

324

Vous pouvez le faire proprement avec un «socket de contrôle» ssh. Pour parler à un processus SSH déjà en cours d'exécution et obtenir son pid, tuez-le, etc. Utilisez le 'socket de contrôle' (-M pour le maître et -S pour le socket) comme suit:

$ 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. 

Notez que my-ctrl-socket sera un fichier réel qui est créé.

J'ai obtenu cette information d' une réponse très RTFM sur la liste de diffusion OpenSSH .

Chris McCormick
la source
6
C'est la meilleure réponse que j'ai vue sur le sujet jusqu'à présent. Merci beaucoup, ça devrait être celui qui a été accepté. J'utilise ceci pour me connecter à ma VM Vagrant et exécuter un script de mise à jour FlywayDB.
Christian
2
Apparemment, les prises de contrôle ne fonctionnent pas partout. Par exemple, je suis Operation not permittedsur l'environnement d'intégration continue drone.io:muxserver_listen: link mux listener ssh-ctrl-socket.wsASkszgSBlK7kqD => ssh-ctrl-socket: Operation not permitted
Mikko Ohtamaa
Alors, qu'arrive-t-il au my-ctrl-socketfichier après son exécution? Lorsque je le fais ls -ladans le dossier actuel, je ne peux plus voir le fichier.
sachinruk
2
Si vous l'utilisez dans un script, vous devez attendre que le socket de contrôle soit disponible pendant quelques secondes. Ma solution:while [ ! -e $ctrl_socket ]; do sleep 0.1; done
Adam Wallner
quand je fais cela, je reçois open failed: administratively prohibited: open failedet je ne pense pas que cela ouvre le tunnel
Andy Ray
21

Vous pouvez dire à SSH de se mettre en arrière-plan avec l'option -f mais vous n'obtiendrez pas le PID avec $ !. De plus, au lieu de mettre votre script en veille un laps de temps arbitraire avant d'utiliser le tunnel, vous pouvez utiliser -o ExitOnForwardFailure = yes avec -f et SSH attendra que tous les transferts de ports distants soient établis avec succès avant de se placer en arrière-plan. Vous pouvez grep la sortie de ps pour obtenir le PID. Par exemple, vous pouvez utiliser

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

et assurez-vous que vous obtenez le PID souhaité

Guy du Maine
la source
Vous ne le réalisez peut-être pas, mais c'est une sorte de génie. Je cherchais un moyen de suivre les PID du tunnel SSH et j'ai presque fini par utiliser les scripts de service systemd. Plus maintenant: je peux grep le processus SSH dont j'ai besoin en utilisant le nom du tunnel. Cette idée m'a complètement sauté. Merci beaucoup!
aexl
19
  • Vous pouvez dire sshde passer en arrière-plan avec &et de ne pas créer un shell de l'autre côté (ouvrez simplement le tunnel) avec un indicateur de ligne de commande (je vois que vous l'avez déjà fait avec -N).
  • Enregistrez le PID avec PID=$!
  • Faites vos affaires
  • kill $PID

EDIT: fixe $? à $! et ajouté le &

ZeissS
la source
3
Si mon script meurt quelque part avant d'arriver au KILL, je dois faire attention à gérer cela.
jm.
2
@jm: trap 'kill $PID' 1 2 15couvrira de nombreux cas d'échec de script.
Norman Ramsey
3
Pour que cela fonctionne de manière fiable, j'ai dû «dormir» un peu APRÈS avoir créé le tunnel, mais avant de l'utiliser.
jm.
@NormanRamsey Je pense que vous voulez dire trap "kill $PID", parce que bash interpolera uniquement les variables à l'intérieur de chaînes entre
guillemets
1
@JuanCaicedo La distinction ne serait importante que si la PIDvariable était redéfinie ultérieurement. La variable est soit développée lorsque la fonction trapintégrée est appelée (l'approche de l'OP) ou lorsqu'un signal a été capté (votre approche); les deux approches produisent ici le même résultat.
Witiko
4

Je préfère lancer un nouveau shell pour des tâches séparées et j'utilise souvent la combinaison de commandes suivante:

  $ sudo bash; exit

ou parfois:

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

Ces commandes créent un shell imbriqué où je peux faire tout mon travail; lorsque j'ai terminé, j'appuie sur CTRL-D et le shell parent se nettoie et se ferme également. Vous pouvez facilement lancer bash;votre script de tunnel ssh juste avant la killpartie pour que lorsque vous vous déconnectez du shell imbriqué, votre tunnel soit fermé:

#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID
trop de php
la source
Très intéressant. Cela peut mieux gérer le problème du «piège». Devra l'essayer.
jm.
2

Vous pouvez lancer le sshavec un &a la fin, pour le mettre en arrière-plan et saisir son identifiant en le faisant. Ensuite, il vous suffit de faire un killde cet identifiant lorsque vous avez terminé.

Valentin Rocher
la source
Soyez conscient si vous utilisez l'esperluette ("&"). C'est une approche laide car vous devrez déterminer la connexion réelle établie pour vous-même. Cela peut entraîner l'exécution d'un code supplémentaire qui n'attend pas que la connexion réelle soit complètement établie. De plus, la connexion ne sera pas supprimée automatiquement si le script se rompt.
Jonathan
2

Une autre option potentielle - si vous pouvez installer le package expect, vous devriez être capable de tout script. Quelques bons exemples ici: http://en.wikipedia.org/wiki/Expect

Phil Pelanne
la source