Ich habe hier als Superuser die Antworten durchgesehen, konnte jedoch keine Antwort finden, die sich auf meinen Anwendungsfall bezieht.
Ich möchte zwei Befehle verketten, die ich verwende und mit denen ich durch einen Tunnel tunneln kann:
ssh -L 22:hostB:22 user@hostA
Und
ssh -L 10002:localhost:10001 user@localhost
Im Moment gebe ich den ersten Befehl ein, öffne ein weiteres Terminal und gebe den zweiten ein. Wenn ich so etwas mache wie
ssh … && ssh …
der zweite Befehl wird erst ausgeführt, nachdem ich die erste Verbindung „beende“.
Erweiterte SSH-Optionen wie Jumphost funktionieren aufgrund der Serverkonfiguration auf HostA nicht, daher bin ich ziemlich sicher, dass dies die einzige Möglichkeit ist, Port 10001 auf diese Weise zugänglich zu machen.
Gibt es eine Möglichkeit, diese beiden Befehle zu einem Alias zu verketten oder ein einfaches Bash-Skript mit Timeouts usw. zu verwenden?
Antwort1
Ich nehme an, Sie können das nicht tun, nur ssh -L 10002:hostB:10001 user@hostA
weil
- entweder bindet alles, was auf B hört,
10001
nur an die lokale Schnittstelle, - oder die Firewall auf B verbietet die Verbindung
hostB:10001
von A zu A, - oder Wasauchimmer.
ssh … && ssh …
der zweite Befehl wird erst ausgeführt, nachdem ich die erste Verbindung „beende“.
Ja, das ist normal. Der vorherige Teil &&
muss beendet werden und den Exit-Status zurückgeben, bevor der nächste Befehl ausgeführt wird (oder nicht, je nach Status).
Dadurch wird das zweite fast sofort ausgeführt, ssh
es weist jedoch Fehler auf:
( ssh … & ) && ssh …
Es ist fehlerhaft, weil das erste ssh
keinen Einfluss auf das zweite hat. Das zweite wird wahrscheinlich ausgeführt, lange bevor das erste den Tunnel aufbaut. Das Abfragen von Passwörtern kann ebenfalls problematisch werden.
Für diesen Anwendungsfall ssh
gibt es -f
eine Option. Vonman 1 ssh
:
-f
Anfragenssh
, in den Hintergrund zu gehen, unmittelbar bevor der Befehl ausgeführt wird. […]
Der grundlegende Befehl könnte also lauten:
ssh -fNL 22:hostB:22 user@hostA && ssh -NL 10002:localhost:10001 user@localhost
-N
Es liegt an Ihnen, ob Sie den zweiten verwenden möchten ssh
oder nicht. Der Trick besteht nun darin, dass der erste ssh
nach der Kennwortabfrage (falls zutreffend) und nach dem Aufbau des ersten Tunnels in den Hintergrund wechselt, sodass es vorübergehend zwei „erste“ ssh
Prozesse gibt. Der im Hintergrund beginnt mit der Handhabung des Tunnels; der im Vordergrund wird beendet, damit &&
gearbeitet werden kann.
Sie möchten wahrscheinlich -o ExitOnForwardFailure=yes
. Wenn der erste Tunnel nicht aufgebaut werden kann, ssh
wird der erste erfolglos beendet und der zweite wird nicht ausgeführt. Der verbesserte Befehl lautet:
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
user@hostA &&
ssh -NL 10002:localhost:10001 user@localhost
Beachten Sie, dass beim Beenden des zweiten ssh
der erste (im Hintergrund) erhalten bleibt. Der Vorteil ist, dass Sie den einzigen zweiten ssh
später ausführen können. Der Nachteil ist, dass Sie den ersten manuell beenden müssen, wenn Sie nicht möchten, dass er erhalten bleibt. In diesem Sinne sollten Sie den folgenden Ansatz in Betracht ziehen:
ssh -fNL 22:hostB:22 \
-o ExitOnForwardFailure=yes \
user@hostA
ssh -NL 10002:localhost:10001 user@localhost
Beachten Sie, dass es kein gibt &&
. Wenn jetzt der alte erste ssh
noch läuft, wird der neue fehlschlagen, da er sich nicht an den Port binden kann. Der zweite ssh
wird trotzdem laufen, er wird den ersten Tunnel verwenden und es spielt keine Rolle, wie alt der Tunnel ist.
Es kann vorkommen, dass kein alter Port vorhanden ist ssh
und der erste ssh
aus irgendeinem Grund fehlschlägt. Wenn das der Fall ist, wird der zweite ausgeführt und dieser wird wahrscheinlich fehlschlagen (Verbindung abgelehnt), weil auf dem Port nichts lauscht 22
.
Dennoch möchten Sie einen „untätigen“ ssh
Prozess vielleicht nicht stehen lassen, sondern ihn automatisch beenden, nachdem er in der zweiten ssh
Runde beendet wurde. Offensichtlich killall ssh
kann dies Kollateralschäden verursachen. Lassen Sie uns etwas Subtileres finden:
-M
Versetzt denssh
Client in den „Master“-Modus zur gemeinsamen Nutzung von Verbindungen. […][…]
-O ctl_cmd
Steuern Sie einen aktiven Verbindungsmultiplex-Masterprozess. […][…]
-S ctl_path
Gibt den Speicherort eines Steuersockets für die gemeinsame Nutzung von Verbindungen an […]
#!/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
}
Nachdem der zweite ssh
beendet ist, fordert der dritte den ersten auf, zu beenden. Beim dritten ssh
Befehl kommt es auf den Socket an, nicht auf den Hostnamen; Sie müssen trotzdem einen Hostnamen angeben, daher dummy
. Der erste ssh
entfernt den Socket vor dem Beenden, es sollte kein Müll übrig bleiben.
Es gibt noch einige Bedenken:
- Wenn das Skript unterbrochen wird,
ssh
bleibt das erste bestehen. Entweder löschen&&
oder-O exit
vorsichtshalber ganz am Anfang senden (es wäre das Nulltessh
). Wenn der erste
ssh
plötzlich stirbt, bleibt der Sockel möglicherweise bestehen. Dadurch schlägt jeder neue erste fehl. Eine gute Idee könnte sein, den Nullten wie folgtssh
zu erweitern :ssh
ssh -S "$socket" -O exit dummy || rm "$socket"
/tmp/hostA.socket
ist möglicherweise nicht der beste Ort für den Socket.ssh_config
Das Äquivalent der-S
Befehlszeilenoption istControlPath
. Sehen Sie, wasman 5 ssh_config
sagt dazu:Es wird empfohlen, dass alle
ControlPath
für die opportunistische Verbindungsfreigabe verwendeten Elemente mindestens%h
,%p
, und%r
(oder alternativ%C
) enthalten und in einem Verzeichnis platziert werden, das für andere Benutzer nicht beschreibbar ist.Ein „Verzeichnis, das für andere Benutzer nicht beschreibbar ist“ kann sein
/run/user/$UID
, wenn Ihr Betriebssystem dies unterstützt (ich glaube, das istsystemd
die Sache von ).