SSH-Remote-Portweiterleitung fehlgeschlagen

26

Follow-up: Es sieht so aus, als ob die schnelle Reihe von Verbindungsabbrüchen, die mit einigen Monaten der Ausführung jedes Servers zusammenfällt, wahrscheinlich zufällig ist und nur dazu dient, das eigentliche Problem aufzudecken. Der Grund, warum die Verbindung nicht wiederhergestellt werden konnte, liegt mit ziemlicher Sicherheit an den AliveInterval-Werten (kasperds Antwort). Wenn Sie die Option ExitOnForwardFailure verwenden, sollte das Zeitlimit vor dem erneuten Herstellen der Verbindung korrekt angezeigt werden. In den meisten Fällen sollte dies das Problem beheben. Der Vorschlag von MadHatter (das Kill-Skript) ist wahrscheinlich der beste Weg, um sicherzustellen, dass der Tunnel wieder verbunden werden kann, auch wenn alles andere fehlschlägt.

Ich habe einen Server (A) hinter einer Firewall, der an mehreren Ports einen Reverse-Tunnel zu einem kleinen DigitalOcean-VPS (B) initiiert, damit ich über die IP-Adresse von B eine Verbindung zu A herstellen kann. Der Tunnel ist seit ungefähr 3 Monaten durchgehend in Betrieb, ist jedoch in den letzten 24 Stunden plötzlich viermal ausgefallen. Dasselbe geschah vor einiger Zeit bei einem anderen VPS-Anbieter - Monate des einwandfreien Betriebs, dann plötzlich mehrere schnelle Ausfälle.

Ich habe ein Skript auf Maschine A, das den Tunnelbefehl automatisch ausführt ( ssh -R *:X:localhost:X address_of_Bfür jeden Port X), aber wenn es ausgeführt wird, heißt es Warning: remote port forwarding failed for listen port X.

Wenn Sie /var/log/secureauf dem Server auf sshd zugreifen, werden die folgenden Fehler angezeigt :

bind: Address already in use
error: bind: Address already in use
error: channel_setup_fwd_listener: cannot listen to port: X

Das Lösen erfordert einen Neustart des VPS. Bis dahin wird bei allen Versuchen, die Verbindung wiederherzustellen, die Meldung "Remote-Portweiterleitung fehlgeschlagen" angezeigt und funktioniert nicht. Es ist jetzt so weit, dass der Tunnel vor dem Anhalten nur noch etwa 4 Stunden dauert.

Am VPS hat sich nichts geändert, und es handelt sich um einen Einbenutzer-Einzelplatzrechner, der nur als Reverse-Tunnel-Endpunkt dient. Es läuft OpenSSH_5.3p1 unter CentOS 6.5. Es scheint, dass sshd die Ports nicht an seinem Ende schließt, wenn die Verbindung unterbrochen wird. Ich bin nicht in der Lage zu erklären, warum oder warum es nach Monaten eines nahezu perfekten Betriebs plötzlich passieren würde.

Um dies zu verdeutlichen, muss ich zuerst herausfinden, warum sshd es ablehnt, die Ports zu überwachen, nachdem der Tunnel ausfällt. Dies scheint darauf zurückzuführen zu sein, dass sshd die Ports offen lässt und sie niemals schließt. Das scheint das Hauptproblem zu sein. Ich bin mir nur nicht sicher, was dazu führen würde, dass es sich nach Monaten des erwarteten Verhaltens so verhält (dh, die Ports werden sofort geschlossen und das Skript kann erneut verbunden werden).

Justin Mrkva
quelle
Was ist deine Frage? Wie kann man den Fehler bei der Portbindung beheben oder herausfinden, warum ssh abstirbt oder noch einmal etwas anderes?
MadHatter unterstützt Monica
Ich muss herausfinden, warum sshd sich weigert, die Ports auf dem VPS zu öffnen (der Bindungsfehler). Der Fehler bei der Portbindung scheint die Wurzel des Problems zu sein, und alles sollte funktionieren, wenn ich das lösen kann.
Justin Mrkva
2
Verwenden Sie für spät lauernde Benutzer statt der manuellen Erstellung eines Skripts, um die Verbindung offen zu halten, einfach autossh. Dies erledigt der Benutzer für Sie. serverfault.com/questions/598210/…
oligofren

Antworten:

27

Ich stimme MadHatter zu, dass es wahrscheinlich Portweiterleitungen von unterbrochenen SSH-Verbindungen sind. Auch wenn sich herausstellt, dass Ihr aktuelles Problem etwas anderes ist, können Sie davon ausgehen, dass Sie früher oder später auf solche unterbrochenen SSH-Verbindungen stoßen.

Es gibt drei Möglichkeiten, wie solche unterbrochenen Verbindungen auftreten können:

  • Einer der beiden Endpunkte wurde neu gestartet, während das andere Ende der Verbindung vollständig inaktiv war.
  • Einer der beiden Endpunkte hat die Verbindung geschlossen. Zum Zeitpunkt der Beendigung der Verbindung war jedoch ein vorübergehender Ausfall der Verbindung aufgetreten. Der Ausfall dauerte einige Minuten, nachdem die Verbindung geschlossen wurde, und daher erfuhr das andere Ende nie etwas über die geschlossene Verbindung.
  • Die Verbindung ist an beiden Endpunkten der SSH-Verbindung immer noch voll funktionsfähig, aber irgendwo zwischen ihnen befindet sich ein Stateful-Gerät, durch das die Verbindung aufgrund von Inaktivität unterbrochen wurde. Dieses zustandsbehaftete Gerät ist entweder eine NAT oder eine Firewall. Die Firewall, die Sie bereits erwähnt haben, ist ein Hauptverdächtiger.

Es ist nicht sehr wichtig, herauszufinden, welche der drei oben genannten Vorgänge stattfinden, da es eine Methode gibt, die alle drei anspricht. Das ist die Verwendung von Keepalive-Nachrichten.

Sie sollten nach dem ClientAliveIntervalSchlüsselwort für sshd_configund dem ServerAliveIntervalIntervall für ssh_configoder suchen ~/.ssh/config.

Das Ausführen des sshBefehls in einer Schleife kann problemlos funktionieren. Es ist eine gute Idee, einen Schlafmodus in die Schleife einzufügen, damit Sie den Server nicht überfluten, wenn die Verbindung aus irgendeinem Grund fehlschlägt.

Wenn der Client erneut eine Verbindung herstellt, bevor die Verbindung auf dem Server beendet wurde, kann dies dazu führen, dass die neue SSH-Verbindung aktiv ist, aber keine Portweiterleitungen aufweist. Um dies zu vermeiden, müssen Sie das ExitOnForwardFailureSchlüsselwort auf der Clientseite verwenden.

Kasperd
quelle
Ich denke, das könnte das Problem sein. Insbesondere wird mein Skript auf A versuchen, die Verbindung zu B wiederherzustellen, wenn der ssh-Prozess abbricht (da die Warnmeldung den ssh-Prozess nicht beendet, hängt es natürlich nur, wenn dies passiert, aber das ist ein Problem für einen anderen Tag). Wenn A jedoch versucht, die Verbindung zu B zu schnell wiederherzustellen, wartet B möglicherweise darauf, dass A die Verbindung wiederherstellt. Wahrscheinlich muss ich sicherstellen, dass B immer das Zeitlimit überschreitet, bevor A die Verbindung wiederherstellt. Wenn Sie dies mit dem Vorschlag von MadHatter kombinieren, die sshd-Prozesse vor dem Wiederverbinden zu beenden, werden wahrscheinlich 95% der möglichen Fälle abgedeckt.
Justin Mrkva
1
Und wenn ich von der Warnmeldung spreche, dass SSH nicht getötet wird, dann habe ich darüber nachgedacht ... und mir die Hilfeseiten angesehen. Es stellte sich heraus, dass -o ExitOnForwardFailure yesgenau das war, was ich brauchte. Das ist eine Sache weniger, die ich herausfinden muss. Zum Nachdenken wollte ich ein Python-Skript schreiben, um diese Warnmeldungen zu analysieren. Das ist viel einfacher. : D
Justin Mrkva
Tut ExitOnForwardFailuremir leid, dass ich das beim Schreiben meiner Antwort vergessen habe. Ich habe es jetzt zur Antwort hinzugefügt.
Kasperd
4
Kein Problem, und es war tatsächlich -o ExitOnForwardFailure=yes(beachte das Gleichheitszeichen). Also, wenn jemand darauf stößt, kopiere es nicht und füge es nicht aus meinem vorherigen Kommentar ein, es wird nicht funktionieren. : P
Justin Mrkva
Also habe ich den Server ungefähr 10 Stunden lang überwacht und es sieht so aus, als ob er gut läuft. An diesem Punkt gehe ich davon aus, dass diese Antwort richtig ist (ich bin mir zu 99% sicher, basierend auf dem, was ich gesehen habe) und dass die Reihe der schnellen Verbindungsabbrüche ein Zufall im Zusammenhang mit Netzwerkproblemen war, die nur wenige Monate später auftraten Starten jedes Dienstes. Vielen Dank an alle für Ihre Hilfe. ;)
Justin Mrkva
4

Sie können den Prozess finden, der den Port auf diesem Server bindet

sudo netstat -apn|grep -w X

Es scheint sehr wahrscheinlich, dass es sich um die halbe Funktionsunfähigkeit handelt sshd, aber warum Annahmen treffen, wenn Sie Daten haben können? Es ist auch eine gute Möglichkeit für ein Skript, eine PID zu finden, an die das Signal 9 gesendet werden kann, bevor versucht wird, den Tunnel wieder hochzufahren.

MadHatter unterstützt Monica
quelle
Ich erinnere mich, dass ich dies beim vorherigen VPS-Anbieter überprüft und bestätigt habe, dass sshd der Prozess ist, der diese Ports abhört. Wenn es das nächste Mal passiert, werde ich es hier überprüfen, aber da das Verhalten und die Einrichtung genau gleich sind, erwarte ich nicht, dass es anders sein wird.
Justin Mrkva
Großartig, also lassen Sie Ihr Skript, das den Tunnel wieder öffnet, den alten Tunneller töten, bevor Sie dies versuchen.
MadHatter unterstützt Monica
Es wird nie mehr als ein Tunnelskript (auf A) gleichzeitig ausgeführt, wenn Sie das sagen. Auf der anderen Seite ist es keine schlechte Idee, wenn Sie das Skript remote auf B ausführen lassen möchten, um die verirrten Prozesse zu beenden. Eine Sorge ist jedoch, dass immer wieder alle SSH-Verbindungen abgebrochen werden, wenn ich ein Debugging versuche. Wenn das Skript auf A immer B aufgrund einer Panne tötet, kann ich nicht ständig von dem Schurken-A-Skript von B abgeworfen werden. : P Ich muss testen, um sicherzustellen, dass es das nicht tut. Aber wie gesagt, keine halb schlechte Idee. ;)
Justin Mrkva
Ich hätte nicht gedacht, dass es so etwas gibt. Sie sagen, dass auf dem Remote-Server ein Skript ausgeführt wird, das versucht, einen Tunnel aufzurufen, und das aufgrund des Bindefehlers fehlschlägt. Ich gehe davon aus, dass es nur ausgeführt wird, wenn Sie es benötigen (dh wenn der vorhandene Tunnel nicht funktioniert). weil du nichts anderes gesagt hast. Alles, was ich vorschlage, ist, dass es den spezifischen Prozess abbricht, der den Port offen hält, bevor versucht wird, den neuen Tunnel aufzurufen.
MadHatter unterstützt Monica
Das Skript, das ssh ausführt, befindet sich nur auf Server A, Server B ist ein einfacher Vanille-Server ohne zusätzliche Skripte. Was ich wahrscheinlich tun werde, ist, ein Kill-Skript zu schreiben, das auf Server B abgelegt wird, und es dann remote von A aufzurufen, wenn die Verbindung eine bestimmte Anzahl von Malen hintereinander nicht hergestellt werden kann. Auf diese Weise ist es weniger wahrscheinlich, dass andere SSH-Verbindungen beeinträchtigt werden. Und ich werde das Kill-Skript wahrscheinlich jedes Mal protokollieren lassen, wenn es ausgeführt und beendet wird, ohne etwas zu tun, wenn es zu oft zu schnell aufgerufen wird. Persönlich scheint es wahrscheinlich klug zu sein, das Tempo eines Skripts zu begrenzen, das sshd beendet. : P
Justin Mrkva
3

Wenn ein sshTunnel getrennt wird, dauert es eine Weile, bis die Verbindung zurückgesetzt wird, sodass der sshProzess weiterhin blockiert wird und ich keine aktiven Tunnel mehr habe und nicht weiß, warum. Eine Umgehungslösung besteht darin, neue Verbindungen sshin den Hintergrund zu stellen -fund zu erzeugen, ohne darauf zu warten, dass alte Verbindungen zurückgesetzt werden. Mit dem -o ExitOnForwardFailure=yeskann die Anzahl neuer Prozesse begrenzt werden. Das -o ServerAliveInterval=60verbessert die Zuverlässigkeit Ihrer aktuellen Verbindung.

Sie können den sshBefehl häufig wiederholen cron, z. B. in einer oder in einer Schleife in Ihrem Skript. Im Folgenden führen wir den sshBefehl alle 3 Minuten aus:

while (1)
do
    ssh -f user@hostname -Rport:host:hostport -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=60
    sleep 180
done
Stephen Quan
quelle
Eine viel robustere Lösung wäre die Verwendung von autossh
Marco Lavagnino
-o ExitOnForwardFailure=yeswar das, wonach ich gesucht habe, vielen Dank!
Vadipp
1

Meiner Erfahrung nach hat ssh die etwas lästige Angewohnheit, nicht sauber zu beenden, wenn auf dem Remote-System noch etwas läuft. ZB im Hintergrund gestartet. Sie können dies reproduzieren durch:

ssh <server>
while true; do  sleep 60; done&
exit

Ihr ssh wird sich abmelden, aber die Sitzung nicht tatsächlich schließen - bis der Remote-Prozess beendet ist (was nicht der Fall ist, da es sich um eine "while true" -Schleife handelt). Es kann sein, dass etwas Ähnliches passiert - Ihre Sitzung hat einen 'festgefahrenen' Prozess, der von ssh erzeugt wird. Der Port wird weiterhin verwendet und kann daher von Ihrem lokalen Prozess nicht wiederverwendet werden.

Sobrique
quelle
Der vollständige SSH-Befehl, der auf der A-Maschine ssh -o ConnectTimeout=10 -o BatchMode=yes -gnN -R *:X:localhost:X root@$TUNSRV 1>>tunnel.log 2>&1 &ausgeführt wird, bewirkt, dass von SSH nichts außer dem Tunnel selbst ausgeführt wird, insbesondere aufgrund der Option -N. Was immer offen gehalten wird, wird auf dem Remote-Server B unter Verwendung von sshd selbst ausgeführt.
Justin Mrkva