Bash-Skript zum Einrichten eines temporären SSH-Tunnels

131

Auf Cygwin möchte ich, dass ein Bash-Skript:

  1. Erstellen Sie einen SSH-Tunnel zu einem Remote-Server.
  2. Arbeiten Sie lokal mit dem Tunnel.
  3. Dann den Tunnel schließen.

Der Shutdown-Teil hat mich ratlos gemacht.

Derzeit habe ich eine lahme Lösung. In einer Shell führe ich Folgendes aus, um einen Tunnel zu erstellen:

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

Dann mache ich in einem anderen Shell-Fenster meine Arbeit:

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

Wenn ich fertig bin, schließe ich das erste Shell-Fenster, um den Tunnel zu töten.

Ich möchte dies alles in einem Skript tun, wie:

# Create tunnel
# Do work
# Kill tunnel

Wie verfolge ich den Tunnelprozess, damit ich weiß, welchen ich töten soll?

jm.
quelle
Ich habe ein Skript geschrieben, das beim SSH-Tunneln helfen würde. Sie können es unter folgender Adresse
Nam Nguyen

Antworten:

323

Sie können dies sauber mit einem ssh 'Control Socket' tun. Um mit einem bereits laufenden SSH-Prozess zu sprechen und dessen PID zu erhalten, ihn zu beenden usw. Verwenden Sie den 'Control Socket' (-M für Master und -S für Socket) wie folgt:

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

Beachten Sie, dass my-ctrl-socket eine tatsächliche Datei ist, die erstellt wird.

Ich habe diese Informationen von einer sehr RTFM-Antwort auf der OpenSSH-Mailingliste erhalten .

Chris McCormick
quelle
6
Dies ist die beste Antwort, die ich bisher zu diesem Thema gesehen habe. Vielen Dank, es sollte das akzeptierte sein. Ich verwende dies, um eine Verbindung zu meiner Vagrant-VM herzustellen und ein FlywayDB-Aktualisierungsskript auszuführen.
Christian
2
Anscheinend funktionieren Steuerbuchsen nicht überall. Zum Beispiel bekomme ich Operation not permittedauf drone.io kontinuierliche Integrationsumgebung:muxserver_listen: link mux listener ssh-ctrl-socket.wsASkszgSBlK7kqD => ssh-ctrl-socket: Operation not permitted
Mikko Ohtamaa
Was passiert nun mit der my-ctrl-socketDatei, nachdem diese ausgeführt wurde? Wenn ich es ls -laim aktuellen Ordner mache, kann ich die Datei nicht mehr sehen.
Sachinruk
2
Wenn Sie es in einem Skript verwenden, müssen Sie einige Sekunden warten, bis der Control Socket verfügbar ist. Meine Lösung:while [ ! -e $ctrl_socket ]; do sleep 0.1; done
Adam Wallner
Wenn ich das mache, bekomme ich open failed: administratively prohibited: open failedund ich glaube nicht, dass es den Tunnel öffnet
Andy Ray
20

Sie können SSH mit der Option -f anweisen, sich selbst in den Hintergrund zu stellen, aber Sie erhalten die PID nicht mit $!. Anstatt Ihr Skript eine beliebige Zeitspanne ruhen zu lassen, bevor Sie den Tunnel verwenden, können Sie -o ExitOnForwardFailure = yes mit -f verwenden. SSH wartet, bis alle Remote-Port-Weiterleitungen erfolgreich eingerichtet wurden, bevor Sie sich in den Hintergrund stellen. Sie können die Ausgabe von ps abrufen, um die PID zu erhalten. Zum Beispiel können Sie verwenden

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

und seien Sie ziemlich sicher, dass Sie die gewünschte PID erhalten

Maine Guy
quelle
Sie können es nicht erkennen, aber das ist eine Art Genie. Ich suchte nach einer Möglichkeit, die SSH-Tunnel-PIDs zu verfolgen, und hätte fast die systemd-Dienstskripte verwendet. Nicht mehr: Ich kann den benötigten SSH-Prozess anhand des Tunnelnamens abrufen. Diese Idee hat mich irgendwie völlig übersprungen. Vielen Dank!
Aexl
19
  • Sie können festlegen ssh, dass Sie &mit einem Befehlszeilenflag in den Hintergrund gehen und keine Shell auf der anderen Seite erstellen (öffnen Sie einfach den Tunnel) (ich sehe, dass Sie dies bereits getan haben -N).
  • Speichern Sie die PID mit PID=$!
  • Mach deine Sachen
  • kill $PID

EDIT: $ behoben? zu $! und fügte das & hinzu

ZeissS
quelle
3
Wenn mein Skript irgendwo stirbt, bevor es zum KILL kommt, muss ich vorsichtig sein, um damit umzugehen.
jm.
2
@jm: trap 'kill $PID' 1 2 15deckt viele Fälle von Skriptfehlern ab.
Norman Ramsey
3
Damit dies zuverlässig funktioniert, musste ich nach dem Erstellen des Tunnels ein wenig "schlafen", aber bevor ich ihn benutzte.
jm.
@NormanRamsey Ich denke, Sie meinen trap "kill $PID", weil Bash nur Variablen in doppelt zitierten Zeichenfolgen interpoliert
JuanCaicedo
1
@JuanCaicedo Die Unterscheidung wäre nur wichtig, wenn die PIDVariable später neu definiert würde. Die Variable wird entweder erweitert, wenn die trapintegrierte Funktion aufgerufen wird (Ansatz des OP) oder wenn ein Signal abgefangen wurde (Ihr Ansatz). Beide Ansätze führen hier zum gleichen Ergebnis.
Witiko
4

Ich starte lieber eine neue Shell für separate Aufgaben und verwende häufig die folgende Befehlskombination:

  $ sudo bash; exit

oder manchmal:

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

Diese Befehle erstellen eine verschachtelte Shell, in der ich meine gesamte Arbeit erledigen kann. Wenn ich fertig bin, drücke ich STRG-D und die übergeordnete Shell wird aufgeräumt und auch beendet. Sie können leicht bash;in Ihr SSH-Tunnelskript direkt vor dem killTeil werfen, sodass Ihr Tunnel geschlossen wird, wenn Sie sich von der verschachtelten Shell abmelden:

#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID
zu viel php
quelle
Sehr interessant. Dies kann das "Trap" -Problem besser behandeln. Muss es versuchen.
jm.
2

Sie können das sshmit einem &Ende starten , um es in den Hintergrund zu stellen und dabei seine ID abzurufen. Dann müssen Sie nur noch eine killdieser IDs erstellen, wenn Sie fertig sind.

Valentin Rocher
quelle
Beachten Sie, wenn Sie das kaufmännische Und ("&") verwenden. Es ist ein hässlicher Ansatz, da Sie die tatsächliche Verbindung selbst bestimmen müssen. Es kann dazu führen, dass weiterer Code ausgeführt wird, der nicht darauf wartet, dass die eigentliche Verbindung vollständig hergestellt wird. Außerdem wird die Verbindung nicht automatisch unterbrochen, wenn das Skript unterbrochen wird.
Jonathan
2

Eine weitere mögliche Option: Wenn Sie das Expect-Paket installieren können, sollten Sie in der Lage sein, das Ganze zu skripten. Einige gute Beispiele hier: http://en.wikipedia.org/wiki/Expect

Phil Pelanne
quelle