Jak niezawodnie utrzymywać tunel SSH otwarty?

234

Używam tunelu SSH z pracy do obchodzenia różnych idiotycznych zapór ogniowych (jest w porządku z moim szefem :)). Problem polega na tym, że po pewnym czasie połączenie ssh zwykle się zawiesza, a tunel jest zepsuty.

Gdybym mógł przynajmniej automatycznie monitorować tunel, mógłbym ponownie uruchomić tunel, gdy się zawiesi, ale nawet nie wymyśliłem, jak to zrobić.

Punkty bonusowe dla tego, kto może mi powiedzieć, jak zapobiec zawieszeniu mojego połączenia ssh!

Peltier
źródło
Czy Twój tunel jest martwy, ponieważ brak aktywności? Miałem ten problem podczas tunelowania portów z mojego telefonu, więc skończyło tarła obojętne polecenia na połączenia, aby go „żywy” przy użyciu watchkomendy jak: watch -n1 60 echo "wiiiii". Tunel nie umrze, chyba że sieć zostanie zerwana lub go nie użyjesz.
erm3nda,
1
Powiązane: unix.stackexchange.com/q/200239
sampablokuper

Odpowiedzi:

280

Wygląda na to, że potrzebujesz autossh . Spowoduje to monitorowanie tunelu ssh i ponowne uruchomienie go w razie potrzeby. Używamy go od kilku lat i wydaje się, że działa dobrze.

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

Więcej szczegółów na temat parametru -M tutaj

KeithB
źródło
2
+1 za autossh, robi to, co mówi na puszce. Uważam, że częścią jego funkcjonalności jest również wysyłanie pakietów typu „zachowaj przy życiu”, aby zapobiec przekroczeniu limitu czasu.
akent
30
Czy możesz podać przykładowy tunel autosshw odpowiedzi?
Ehtesh Choudhury,
5
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 [email protected] Możesz zauważyć, że ustawiłem to za pomocą opcji -nNT, która nie tworzy zdalnego terminala, dzięki czemu mogę umieścić autossh w tle, a także opcję -i dla SSH, aby użyć pliku .pem. Jeśli zamierzasz utrzymywać połączenie otwarte przez cały czas, zdecydowanie zalecamy przejrzenie dodatkowej konfiguracji.
juckele
2
Jeśli chodzi o wartość, wygląda na to, że zazwyczaj lepiej jest pominąć -Mparametr: bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162
rinogo
2
Zrobiłem to, aby spróbować ponownie po zmianie sieci, działa dobrze dla mnie: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 [email protected]
Luke Stanley
39

Wszystkie zapory stanowe zapominają o połączeniu po tym, jak przez pewien czas nie widziały pakietu dla tego połączenia (aby zapobiec zapełnieniu się tablic stanów połączeń, w których oba końce zginęły bez zamykania połączenia). Większość implementacji TCP wyśle ​​pakiet podtrzymujący po długim czasie bez słyszenia z drugiej strony (2 godziny to wspólna wartość). Jeśli jednak istnieje zapora stanowa, która zapomina o połączeniu przed wysłaniem pakietów utrzymywania aktywności, połączenie długo działające, ale bezczynne, zostanie przerwane.

W takim przypadku rozwiązaniem jest zapobieganie bezczynności połączenia. OpenSSH ma opcję o nazwie ServerAliveInterval, której można użyć, aby zapobiec zbyt długiemu bezczynności połączenia (jako bonus, wykrywa, kiedy peer zmarł wcześniej, nawet jeśli połączenie jest bezczynne).

CesarB
źródło
Określony przedział czasu jest wyrażony w sekundach, więc możesz zapewnić pewne dostrojenie. Jeśli zapora stanowa ma 5-minutowy limit czasu bezczynności, wystarczy 60 lub 120 sekund, aby utrzymać połączenie otwarte. Jest to jeden ze sposobów, w jaki utrzymuję moje sesje ssh przez mój domowy router.
Darren Hall,
Dzięki, pomogłem. Ale zauważ (z odpowiedzi niższej rangi tutaj, superuser.com/a/146641/115515 ), że jeśli podasz ServerAliveInterval, a nie ServerAliveCountMax, możesz ssh celowo rozłączać się szybciej niż chciałeś.
metamatt
4
@metamatt, odpowiedź na niższą pozycję, do której się odwołujesz, jest niższa z ważnych powodów: TO NIEPRAWIDŁOWE.
Lambart
24

Na własnym komputerze Mac lub Linux skonfiguruj ssh, aby utrzymać serwer ssh przy życiu co 3 minuty. Otwórz terminal i przejdź do swojego niewidzialnego .ssh w domu:

cd ~/.ssh/ 

następnie utwórz 1-liniowy plik konfiguracyjny z:

echo "ServerAliveInterval 180" >> config

powinieneś również dodać:

ServerAliveCountMax xxxx (high number)

wartością domyślną jest 3, więc ServerAliveInterval 180 przestanie wysyłać po 9 minutach (3 z 3-minutowego przedziału czasu określonego przez ServerAliveInterval).

David Shaw
źródło
2
Pamiętaj, że twoje polecenie nie jest zalecane, jeśli masz już plik konfiguracyjny. Użycie >> do przekierowania byłoby znacznie lepsze!
Peltier
dlaczego ServerAliveInterval 180daje nam 6 minut? intuicja każe mi spróbować tego: 180/60 == 3. Czy ServerAliveIntervaldziała w wielokrotnościach 30 sekund?
nemesisfixx
@mcnemesis: ServerAliveInterval 180 oznacza 3 minuty. ServerAliveCountMax domyślnie 3 oznacza 3 z tych przedziałów, czyli 9 minut.
metamatt
2
Głosuję za odpowiedzią, ponieważ dziękuję za wzmiankę o ServerAliveCountMax i co się stanie, jeśli podasz ServerAliveInterval bez ServerAliveCountMax. Ale podobnie jak poprzednie komentarze, zauważam, że obliczenie „przestanie wysyłać po” jest niepoprawne i myślę, że ta odpowiedź byłaby lepsza, gdyby po prostu podała informacje o tych opcjach, nie mówiąc nam, jak zastosować je za pomocą poleceń cd i echa .
metamatt
20
Downvoting, ponieważ nie ma sensu ustawiać ServerAliveCountMax na „wysoką liczbę”. ServerAliveCountMax określa, ile razy będzie próbował wysłać komunikat „zachowaj” przed rezygnacją. Domyślna wartość to 3, więc w przypadku ServerAliveInterval 180 przestanie wysyłać TYLKO, jeśli serwer NIE ODPOWIADA po 9 minutach, w takim przypadku połączenie prawdopodobnie jest dobre i naprawdę nie działa.
Lambart
22

Użyłem następującego skryptu Bash, aby odradzać nowe tunele ssh po śmierci poprzedniego. Korzystanie ze skryptu jest przydatne, gdy nie chcesz lub nie możesz zainstalować dodatkowych pakietów lub użyć kompilatora.

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

Zauważ, że wymaga to pliku klucza do automatycznego nawiązania połączenia, ale dotyczy to również autossh.

Jawa
źródło
2
Powinieneś dodać powody, dla których używałbyś tego skryptu zamiast autossh, czy jest to po prostu łatwiejsze w ten sposób?
Kyrias
4
Nie pomogłoby to, gdyby sam ssh się zawiesił, prawda?
nafg
1
Pomaga, jeśli nie możesz zainstalować rzeczy na serwerze. autossh nie jest fabrycznie zainstalowany, a biurokracja bywa czasem bardzo tępa.
quarkex
Tak, lepiej nie trzeba instalować rzeczy. Robiłem to w ten sposób od roku jako jedyny sposób, aby utrzymać zdalną maszynę dostępną (nawet ustawić crontab, aby uruchamiała ją po ponownym uruchomieniu). To nigdy nie zawiodło, a co ważniejsze, wiem dlaczego nigdy nie zawiedzie.
sudo,
16

Systemd idealnie nadaje się do tego.

Utwórz plik usługi /etc/systemd/system/sshtunnel.servicezawierający:

[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

(Zmodyfikuj polecenie ssh, aby dopasować)

  • to będzie działać jako użytkownik, sshtunnelwięc upewnij się, że użytkownik istnieje jako pierwszy
  • problem, systemctl enable sshtunnelaby ustawić, aby uruchamiał się w czasie uruchamiania
  • problem, systemctl start sshtunnelaby rozpocząć natychmiast

Aktualizacja stycznia 2018 r . : niektóre dystrybucje (np. Fedora 27) mogą korzystać z polityki SELinux, aby uniemożliwić korzystanie z SSH z systemd init, w takim przypadku konieczne będzie utworzenie niestandardowej polityki w celu zapewnienia niezbędnych wyjątków.

IanB
źródło
2
Wygląda to bardzo podobnie do mojej treści: gist.github.com/guettli/… Informacje zwrotne są mile widziane!
guettli,
Doskonały do systemdsystemu. Jeśli ktoś użyje Restart=on-failuretego, wówczas ręczne zabicie klienta SSH nie spowoduje ponownego uruchomienia przez systemd jako klienta SSH z pomyślnym zakończeniem.
David Tonhofer
Jeśli chcesz uruchomić ssh ze skryptu (bash) podanego jako argument, aby ExecStartna przykład zbudować sshlistę argumentów, wykonaj podstawowe kontrole itp., A następnie wywołaj go ze skryptu w ten sposób exec /bin/ssh -N .... Oto moje polecenie: 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}"gdzie TUNNEL_INLET="127.0.0.1:3307"iTUNNEL_OUTLET="127.0.0.1:3306"
David Tonhofer
10

Z pewnością wygląda na to, że wszyscy źle interpretujecie ServerAliveCountMax. Jak rozumiem dokumentację, jest to liczba wiadomości z serwera, które mogą pozostać bez odpowiedzi bez przerywania połączenia. Tak więc w przypadkach takich jak omawiany tutaj ustawienie wysokiej wartości zapewni po prostu, że zawieszone połączenie nie zostanie wykryte i zakończone!

Po prostu ustawienie ServerAliveInterval powinno wystarczyć do rozwiązania problemu z zaporą zapominającą o połączeniu, a pozostawienie ServerAliveCountMax na niskim poziomie pozwoli początkowemu końcowi zauważyć awarię i zakończyć połączenie, jeśli połączenie i tak się nie powiedzie.

To, czego chcesz, to: 1) aby połączenie pozostało otwarte na stałe w normalnych okolicznościach, 2) aby wykryć awarię połączenia i strona inicjująca wyjść z niego po awarii, oraz 3) aby komenda ssh była wydawana za każdym razem wychodzi (sposób, w jaki to robisz, jest bardzo zależny od platformy, sugerowany przez Jawę skrypt „while true” jest jednym ze sposobów, w rzeczywistości system OS XI konfiguruje uruchomiony element).

użytkownik2793784
źródło
9

Zawsze używaj ServerAliveIntervalopcji SSH, jeśli problemy z tunelem są generowane przez wygasłe sesje NAT.

Zawsze używaj metody odrodzenia na wypadek, gdyby łączność całkowicie się pogorszyła, masz tutaj co najmniej trzy opcje:

  • program autossh
  • Skrypt bash ( while true do ssh ...; sleep 5; done) nie usuwa polecenia uśpienia, sshmoże szybko zawieść i odrodzi się zbyt wiele procesów
  • /etc/inittab, aby mieć dostęp do skrzynki wysłanej i zainstalowanej w innym kraju, za NAT, bez przekierowywania portów do skrzynki, możesz skonfigurować ją tak, aby utworzyła z powrotem tunel ssh:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • skrypt upstart na Ubuntu, gdzie /etc/inittabnie jest dostępny:

    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
    

lub zawsze używaj obu metod.

claudiuf
źródło
1
+1 za opcję wbudowaną, jeśli nie chcesz jej dla wszystkich połączeń SSH
1146334
Piszesz „na wypadek, gdyby łączność uległa całkowitemu awarii”. Teraz nie rozumiem, jakie problemy rozwiązuje autossh, a czego nie? Pomyślałem oczywiście, że to rozwiąże wszelkie zerwane połączenie, takie jak odłączenie kabla na kilka godzin, ale może nie?
Mads Skjern
6

Rozwiązałem ten problem w ten sposób:

Edytować

~/.ssh/config

I dodaj

ServerAliveInterval 15
ServerAliveCountMax 4

Według strony man dla 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
źródło
Co 15 sekund wydaje się dość często pingować serwer.
Lambart
@Lambart, ale jeśli połączenie jest bardzo niestabilne i często zrywa połączenia, przynajmniej wykrywa martwe połączenie i daje możliwość ponownej próby wcześniej.
binki
4

ExitOnForwardFailure yesjest dobrym dodatkiem do innych sugestii. Jeśli łączy się, ale nie może ustanowić przekierowania portów, jest dla ciebie tak samo bezużyteczny, jak gdyby w ogóle się nie łączył.

jcomeau_ictx
źródło
To bardzo dobry pomysł. Nawet autossh jest bezużyteczny, jeśli poprzednie połączenie jest postrzegane jako przekroczenie limitu czasu wcześniej po stronie zdalnej niż na hoście lokalnym, ponieważ w tym przypadku host lokalny spróbuje połączyć się ponownie, ale nie można ustanowić przekazywania, ponieważ port jest nadal otwarty.
Raúl Salinas-Monteagudo
1

Musiałem utrzymać długoterminowy tunel SSH. Moje rozwiązanie działało z serwera Linux i jest to tylko mały program C, który odradza ssh przy użyciu uwierzytelniania opartego na kluczach.

Nie jestem pewien co do zawieszenia, ale miałem śmierć tuneli z powodu przekroczenia limitu czasu.

Chciałbym podać kod dla respawnera, ale nie mogę go teraz znaleźć.

baumgart
źródło
1

podczas gdy istnieją narzędzia takie jak autossh, które pomagają zrestartować sesję ssh ... to, co uważam za naprawdę przydatne, to uruchomienie polecenia „screen”. Umożliwia Wznowienie sesji SSH nawet po rozłączeniu. Szczególnie przydatne, jeśli połączenie nie jest tak niezawodne, jak powinno być.

... nie zapomnij zaznaczyć, że jest to „poprawna” odpowiedź, jeśli pomaga ci! ;-)

koss
źródło
7
... ale pytanie dotyczyło tego, jak utrzymywać tunele SSH otwarte, a nie tylko sesję terminalową. Ekran jest świetny!
akent
Już korzystam z ekranu, ale to nie rozwiązuje mojego problemu: - / Dziękuję za odpowiedź.
Peltier
1

Trochę hack, ale lubię używać screena, żeby to zachować. Obecnie mam zdalne przekazywanie, które działa od tygodni.

Przykład, zaczynając lokalnie:

screen
ssh -R ......

Po zastosowaniu zdalnego przekazywania i gdy na komputerze zdalnym znajduje się powłoka:

screen
Ctrl + a + d

Masz teraz nieprzerwany zdalny przekaz. Sztuką jest uruchomienie ekranu na obu końcach

landypro
źródło
1

Ostatnio sam miałem ten problem, ponieważ te rozwiązania wymagają ponownego wprowadzenia hasła za każdym razem, jeśli używasz loginu do hasła. Użyłem sshpass w pętli wraz z monitem tekstowym, aby uniknąć posiadania hasła w pliku wsadowym.

Pomyślałem, że podzielę się tym rozwiązaniem na wypadek, gdyby ktoś miał ten sam problem:

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

Miałem podobne problemy z poprzednim usługodawcą internetowym. Dla mnie było tak samo z każdym połączeniem tcp, odwiedzaniem stron internetowych lub wysyłaniem poczty.

Rozwiązaniem było skonfigurowanie połączenia VPN przez UDP (korzystałem z OpenVPN). To połączenie było bardziej tolerancyjne na wszystko, co powodowało rozłączenia. Następnie możesz uruchomić dowolną usługę za pośrednictwem tego połączenia.

Nadal mogą występować problemy z połączeniem, ale ponieważ tunel będzie bardziej tolerancyjny, każda sesja ssh będzie odczuwać krótkie zawieszenie, a nie rozłączanie.

Aby to zrobić, potrzebujesz usługi VPN online, którą możesz skonfigurować na własnym serwerze.

hultqvist
źródło
0

Ponieważ autosshnie spełnia naszych potrzeb (istnieje błąd, jeśli nie może połączyć się z serwerem przy pierwszej próbie), napisaliśmy aplikację czysto bashową: https://github.com/aktos-io/link- z serwerem

Domyślnie tworzy tunel zwrotny dla portu sshd NODE (22) na serwerze. Jeśli musisz wykonać inne czynności (takie jak przekierowanie dodatkowych portów, wysyłanie wiadomości e-mail przy połączeniu itp.), Możesz umieścić swoje skrypty on-connecti on-disconnectfoldery.

ceremcem
źródło