Verketten Sie zwei SSH-Portweiterleitungen

Verketten Sie zwei SSH-Portweiterleitungen

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@hostAweil

  • entweder bindet alles, was auf B hört, 10001nur an die lokale Schnittstelle,
  • oder die Firewall auf B verbietet die Verbindung hostB:10001von 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, sshes weist jedoch Fehler auf:

( ssh … & ) && ssh …

Es ist fehlerhaft, weil das erste sshkeinen 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 sshgibt es -feine Option. Vonman 1 ssh:

-f
Anfragen ssh, 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

-NEs liegt an Ihnen, ob Sie den zweiten verwenden möchten sshoder nicht. Der Trick besteht nun darin, dass der erste sshnach der Kennwortabfrage (falls zutreffend) und nach dem Aufbau des ersten Tunnels in den Hintergrund wechselt, sodass es vorübergehend zwei „erste“ sshProzesse 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, sshwird 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 sshder erste (im Hintergrund) erhalten bleibt. Der Vorteil ist, dass Sie den einzigen zweiten sshspä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 sshnoch läuft, wird der neue fehlschlagen, da er sich nicht an den Port binden kann. Der zweite sshwird 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 sshund der erste sshaus 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“ sshProzess vielleicht nicht stehen lassen, sondern ihn automatisch beenden, nachdem er in der zweiten sshRunde beendet wurde. Offensichtlich killall sshkann dies Kollateralschäden verursachen. Lassen Sie uns etwas Subtileres finden:

-M
Versetzt den sshClient 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 sshbeendet ist, fordert der dritte den ersten auf, zu beenden. Beim dritten sshBefehl kommt es auf den Socket an, nicht auf den Hostnamen; Sie müssen trotzdem einen Hostnamen angeben, daher dummy. Der erste sshentfernt den Socket vor dem Beenden, es sollte kein Müll übrig bleiben.

Es gibt noch einige Bedenken:

  • Wenn das Skript unterbrochen wird, sshbleibt das erste bestehen. Entweder löschen &&oder -O exitvorsichtshalber ganz am Anfang senden (es wäre das Nullte ssh).
  • Wenn der erste sshplötzlich stirbt, bleibt der Sockel möglicherweise bestehen. Dadurch schlägt jeder neue erste fehl. Eine gute Idee könnte sein, den Nullten wie folgt sshzu erweitern :ssh

    ssh -S "$socket" -O exit dummy || rm "$socket"
    
  • /tmp/hostA.socketist möglicherweise nicht der beste Ort für den Socket. ssh_configDas Äquivalent der -SBefehlszeilenoption ist ControlPath. Sehen Sie, wasman 5 ssh_configsagt dazu:

    Es wird empfohlen, dass alle ControlPathfü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 ist systemddie Sache von ).

verwandte Informationen