Вопрос или проблема
Я использую SSH туннель с работы, чтобы обойти различные идиотские файрволы (это нормально для моего босса :)). Проблема в том, что через некоторое время SSH соединение обычно зависает, и туннель разрывается.
Если бы я хотя бы мог автоматически отслеживать туннель, я бы мог перезапускать туннель, когда он зависает, но я даже не нашел способа сделать это.
Бонусные баллы для того, кто подскажет, как предотвратить зависание SSH соединения, конечно!
Похоже, вам нужен autossh. Он будет отслеживать SSH туннель и перезапускать его по мере необходимости. Мы использовали его несколько лет, и он, кажется, работает хорошо.
autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C
Более подробная информация о параметре -M здесь
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, в этом случае потребуется создание пользовательской политики для предоставления необходимых исключений.
Все состоявшие файрволы забывают о соединении после того, как в течение некоторого времени не видят пакета для этого соединения (чтобы предотвратить переполнение таблиц состояний соединениями, где обе стороны завершили работу без закрытия соединения). Большинство реализаций TCP отправят пакет keepalive после долгого времени без ответа от другой стороны (обычное значение – 2 часа). Однако, если есть файрвол с состоянием, который забывает о соединении до того, как могут быть отправлены пакеты keepalive, долго-живущее, но неактивное соединение умрет.
Если это так, решение состоит в том, чтобы предотвратить бездействие соединения. OpenSSH имеет опцию под названием ServerAliveInterval, которую можно использовать для предотвращения долгого бездействия соединения (кроме того, она рано обнаружит, когда другой конец умер, даже если соединение бездействует).
Я использовал следующий скрипт Bash для повторного создания новых SSH туннелей, когда предыдущий умирает. Использование скрипта удобно, когда не хотите или не можете установить дополнительные пакеты или использовать компилятор.
while true
do
ssh <ssh_options> [user@]hostname
sleep 15
done
Обратите внимание, что это требует наличия файла ключа для автоматического установления соединения, но это применимо и для autossh.
На вашем Mac или Linux устройстве настройте SSH так, чтобы сервер SSH оставался активным каждые 3 минуты. Откройте терминал и перейдите в ваш невидимый .ssh в вашей домашней директории:
cd ~/.ssh/
затем создайте файл конфигурации с одной строкой:
echo "ServerAliveInterval 180" >> config
Вы можете установить это на высокое число, чтобы ваш SSH пытался достучаться до сервера и не завершал соединение еще, даже если он не ответил на столько пакетов keepalive:
ServerAliveCountMax xxxx (высокое число)
По умолчанию установлено 3, так что ServerAliveInterval 180 прекратит отправку keepalives и завершит соединение через 9 минут, в течение которых ни один из keepalives не получил ответ (3 из 3-минутного интервала, указанного ServerAliveInterval).
Для тех, кто не хочет или не может использовать AutoSSH…
У меня есть NAS, который я хочу достичь из интернета, я не могу использовать перенаправление портов, потому что мой интернет-провайдер использует CGNAT (мой публичный IP не является моим публичным IP, я нахожусь за другим роутером, которым я не управляю). Таким образом, чтобы достичь моего NAS, у меня есть VPS (который я арендую у OVH за небольшие ежемесячные расходы), и у него есть фиксированный публичный IP адрес. Поэтому, чтобы добраться до моего NAS из интернета, мне просто нужно создать SSH туннель между моим NAS и моим VPS, который надежно остается открытым все время (для круглосуточного доступа). Однако, я сталкивался с тем, что SSH туннель “закрывался” из-за бездействия (несмотря на то, что процесс ssh остается запущенным). Это легко преодолеть, заставив клиента (в моем случае, VPS) “пинговать” сервер (в моем случае, NAS), используя опцию keep alive.
Чтобы создать SSH туннель, я выполняю следующую команду (с NAS):
ssh -NT -o ServerAliveInterval=60 -o ServerAliveCountMax=10 -o ExitOnForwardFailure=yes -i /var/services/homes/foouser/.ssh/id_rsa -R 8080:localhost:80 -R 4443:localhost:443 foouser@<VPS>
Для объяснения этой команды:
-N
– Не выполнять удаленную команду; это полезно только для пересылки портов.-T
– Отключить выделение псевдо-tty.-R 8080:localhost:80
– Указывает, что данный порт на удаленном (серверном) хосте будет пересылаться на данный хост и порт на локальной стороне. В этом случае, это означает пересылку порта 80 удаленного сервера на порт 8080 клиента.-i /path/to/key
– Укажите путь к SSH ключу, используемому для установления SSH сессии, без этого вам придется вводить имя пользователя (если не указано) и пароль для установления SSH сессии.ServerAliveInterval
– число секунд, которые клиент будет ждать перед отправкой “сообщения о живучести сервера” серверу, чтобы сохранить соединение активным.ServerAliveCountMax
– количество “сообщений о живучести сервера”, которые могут быть отправлены без ответа от сервера. Если этот порог достигнут, ssh отключится от сервера, окончив сессию.ExitOnForwardFailure
– если установлено “yes”, соединение будет разорвано, если ssh не может настроить все запрошенные динамические, туннельные, локальные и удаленные пересылки портов (например, если ни один из концов не может привязаться и слушать на указанном порту).foouser@<VPS>
– указывает аккаунт пользователяfoouser
, используемый для установления SSH сессии удаленного пересылки портов с сервером<VPS>
.
Также стоит добавить некоторые параметры конфигурации SSH на сервер (в моем случае, на мой VPS); добавив следующий файл, если он еще не существует:
[foouser@vps ~]$ cat /home/foouser/.ssh/config
Host *
TCPKeepAlive yes
ClientAliveInterval 30
ClientAliveCountMax 9999
Примечание: вы можете заменить *
(что означает применение этой конфигурации ко “всем хостам”) на конкретный хост – в моем случае мой NAS (то есть хост, который подключается к моему VPS) находится за моим роутером; публичный IP адрес моего роутера часто меняется, так как он DHCP назначен (от моего интернет-поставщика), поэтому я оставил “все хосты”.
SystemD Процесс (Synology NAS)
У меня также есть эта команда (та, которая запускает SSH туннель как systemd процесс, если кто-то заинтересован, вот скрипт:
foouser@nas:~$ cat /etc/systemd/system/sshtunnel-web.service
[Unit]
Description=SSH Tunnel for WebStation
After=network.target
[Service]
Restart=always
RestartSec=1
User=foouser
ExecStart=/bin/ssh \
-NT \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=10 \
-o ExitOnForwardFailure=yes \
-i /var/services/homes/foouser/.ssh/id_rsa \
-R 8080:localhost:80 \
-R 4443:localhost:443 \
foouser@<VPS>
[Install]
WantedBy=multi-user.target
Для запуска и включения службы SSH туннеля:
foouser@nas:~$ sudo systemctl daemon-reload
foouser@nas:~$ sudo systemctl start sshtunnel-web.service
foouser@nas:~$ sudo systemctl enable sshtunnel-web.service
Это работает надежно для меня уже несколько месяцев. Это включает в себя надежность в случае перезагрузки моего домашнего роутера, VPS сервера и NAS.
Мне кажется, что вы все неправильно интерпретируете ServerAliveCountMax. Как я понимаю документацию, это количество сообщений о живучести сервера, которые могут быть отправлены без ответа из сервера перед тем, как соединение будет разорвано. Таким образом, в обсуждаемых нами случаях, установка его на высокое значение просто обеспечит, что “зависшее” соединение не будет обнаружено и окончено!
Просто установка ServerAliveInterval должна быть достаточной для решения проблемы с файрволом, забывающим о соединении, и оставляя ServerAliveCountMax на низком уровне позволит исходящему концу заметить неисправность и завершить соединение, если оно все равно не работает.
Вам нужно, чтобы 1) соединение оставалось открытым постоянно при нормальных обстоятельствах, 2) неисправность соединения была обнаружена и исходящий конец вышел при неисправности, и 3) команда ssh была снова выдана каждый раз при выходе (как вы это делаете зависят от платформы, сценарий “while true”, предложенный Jawa, является одним из способов, на OS X я фактически забил элемент в launchd).
Всегда используйте опцию SSH ServerAliveInterval
на случай, если проблемы с туннелем вызваны истекшими сессиями NAT.
Всегда используйте метод респаунения на случай, если соединение полностью прерывается, у вас есть как минимум три варианта здесь:
- программа autossh
- bash скрипт (
while true do ssh ...; sleep 5; done
) не удаляйте команду sleep,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'
-
upstart скрипт на 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
или всегда используйте оба метода.
Я решил эту проблему следующим образом:
Редактировать
~/.ssh/config
И добавить
ServerAliveInterval 15
ServerAliveCountMax 4
Согласно странице man для ssh_config:
ServerAliveCountMax
Устанавливает количество сообщений о живучести сервера (см. ниже), которые могут быть
отправлены без ssh(1), получающего какие-либо сообщения обратно от сервера.
Если этот порог достигнут, пока сообщения о живучести сервера
отправляются, ssh отключится от сервера, окончив
сессию. Важно отметить, что использование сообщений о живучести сервера
очень отличается от TCPKeepAlive (ниже). Сообщения о живучести сервера
отправляются через зашифрованный канал и, следовательно, не поддаются подделке.
Опция TCP keepalive, включенная TCPKeepAlive, поддается подделке. Механизм живучести сервера
ценен, когда клиент или сервер зависят от знания момента, когда соединение становится неактивным.
Значение по умолчанию - 3. Если, например, ServerAliveInterval
(см. ниже) установлено на 15, а ServerAliveCountMax оставлено по умолчанию, если сервер
становится неответственным, ssh отключится через примерно 45 секунд.
Эта опция применяется только к протоколу версии 2.
ServerAliveInterval
Устанавливает интервал таймаута в секундах, после которого, если
сервер не получил никаких данных, ssh(1) отправит сообщение
через зашифрованный канал, чтобы запросить ответ
от сервера. Значение по умолчанию - 0, что означает, что эти сообщения не будут
отправляться серверу. Эта опция применяется только к протоколу версии 2.
ExitOnForwardFailure yes
– хорошее дополнение к другим предложенным решениям. Если соединение установлено, но не удается создать перенаправление порта, оно так же бесполезно вам, как если бы оно не было установлено вовсе.
Небольшой хак, но мне нравится использовать screen для этого. У меня в настоящее время активно удаленное пересылка портов, которая работает уже несколько недель.
Пример, запущенный локально:
screen
ssh -R ......
Когда удаленное пересылка установлено, и у вас есть оболочка на удаленном компьютере:
screen
Ctrl + a + d
Теперь у вас есть непрерывное удаленное пересылка. Хитрость заключается в том, чтобы запускать screen на обоих концах
Недавно у меня возникла эта проблема, потому что эти решения требуют повторного ввода пароля каждый раз, если вы используете вход по паролю, я использовал sshpass в цикле в комбинации с текстовым приглашением, чтобы избежать наличия пароля в batch файле.
Думал, что поделюсь своим решением в этой теме, на случай, если у кого-то появится такая же проблема:
#!/bin/bash
read -s -p "Password: " pass
while true
do
sshpass -p "$pass" ssh user@address -p port
sleep 1
done
Поскольку autossh
не удовлетворяет нашим нуждам (он завершает работу с ошибкой, если не может подключиться к серверу при первой попытке), мы написали чисто bash приложение: https://github.com/aktos-io/link-with-server
Оно создает обратный туннель для порта SSHD УЗЛА (22) на сервере по умолчанию. Если вам нужно выполнить любые другие действия (например, перенаправление дополнительных портов, отправка писем при подключении и т. д.), вы можете поместить свои скрипты в папки on-connect
и on-disconnect
.
Вы могли бы очень просто использовать команду tmux
на вашем удаленном сервере. Она позволяет открыть “сессию” на удаленной машине, где вы запустите код.
Затем вы можете выйти (или потерять соединение в вашем случае) с удаленной машины без риска, что ваш код останется в целости и сохранности в вашей tmux
сессии.
Наконец, когда вы подключитесь снова на удаленном сервере, вам просто нужно tmux attach
, чтобы вернуться к сессии, открытую ранее, и все готово.
Я использую простую терминальную команду после SSH подключения
watch -n 60 echo 1
Она сохраняет мое SSH соединение все время, если нет проблем с интернетом.
Нет нужды устанавливать дополнительные пакеты, такие как autossh
Мне приходилось поддерживать SSH-туннель в течение длительного времени. Мое решение заключалось в запуске его на сервере Linux, и это просто небольшая программа на Си, которая возобновляет ssh, используя аутентификацию по ключам.
Я не уверен по поводу зависания, но у меня бывали туннели, умиравшие из-за таймаутов.
Я бы с удовольствием предоставил код для респаунера, но, похоже, на данный момент не могу его найти.
хотя есть такие инструменты, как autossh, которые помогают перезапустить ssh сессию… что я считаю действительно полезным, так это использование команды ‘screen’. Это позволяет вам ПОВТОРНО ПОДКЛЮЧИТЬ ваши ssh сессии даже после отключения. Особенно полезно, если ваше соединение не такое надежное, как должно быть.
…не забудьте отметить это как “правильный” ответ, если это поможет вам, окей! 😉
У меня были похожие проблемы с моим предыдущим интернет-провайдером.
Для меня это было одинаково для любого tcp соединения, посещение веб-сайтов или отправка почты.
Решением было настроить VPN соединение по UDP (я использовал OpenVPN). Это соединение было более терпимо к тому, что вызывало отключения. Затем вы можете запускать любую службу через это соединение.
Все еще могут возникнуть проблемы с соединением, но поскольку туннель будет более терпимым, любое ssh соединение будет ощущаться как короткая задержка, а не отключение.
Для этого вам понадобится онлайн VPN-служба, которую вы можете настроить на своем сервере.
Глупый способ, который просто работает
Вместо того чтобы использовать autossh
, который я настоятельно рекомендую, вы можете использовать sshpass с ssh через питон-скрипт, который автоматически переподключает, когда ssh сессия отключается. Это может помочь некоторым рабочим процессам, в которых вы предпочли бы иметь доступ к python
для выполнения некоторых задач, когда происходит отключение.
Установка sshpass
Если вы хотели бы использовать пароль SSH вместо файла key.pem для аутентификации, вам следует использовать sshpass
, чтобы установить sshpass, вы можете просто сделать: sudo apt-get install sshpass
import os
while True:
os.system("sshpass -p 'mypass' ssh -L 5000:0.0.0.0:5000 [email protected] -p 22")
# perform x task here, like alert the user, you have choices.
print("[+] Reconnecting Again")
.
Ответ или решение
Чтобы надежно поддерживать открытую SSH-туннель, важно учесть несколько ключевых факторов, таких как настройки защиты от тайм-аутов подключения, использование специализированных утилит для автоматического восстановление соединения и продуманная конфигурация SSH-клиента и сервера. Давайте рассмотрим, как этих целей можно достичь на практике.
Теория
SSH-туннели используются для безопасного пропуска данных между локальной и удаленной машинами, особенно при обходе ограничений сети или межсетевых экранов. Однако, во многих сетях могут возникать проблемы с прерыванием соединений из-за временных тайм-аутов или сброшенных пакетов со стороны провайдеров или межсетевых экранов. Для предотвращения таких разрывов необходимо регулярно отправлять так называемые "keepalive" пакеты, которые информируют сервер о том, что соединение ещё активно.
Пример
Примером инструмента, позволяющего автоматически следить за состоянием SSH-туннеля и перезапускать его в случае сбоя, является autossh. Эта утилита мониторит состояние SSH-сессий и автоматически перезапускает их при необходимости. Для ее использования достаточно выполнить следующую команду:
autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C
Модификатор -M
задает мониторинговый порт, а остальные параметры позволяют настроить проброс портов и другие аспекты SSH-соединения.
Еще одним способом поддержания SSH-туннеля является использование 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
Применение
-
Установите "ServerAliveInterval": Этот параметр конфигурации SSH позволяет настроить интервал между отправкой "keepalive" пакетов. Например, установка значения
60
секунд будет означать отправку пакета каждую минуту:echo "ServerAliveInterval 60" >> ~/.ssh/config
-
Используйте "ServerAliveCountMax": Этот параметр определяет, сколько "keepalive" пакетов может быть отправлено без ответа сервера, прежде чем соединение будет разорвано. Уменьшение этого значения поможет быстрее обнаруживать потерянные соединения.
-
Автоматизация с помощью скриптов: Простой скрипт на bash может помочь в автоматическом перезапуске SSH-туннеля, если он неожиданно завершится. Пример скрипта:
while true do ssh -o ServerAliveInterval=60 [user@]hostname sleep 15 done
-
Редактирование конфигурации сервера SSH: Также имеет смысл внести изменения в конфигурацию SSH-сервера. На сервере можно задать параметры
ClientAliveInterval
иClientAliveCountMax
, чтобы развёрнутый сервис также отправлял контрольный сигнал через определенные интервалы времени. -
Мониторинг и уведомления: Поскольку ваш туннель может служить критически важной частью вашего рабочего процесса, полезно настроить уведомления о состоянии туннеля, например, через почту или систему уведомлений.
Поддержка надежного SSH-туннеля требует как грамотной настройки параметров соединения, так и использования автоматизированных средств управления для обеспечения постоянного мониторинга и восстановления туннеля в случае неожиданного завершения. Это особенно важно в обстоятельствах, где минимальные простои соединения могут повлечь за собой значительные проблемы в работе.