Wie kann man einen SSH-Tunnel zuverlässig offen halten?

234

Ich benutze einen SSH-Tunnel von der Arbeit, um verschiedene idotische Firewalls zu umgehen (bei meinem Chef ist das in Ordnung :)). Das Problem ist, dass nach einer Weile die SSH-Verbindung normalerweise unterbrochen wird und der Tunnel unterbrochen wird.

Wenn ich den Tunnel zumindest automatisch überwachen könnte, könnte ich den Tunnel neu starten, wenn er hängt, aber ich habe nicht einmal eine Möglichkeit gefunden, dies zu tun.

Bonuspunkte für denjenigen, der mir sagen kann, wie ich verhindern kann, dass meine SSH-Verbindung hängt!

Peltier
quelle
Ist dein Tunnel tot, weil du untätig bist? Ich hatte dieses Problem , wenn Ports von meinem Handy tunneln , so dass ich schließlich Dummy - Befehle auf der Verbindung Laichen endete es „am Leben“ mit dem machen , watchwie Befehl: watch -n1 60 echo "wiiiii". Der Tunnel stirbt erst, wenn das Netzwerk unterbrochen ist oder Sie es nicht verwenden.
erm3nda
1
Related: unix.stackexchange.com/q/200239
sampablokuper

Antworten:

280

Klingt so, als ob Sie autossh brauchen . Dadurch wird ein SSH-Tunnel überwacht und bei Bedarf neu gestartet. Wir haben es für ein paar Jahre benutzt und es scheint gut zu funktionieren.

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C

Weitere Details zum -M-Parameter hier

KeithB
quelle
2
+1 für autossh, es macht das, was es verspricht. Ich glaube, ein Teil seiner Funktionalität besteht auch darin, Keep-Alive-Pakete zu senden, um jegliche Art von Timeout zu verhindern.
Akent
30
Könnten Sie den Beispieltunnel autosshin der Antwort verwenden?
Ehtesh Choudhury
5
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 [email protected] Möglicherweise bemerken Sie, dass ich dies mit -nNT eingerichtet habe, wodurch kein Remote-Terminal erstellt wird, sodass ich autossh in den Hintergrund stellen kann, und mit der Option -i, damit SSH eine PEM-Datei verwendet. Wenn Sie die ganze Zeit über eine Verbindung offen halten, empfehle ich auf jeden Fall, das zusätzliche Setup durchzuarbeiten.
Juckele
2
Für das, was es wert ist, sieht es so aus, als ob es normalerweise besser ist, den -MParameter wegzulassen : bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162
rinogo
2
Ich habe dies getan, damit es nach einer Netzwerkänderung erneut versucht wird. Es funktioniert gut für mich: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 [email protected]
Luke Stanley
39

Alle Stateful Firewalls vergessen eine Verbindung, nachdem sie einige Zeit kein Paket für diese Verbindung gesehen haben (um zu verhindern, dass die Statustabellen mit Verbindungen voll werden, bei denen beide Enden gestorben sind, ohne die Verbindung zu schließen). Die meisten TCP-Implementierungen senden nach einer langen Zeit ein Keepalive-Paket, ohne von der anderen Seite zu hören (2 Stunden sind ein üblicher Wert). Wenn es jedoch eine Stateful Firewall gibt, die die Verbindung vergisst, bevor die Keepalive-Pakete gesendet werden können, wird eine langlebige, aber inaktive Verbindung unterbrochen.

In diesem Fall besteht die Lösung darin, zu verhindern, dass die Verbindung inaktiv wird. OpenSSH verfügt über eine Option namens ServerAliveInterval , mit der verhindert werden kann, dass die Verbindung zu lange inaktiv ist.

CesarB
quelle
Das angegebene Intervall ist in Sekunden angegeben, sodass Sie eine Feinabstimmung vornehmen können. Wenn Ihre Stateful Firewall ein 5-minütiges Timeout im Leerlauf aufweist, sind 60 oder 120 Sekunden ausreichend, um die Verbindung offen zu halten. Auf diese Weise halte ich meine SSH-Sitzungen über meinen Heimrouter offen.
Darren Hall
Danke, das hat geholfen. Aber beachten Sie (von einem niedrigeren Rangs Antwort hier, superuser.com/a/146641/115515 ) , dass , wenn Sie ServerAliveInterval und nicht ServerAliveCountMax angeben, können Sie ssh absichtlich trennen früher , als Sie wollten.
Metamatt
4
@metamatt, diese von Ihnen angegebene Antwort hat aus gutem Grund einen niedrigeren Rang: ES IST FALSCH.
Lambart
24

Konfigurieren Sie Ihren ssh auf Ihrem eigenen Mac oder Linux-Computer und halten Sie den ssh-Server alle 3 Minuten am Leben. Öffne ein Terminal und bring deine unsichtbare .ssh zu dir nach Hause:

cd ~/.ssh/ 

Dann erstelle eine 1-zeilige Konfigurationsdatei mit:

echo "ServerAliveInterval 180" >> config

Sie sollten auch hinzufügen:

ServerAliveCountMax xxxx (high number)

Der Standardwert ist 3, sodass ServerAliveInterval 180 den Sendevorgang nach 9 Minuten beendet (3 des von ServerAliveInterval angegebenen 3-Minuten-Intervalls).

David Shaw
quelle
2
Beachten Sie, dass Ihr Befehl nicht empfohlen wird, wenn Sie bereits eine Konfigurationsdatei haben. >> für die Weiterleitung zu verwenden wäre viel besser!
Peltier
Warum ServerAliveInterval 180geben Sie uns 6 Minuten? Intuition macht mich dies versuchen: 180/60 == 3. Funktioniert das also ServerAliveIntervalin Vielfachen von 30 Sekunden?
Nemesisfixx
@mcnemesis: ServerAliveInterval 180 bedeutet 3 Minuten. Der ServerAliveCountMax-Standardwert von 3 bedeutet 3 dieser Intervalle, also 9 Minuten.
Metamatt
2
Ich stimme für diese Antwort, weil ich danke, dass Sie ServerAliveCountMax erwähnt haben, und was passiert, wenn Sie ServerAliveInterval ohne ServerAliveCountMax angeben. Aber wie in den vorangegangenen Kommentaren, stelle ich fest, dass die Berechnung für "Senden nach beenden" falsch ist, und ich denke, diese Antwort wäre besser, wenn sie nur die Informationen zu diesen Optionen enthält und uns nicht sagt, wie sie mit CD- und Echo-Befehlen angewendet werden sollen .
Metamatt
20
Downvoting, weil es keinen Sinn macht, ServerAliveCountMax auf eine "hohe Zahl" zu setzen. ServerAliveCountMax gibt an, wie oft versucht wird, die "Keepalive" -Nachricht zu senden, bevor aufgegeben wird. Der Standardwert ist 3. Mit ServerAliveInterval 180 wird der Sendevorgang NUR dann abgebrochen, wenn der Server nach 9 Minuten nicht geantwortet hat. In diesem Fall ist Ihre Verbindung wahrscheinlich vollständig getrennt.
Lambart
22

Ich habe das folgende Bash-Skript verwendet, um weiterhin neue ssh-Tunnel zu erzeugen, wenn der vorherige stirbt. Die Verwendung eines Skripts ist praktisch, wenn Sie keine zusätzlichen Pakete installieren möchten oder können oder den Compiler verwenden.

while true
do
  ssh <ssh_options> [user@]hostname
  sleep 15
done

Beachten Sie, dass hierfür eine Schlüsseldatei erforderlich ist, um die Verbindung automatisch herzustellen. Dies gilt jedoch auch für autossh.

Jawa
quelle
2
Sie sollten Gründe hinzufügen, warum Sie dieses Skript über autossh verwenden würden, oder ist es nur so, dass es einfacher ist?
kyrias
4
Das würde nicht helfen, wenn ssh selbst einfriert, oder?
Nafg
1
Es hilft, wenn Sie keine Dinge auf dem Server installieren können. autossh ist nicht vorinstalliert und bürokratisch ist es manchmal sehr stumpf.
Quarkex
Ja, es ist vorzuziehen, keine Dinge installieren zu müssen. Ich mache das seit einem Jahr auf diese Weise, als meine einzige Möglichkeit, einen Remote-Computer zugänglich zu halten (sogar crontab so einstellen, dass es beim Neustart ausgeführt wird). Es ist niemals gescheitert, und was noch wichtiger ist, ich weiß, warum es niemals scheitern wird.
Sudo
16

Dafür ist Systemd bestens geeignet.

Erstellen Sie eine Servicedatei /etc/systemd/system/sshtunnel.servicemit:

[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Restart=always
RestartSec=20
User=sshtunnel
ExecStart=/bin/ssh -NT -o ServerAliveInterval=60 -L 5900:localhost:5900 user@otherserver

[Install]
WantedBy=multi-user.target

(Ändern Sie den Befehl ssh entsprechend.)

  • Dies wird als Benutzer ausgeführt sshtunnel, stellen Sie also sicher, dass der Benutzer zuerst existiert
  • Problem systemctl enable sshtunnel, um es so einzustellen, dass es beim Booten startet
  • Problem systemctl start sshtunnelsofort zu starten

Update Januar 2018 : Einige Distributionen (z. B. Fedora 27) verwenden möglicherweise die SELinux-Richtlinie, um die Verwendung von SSH von systemd init zu verhindern. In diesem Fall muss eine benutzerdefinierte Richtlinie erstellt werden, um die erforderlichen Ausnahmen bereitzustellen.

IanB
quelle
2
Das sieht meinem Inhalt sehr ähnlich: gist.github.com/guettli/… Feedback ist willkommen!
Guettli
Hervorragend für ein systemdSystem. Wenn man Restart=on-failureden SSH-Client verwendet, führt das manuelle Beenden nicht zu einem Neustart durch das System, da der SSH-Client mit Erfolg beendet wird.
David Tonhofer
Wenn Sie ssh von einem (Bash-) Skript aus starten möchten, das als Argument angegeben wurde, ExecStartum beispielsweise die sshArgumentliste zu erstellen , führen Sie grundlegende Überprüfungen usw. durch, und rufen Sie es dann wie folgt aus dem Skript auf exec /bin/ssh -N .... Hier ist mein Befehl: exec /bin/ssh -N -oExitOnForwardFailure=Yes -oTCPKeepAlive=no -oServerAliveInterval=5 -oServerAliveCountMax=6 -i "${LOCAL_PRIVATE_KEY}" -L "${TUNNEL_INLET}:${TUNNEL_OUTLET}" "${REMOTE_USER}@${REMOTE_MACHINE}"wo TUNNEL_INLET="127.0.0.1:3307"undTUNNEL_OUTLET="127.0.0.1:3306"
David Tonhofer
10

Mir scheint, dass Sie ServerAliveCountMax falsch interpretieren. Nach meinem Verständnis ist es die Anzahl der Server-Alive-Nachrichten, die unbeantwortet bleiben können, ohne dass die Verbindung beendet wird. Wenn Sie also in den hier beschriebenen Fällen einen hohen Wert festlegen, wird nur sichergestellt, dass eine unterbrochene Verbindung nicht erkannt und beendet wird!

Das einfache Einstellen von ServerAliveInterval sollte ausreichen, um das Problem zu lösen, dass eine Firewall die Verbindung vergisst. Wenn Sie ServerAliveCountMax niedrig lassen, kann das Ursprungsende den Fehler feststellen und die Verbindung beenden, wenn die Verbindung trotzdem fehlschlägt.

Was Sie möchten, ist, 1) dass die Verbindung unter normalen Umständen permanent offen bleibt, 2) dass ein Verbindungsfehler erkannt wird und die Ursprungsseite bei einem Fehler beendet wird, und 3) dass der Befehl ssh jedes Mal neu ausgegeben wird Exits (wie Sie das machen, ist sehr plattformabhängig, das von Jawa vorgeschlagene "while true" -Skript ist eine Möglichkeit, unter OS XI tatsächlich ein launchd-Element einzurichten).

user2793784
quelle
9

Verwenden Sie immer die ServerAliveIntervalSSH-Option, wenn die Tunnelprobleme durch abgelaufene NAT-Sitzungen generiert werden.

Verwenden Sie immer eine Respawn-Methode, wenn die Konnektivität vollständig ausfällt. Hier haben Sie mindestens drei Möglichkeiten:

  • Autossh-Programm
  • bash script ( while true do ssh ...; sleep 5; done) entfernt den Befehl sleep nicht, schlägt sshmöglicherweise schnell fehl und Sie werden zu viele Prozesse erneut erzeugen
  • /etc/inittabUm Zugriff auf eine Box zu erhalten, die in einem anderen Land geliefert und installiert wurde, und zwar hinter NAT, ohne dass ein Port an die Box weitergeleitet wird, können Sie diese so konfigurieren, dass ein SSH-Tunnel für Sie erstellt wird:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • Upstart-Skript auf Ubuntu, wo /etc/inittabnicht verfügbar:

    start on net-device-up IFACE=eth0
    stop on runlevel [01S6]
    respawn
    respawn limit 180 900
    exec ssh -i /path/to/rsaKey -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip
    post-stop script
        sleep 5
    end script
    

oder immer beide Methoden anwenden.

claudiuf
quelle
1
+1 für Inline-Option, falls Sie es nicht für alle Ihre SSH-Verbindungen
möchten
Sie schreiben "falls die Konnektivität komplett ausfällt". Jetzt verstehe ich nicht, welche Probleme autossh selbst behebt und was nicht? Ich dachte natürlich, es würde für eine unterbrochene Verbindung sorgen, wie das Abziehen des Kabels für ein paar Stunden, aber vielleicht auch nicht?
Mads Skjern
6

Ich habe dieses Problem folgendermaßen gelöst:

Bearbeiten

~/.ssh/config

Und füge hinzu

ServerAliveInterval 15
ServerAliveCountMax 4

Laut Manpage für ssh_config:

ServerAliveCountMax
         Sets the number of server alive messages (see below) which may be
         sent without ssh(1) receiving any messages back from the server.
         If this threshold is reached while server alive messages are
         being sent, ssh will disconnect from the server, terminating the
         session.  It is important to note that the use of server alive
         messages is very different from TCPKeepAlive (below).  The server
         alive messages are sent through the encrypted channel and there‐
         fore will not be spoofable.  The TCP keepalive option enabled by
         TCPKeepAlive is spoofable.  The server alive mechanism is valu‐
         able when the client or server depend on knowing when a connec‐
         tion has become inactive.

         The default value is 3.  If, for example, ServerAliveInterval
         (see below) is set to 15 and ServerAliveCountMax is left at the
         default, if the server becomes unresponsive, ssh will disconnect
         after approximately 45 seconds.  This option applies to protocol
         version 2 only.

 ServerAliveInterval
         Sets a timeout interval in seconds after which if no data has
         been received from the server, ssh(1) will send a message through
         the encrypted channel to request a response from the server.  The
         default is 0, indicating that these messages will not be sent to
         the server.  This option applies to protocol version 2 only.
nachopro
quelle
Alle 15 Sekunden scheint der Server ziemlich oft zu pingen.
Lambart
@ Lambart, aber wenn die Verbindung sehr instabil ist und Verbindungen häufig unterbrochen werden, wird zumindest eine tote Verbindung erkannt und es besteht die Möglichkeit, sie früher erneut zu versuchen.
binki
4

ExitOnForwardFailure yesist eine gute Ergänzung zu den anderen Vorschlägen. Wenn eine Verbindung hergestellt wird, die Portweiterleitung jedoch nicht hergestellt werden kann, ist dies für Sie genauso nutzlos, als ob überhaupt keine Verbindung hergestellt worden wäre.

jcomeau_ictx
quelle
Dies ist eine sehr gute Idee. Selbst autossh ist unbrauchbar, wenn die vorherige Verbindung auf der Remote-Seite früher als auf dem lokalen Host als Zeitüberschreitung erkannt wird, da in diesem Fall der lokale Host erneut versucht, eine Verbindung herzustellen, die Weiterleitung jedoch nicht hergestellt werden kann, da der Port noch offen ist.
Raúl Salinas-Monteagudo
1

Ich musste einen SSH-Tunnel langfristig unterhalten. Meine Lösung wurde von einem Linux-Server ausgeführt und es ist nur ein kleines C-Programm, das ssh mithilfe der schlüsselbasierten Authentifizierung wiederherstellt.

Ich bin nicht sicher, ob ich hängen soll, aber ich habe Tunnel wegen Timeouts sterben lassen.

Ich würde gerne den Code für den Respawner bereitstellen, aber ich kann ihn augenblicklich nicht finden.

baumgart
quelle
1

während es tools wie autossh gibt, die helfen, ssh session neu zu starten ... was ich wirklich nützlich finde, ist, den 'screen'-befehl auszuführen. Sie können Ihre ssh-Sitzungen auch nach dem Trennen der Verbindung fortsetzen. Besonders nützlich, wenn Ihre Verbindung nicht so zuverlässig ist, wie sie sein sollte.

... vergessen Sie nicht zu markieren, dies ist die "richtige" Antwort, wenn es Ihnen hilft, k! ;-)

koss
quelle
7
... aber die Frage war, wie man SSH-Tunnel offen hält, nicht nur eine Terminalsitzung. Der Bildschirm ist großartig!
Akent
Ich benutze den Bildschirm bereits, aber er löst mein Problem nicht: - / Vielen Dank für Ihre Antwort.
Peltier
1

Ein bisschen hacken, aber ich benutze gerne Bildschirm, um dies zu halten. Ich habe derzeit eine Remote-Weiterleitung, die seit Wochen ausgeführt wird.

Beispiel, lokal starten:

screen
ssh -R ......

Wenn die Remote-Weiterleitung angewendet wird und Sie eine Shell auf dem Remote-Computer haben:

screen
Ctrl + a + d

Sie haben jetzt eine ununterbrochene Remote-Weiterleitung. Der Trick besteht darin, den Bildschirm an beiden Enden auszuführen

landypro
quelle
1

Vor kurzem hatte ich dieses Problem, da Sie bei diesen Lösungen jedes Mal das Kennwort erneut eingeben müssen, wenn Sie eine Kennwortanmeldung verwenden. Ich habe sshpass in einer Schleife zusammen mit einer Textaufforderung verwendet, um zu vermeiden, dass das Kennwort in der Batchdatei enthalten ist.

Dachte, ich würde meine Lösung auf diesem Thead teilen, falls jemand das gleiche Problem hat:

#!/bin/bash
read -s -p "Password: " pass
while true
do
    sshpass -p "$pass" ssh user@address -p port
    sleep 1
done
Brainfloat
quelle
0

Ich hatte ähnliche Probleme mit meinem vorherigen ISP. Für mich war es mit jeder TCP-Verbindung, dem Besuch von Websites oder dem Versenden von E-Mails dasselbe.

Die Lösung bestand darin, eine VPN-Verbindung über UDP zu konfigurieren (ich verwendete OpenVPN). Diese Verbindung war toleranter gegenüber den Ursachen der Unterbrechungen. Dann können Sie jeden Dienst über diese Verbindung ausführen.

Es kann immer noch Probleme mit der Verbindung geben, aber da der Tunnel toleranter ist, fühlt sich jede SSH-Sitzung eher als eine kurze Verzögerung an, als dass die Verbindung getrennt wird.

Dazu benötigen Sie einen VPN-Dienst online, den Sie auf Ihrem eigenen Server einrichten können.

hultqvist
quelle
0

Da autosshdies nicht unseren Anforderungen entspricht (es ist fehlerhaft, wenn beim ersten Versuch keine Verbindung zum Server hergestellt werden kann), haben wir eine reine Bash-Anwendung geschrieben: https://github.com/aktos-io/link- with-server

Standardmäßig wird ein umgekehrter Tunnel für den sshd-Port (22) des NODE auf dem Server erstellt. Wenn Sie andere Aktionen ausführen müssen (z. B. Weiterleiten zusätzlicher Ports, Senden von E-Mails bei bestehender Verbindung usw.), können Sie Ihre Skripts on-connectund on-disconnectOrdner platzieren.

Zeremonie
quelle