Як надійно тримати тунель SSH відкритим?


234

Я використовую тунель SSH з роботи, щоб обійти різні ідотичні брандмауери (це нормально з моїм начальником :)). Проблема полягає в тому, що через деякий час ssh-з'єднання зазвичай висить, і тунель порушений.

Якби я міг хоча б автоматично контролювати тунель, я міг би перезапустити тунель, коли він висить, але я навіть не придумав, як це зробити.

Бонусні бали тому, хто може мені підказати, як запобігти моєму ssh-зв’язку, звісно!


Це ваш тунель мертвий через бездіяльність? У мене був ця проблема , коли туннелирование порти з телефону , так що я , нарешті , закінчився нерест фіктивних команд на зв'язку , щоб зробити його «живим» , використовуючи watchкоманду типу: watch -n1 60 echo "wiiiii". Тунель не загине, якщо мережа не зламана або ви не використовуєте її.
erm3nda

Відповіді:


280

Здається, що вам потрібно автошшш . Це дозволить контролювати ssh-тунель і перезапустити його за потребою. Ми використовуємо його пару років, і, здається, він працює добре.

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

Детальніше про параметр -M тут


2
+1 для autossh, він робить те, що написано на жерсті. Я вважаю, що частиною його функціональності є також відправлення збережених пакетів стилів, щоб запобігти будь-який час очікування.
akent

30
Чи можете ви поставити приклад тунелю, використовуючи autosshу відповіді?
Ехтеш Чудхурі

5
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 username@myoutsidebox.com Ви можете помітити, що я налаштував це за допомогою -nNT, який не створює віддалений термінал, щоб я міг поставити autossh на задній план, і -i варіант для SSH використовувати файл .pem. Якщо ви постійно будете підтримувати зв’язок відкритим, я напевно рекомендую пройти додаткові налаштування.
жукель


2
Я зробив це, щоб зробити його повторним при зміні мережі, він добре працює для мене: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 server@example.com
Люк Стенлі

39

Усі стаціонарні брандмауери забувають про з'єднання після того, як деякий час не бачать пакет для цього з'єднання (щоб запобігти переповненню таблиць стану, коли обидва кінці загинули, не закриваючи з'єднання). Більшість реалізацій TCP надсилатиме пакет збереженого пакету через довгий час, не чуючи з іншого боку (2 години - це загальне значення). Якщо, однак, є потужний брандмауер, який забуде про з'єднання перед тим, як пакети зберігатимуть можливість збереження, довгоживуче, але непрацююче з'єднання загине.

Якщо це так, рішення полягає в тому, щоб не допустити з’єднання в режимі очікування. У OpenSSH є параметр під назвою ServerAliveInterval, який може бути використаний для запобігання з’єднанню в режимі очікування занадто довго (як бонус, він виявить, коли однолітник помер раніше, навіть якщо з'єднання не працює).


Зазначений інтервал є в секундах, тому ви можете забезпечити точну настройку. Якщо у вашого стаціонарного брандмауера є 5-хвилинний час очікування в режимі очікування, то 60 або 120 секунд достатньо, щоб з'єднання було відкритим. Це один із способів тримати свої сеанси ssh через домашній маршрутизатор відкритим.
Даррен Хол

Дякую, це допомогло. Але зауважте (з відповіді нижчого рангу тут, superuser.com/a/146641/115515 ), що якщо ви вказали ServerAliveInterval, а не ServerAliveCountMax, ви можете виявити, що ssh навмисно відключається раніше, ніж ви хотіли.
метамат

4
@metamatt, відповідь нижчого рангу, на який ви посилаєтесь, нижчий за рейтингом: це неправильно.
Ламбарт

24

На власному комп'ютері Mac або Linux налаштуйте ваш ssh кожен раз на 3 хвилини. Відкрийте термінал і перейдіть у свій будинок свого невидимого .ssh:

cd ~/.ssh/ 

потім створіть конфігураційний файл 1 рядка за допомогою:

echo "ServerAliveInterval 180" >> config

Ви також повинні додати:

ServerAliveCountMax xxxx (high number)

за замовчуванням - 3, тому ServerAliveInterval 180 припинить надсилання через 9 хвилин (3 з 3-хвилинного інтервалу, визначеного ServerAliveInterval).


2
Зауважте, що ваша команда не рекомендується, якщо у вас вже є файл конфігурації. Використання >> для перенаправлення було б набагато краще!
Пельтьє

чому ServerAliveInterval 180нам дають 6 хвилин? інтуїція змушує мене спробувати це: 180/60 == 3. Отже, чи ServerAliveIntervalпрацює кратним 30 секунд?
nemesisfixx

@mcnemesis: ServerAliveInterval 180 означає 3 хвилини. За замовчуванням ServerAliveCountMax 3 означає 3 з цих інтервалів, тобто 9 хвилин.
метамат

2
Я голосую за цю відповідь, тому що дякую за згадування ServerAliveCountMax, і що трапиться, якщо ви вказали ServerAliveInterval без ServerAliveCountMax. Але, як і попередні коментарі, я помічаю, що обчислення "перестане надсилатись після" є помилковим, і я думаю, що ця відповідь послужила б краще, якби вона просто дала інформацію про ці параметри, не розповідаючи, як їх застосовувати за допомогою команд cd та echo. .
метамат

20
Нахил, оскільки немає сенсу встановлювати ServerAliveCountMax на "високе число". ServerAliveCountMax вказує, скільки разів він намагатиметься надіслати повідомлення "keepalive" перед тим, як відмовитись. За замовчуванням - 3, тож із сервером ServerAliveInterval 180 він перестане надсилати ТОЛЬКО, якщо сервер НЕ ВІДПОВІДАЮТЬ через 9 хвилин, і в цьому випадку ваш зв’язок, ймовірно, справді невдалий.
Ламбарт

22

Я використовував наступний сценарій Bash, щоб продовжувати нерестувати нові тунелі ssh, коли попередній відмирає. Використання сценарію зручно, коли ви не хочете або не можете встановити додаткові пакети або використовувати компілятор.

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

Зауважте, що для автоматичного встановлення з’єднання для цього потрібен файл ключів, але це теж справа з автосмішкою.


2
Ви повинні додати будь-які причини, що ви використовували б цей скрипт через autossh, чи це просто так?
kyrias

4
Це не допомогло б, якщо сам ssh замерзне, чи не так?
нафг

1
Це допомагає, якщо ви не можете встановити речі на сервері. autossh не встановлюється заздалегідь, а бюрократія іноді дуже тупа.
кваркс

Так, бажано не встановлювати речі. Я роблю це таким чином протягом року, як єдиний спосіб зберегти доступну віддалену машину (навіть встановити crontab, щоб запустити її при перезавантаженні). Це ніколи не провалюється, і що ще важливіше, я знаю, чому він ніколи не вийде з ладу.
судо

16

Systemd ідеально підходить для цього.

Створіть файл служби, /etc/systemd/system/sshtunnel.serviceщо містить:

[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

(Змініть команду ssh відповідно до)

  • це буде працювати як користувач, sshtunnelтому переконайтеся, що користувач спочатку існує
  • проблему, systemctl enable sshtunnelщоб встановити його для запуску під час завантаження
  • питання, systemctl start sshtunnelщоб розпочати негайно

Оновлення січня 2018 року : деякі дистрибутиви (наприклад, Fedora 27) можуть використовувати політику SELinux для запобігання використанню SSH від systemd init; у цьому випадку для створення необхідних винятків потрібно створити спеціальну політику.


2
Це дуже схоже на мою суть: gist.github.com/guettli/… Відгуки вітаються!
guettli

Відмінно підходить для systemdсистеми. Якщо користуватися, Restart=on-failureто вручну вбивати клієнт SSH не призведе до перезавантаження системи, як клієнт SSH з успішним виходом.
Девід Тонхофер

Якщо ви хочете запустити ssh із (bash) скрипту, поданого як аргумент, ExecStartнаприклад, для складання списку sshаргументів, зробіть основні перевірки тощо, а потім зателефонуйте йому зі сценарію так exec /bin/ssh -N .... Ось моя команда: 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}"де TUNNEL_INLET="127.0.0.1:3307"іTUNNEL_OUTLET="127.0.0.1:3306"
Девід Тонхофер

10

Мені впевнено здається, що ви все неправильно трактуєте ServerAliveCountMax. Як я розумію, документи, кількість повідомлень на сервері, що живуть, може залишатися без відповіді без припинення з'єднання. Тож у таких випадках, як ми тут обговорюємо, встановлення високого значення просто гарантуватиме, що висяче з'єднання не буде виявлено та припинено!

Просте встановлення ServerAliveInterval повинно бути достатнім, щоб вирішити проблему, коли брандмауер забув про з'єднання, а залишення ServerAliveCountMax низьким дозволить початковому кінці помітити помилку і припинити, якщо з'єднання все-таки не вдасться.

1) для того, щоб з'єднання постійно залишалося відкритим при звичайних обставинах; 2) для виявлення відмови з'єднання та вихідної сторони для виходу з ладу; 3) для повторної видачі команди ssh щоразу виходи (як це робиться від платформи, сценарій "в той час як справжній", запропонований Jawa, є одним із способів, на OS XI фактично налаштований запущений елемент).


9

Завжди використовуйте ServerAliveIntervalпараметр SSH у випадку, якщо проблеми з тунелем породжуються минулими сеансами NAT.

Завжди використовуйте метод повторного зашивання, якщо зв’язок повністю знижується, тут у вас є щонайменше три варіанти:

  • програма autossh
  • bash script ( while true do ssh ...; sleep 5; done) не видаляйте команду сну, sshможе швидко вийти з ладу, і ви відновите занадто багато процесів
  • /etc/inittab, щоб мати доступ до коробки, що постачається та встановлюється в іншій країні, за NAT, не пересилаючи порт до коробки, ви можете налаштувати її, щоб створити туди ssh назад до вас:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • сценарій на початку Ubuntu, де /etc/inittabвін недоступний:

    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
    

або завжди використовувати обидва методи.


1
+1 для вбудованого варіанту, якщо ви не хочете його для всіх своїх підключень SSH
користувач1146334

Ви пишете "у випадку, якщо зв’язок повністю знизиться". Зараз я не розумію, які проблеми виправляє автосш, а що ні? Я думав, звичайно, це подбає про будь-яке розірване з'єднання, як відключення кабелю на кілька годин, але, можливо, ні?
Mads Skjern

6

Я вирішив цю проблему цим:

Редагувати

~/.ssh/config

І додайте

ServerAliveInterval 15
ServerAliveCountMax 4

Відповідно до сторінки людини для 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.

Кожні 15 секунд здається, що часто пінг-сервер.
Ламбарт

@Lambart, але якщо з'єднання справді розмито і часто перериває з'єднання, воно принаймні виявляє мертве з'єднання і дає можливість повторити спробу раніше.
бінкі

4

ExitOnForwardFailure yesє гарним доповненням до інших пропозицій. Якщо він з'єднується, але не може встановити переадресацію порту, він так само марний для вас, як ніби він взагалі не з’єднався.


Це дуже гарна ідея. Навіть autossh є марним, якщо попереднє з'єднання сприймається раніше, ніж вимкнено на віддаленій стороні, ніж у локальному хості, оскільки в цьому випадку локальний хост спробує знову підключитися, але переадресацію неможливо встановити, оскільки порт все ще відкритий.
Рауль Салінас-Монтеагудо

1

У мене була потреба підтримувати тунель SSH довгостроково. Моє рішення було запущено з сервера Linux, і це лише невелика програма C, яка повторно створює ssh, використовуючи аутентифікацію на основі ключів.

Я не впевнений у висі, але в мене тунелі загинули через тайм-аути.

Я хотів би надати код для репаунер, але, здається, зараз не знаходжу його.


1

Хоча є такі інструменти, як autossh, який допомагає перезапустити ssh-сеанс ... що мені здається дуже корисним, це запустити команду 'screen'. Це дозволяє відновити свої сеанси ssh навіть після відключення. Особливо корисно, якщо ваш зв’язок не такий надійний, як повинен бути.

... не забудьте позначити це "правильна" відповідь, якщо це допоможе вам k! ;-)


7
... але питання полягало у тому, як тримати тунелі SSH відкритими, а не лише термінальним сеансом. Екран ЦЕ чудовий!
akent

Я вже використовую екран, але це не вирішує моєї проблеми: - / Дякую за вашу відповідь.
Пельтьє

1

Трохи хак, але я люблю використовувати екран, щоб зберегти це. На даний момент у мене є віддалений вперед, який працює тижнями.

Приклад, починаючи локально:

screen
ssh -R ......

Коли застосовано віддалений вперед, і у вас є оболонка на віддаленому комп'ютері:

screen
Ctrl + a + d

Тепер у вас безперебійний віддалений вперед. Трюк - запустити екран з обох кінців


1

Нещодавно я мав цю проблему, тому що ці рішення вимагають від вас повторного введення пароля щоразу, коли ви використовуєте логін пароля, я використовував sshpass у циклі разом із текстовим підказом, щоб уникнути наявності пароля у пакетному файлі.

Я думав, що я поділюсь своїм рішенням щодо цієї теми на випадок, якщо хтось інший має те саме питання:

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

0

У мене були подібні проблеми з попереднім провайдером. Для мене це було те саме з будь-яким з'єднанням tcp, відвідуванням веб-сайтів або відправленням пошти.

Рішенням було налаштувати VPN-з'єднання через UDP (я використовував OpenVPN). Це з'єднання було більш толерантним до того, що спричинило відключення. Тоді ви можете запустити будь-яку службу через це з'єднання.

З підключенням все ще можуть виникнути проблеми, але оскільки тунель буде більш толерантним, будь-який сеанс ssh відчує коротке затримку, а не відключення.

Для цього вам знадобиться послуга VPN в Інтернеті, яку можна налаштувати на власному сервері.


0

Оскільки autosshне відповідає нашим потребам (він існує з помилкою, якщо він не може підключитися до сервера при першій спробі), ми написали чисту програму bash: https://github.com/aktos-io/link- з-сервером

Він створює зворотний тунель для sshd-порту (22) NODE на сервері за замовчуванням. Якщо вам потрібно виконати будь-які інші дії (наприклад, переадресація додаткових портів, надсилання електронної пошти під час з'єднання тощо), ви можете розмістити свої сценарії on-connectта on-disconnectпапки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.