Как надежно поддерживать открытый туннель SSH?
Я использую SSH-туннель с работы, чтобы обойти различные идиотские брандмауэры (это нормально для моего босса:)). Проблема в том, что через некоторое время соединение ssh обычно зависает, и туннель нарушается.
Если бы я мог хотя бы контролировать туннель автоматически, я мог бы перезапустить туннель, когда он зависает, но я даже не придумал, как это сделать.
Бонусные баллы для того, кто скажет мне, как предотвратить зависание моего ssh-соединения, конечно же!
19 ответов
Все брандмауэры с сохранением состояния забывают о соединении после того, как некоторое время не видели пакета для этого соединения (чтобы таблицы состояний не заполнялись соединениями, где оба конца погибли, не закрывая соединение). Большинство реализаций TCP отправят пакет keepalive через долгое время, не услышав об этом с другой стороны (обычно 2 часа). Однако, если существует межсетевой экран с отслеживанием состояния, который забывает о соединении до того, как пакеты поддержки активности могут быть отправлены, долгоживущее, но простое соединение прекратит работу.
Если дело обстоит так, решение состоит в том, чтобы препятствовать тому, чтобы соединение стало бездействующим. OpenSSH имеет опцию ServerAliveInterval, которую можно использовать для предотвращения слишком долгого простоя соединения (в качестве бонуса он обнаружит, когда партнер умер раньше, даже если соединение простаивает).
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, и в этом случае необходимо будет создать собственную политику для предоставления необходимых исключений.
Я использовал следующий скрипт Bash, чтобы продолжать порождать новые ssh-туннели, когда предыдущий умирает. Использование скрипта удобно, когда вы не хотите или не можете установить дополнительные пакеты или использовать компилятор.
while true
do
ssh <ssh_options> [user@]hostname
sleep 15
done
Обратите внимание, что для этого требуется ключевой файл для автоматического установления соединения, но это также относится и к autossh.
На вашем компьютере Mac или Linux сконфигурируйте ваш ssh, поддерживайте работу сервера ssh каждые 3 минуты. Откройте терминал и отправьте свой невидимый.ssh в свой дом:
cd ~/.ssh/
затем создайте файл конфигурации в 1 строку с:
echo "ServerAliveInterval 180" >> config
Вы также должны добавить:
ServerAliveCountMax xxxx (high number)
по умолчанию установлено значение 3, поэтому ServerAliveInterval 180 прекратит отправку через 9 минут (3 из 3-минутного интервала, указанного в ServerAliveInterval).
Для тех, кто не хочет (или не может) использовать AutoSSH...
У меня есть NAS, к которому я хочу подключиться из Интернета, я не могу использовать переадресацию портов, потому что мой интернет-провайдер использует CGNAT (мой общедоступный IP-адрес на самом деле не является моим общедоступным IP-адресом, я нахожусь за другим маршрутизатором, над которым не имею никакого контроля). ). Поэтому, чтобы получить доступ к своему NAS, у меня есть VPS (который я арендую у OVH за очень небольшую ежемесячную плату) и у него фиксированный общедоступный IP-адрес. Таким образом, чтобы получить доступ к моему NAS из Интернета, мне просто нужно создать SSH-туннель между моим NAS и моим VPS, который будет надежно оставаться открытым все время (для круглосуточного доступа). Однако я пострадал от того, что туннель SSH был «закрыт» из-за бездействия (несмотря на то, что процесс ssh оставался включенным). Это можно легко преодолеть, если клиент (в моем случае VPS) «пингует» сервер (в моем случае NAS), используя опцию поддержания активности.
Чтобы создать 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>
Чтобы объяснить эту команду:
-
- Не выполнять удаленную команду; это полезно просто для переадресации портов. -
- Отключить распределение псевдо-терминала. -
- Указывает, что данный порт на удаленном (серверном) хосте должен быть перенаправлен на данный хост и порт на локальной стороне. В данном случае это означает переадресацию порта 80 удаленного сервера на порт 8080 клиента. -
- Укажите путь к ключу ssh, используемому для установления сеанса ssh, без этого вам придется ввести имя пользователя (если он не указан) и пароль для установления сеанса ssh. -
— количество секунд, в течение которых клиент будет ждать перед отправкой на сервер сообщения «сервер активен», чтобы поддерживать соединение. -
- количество сообщений «сервер активен», которые могут быть отправлены без ответа от сервера. Если этот порог будет достигнут, ssh отключится от сервера, завершив сеанс. -
- если установлено значение «да», соединение будет прервано, если ssh не может настроить все запрошенные динамические, туннельные, локальные и удаленные переадресации портов (например, если любой конец не может привязать и прослушивать указанный порт). -
- Указывает учетную запись пользователя используется для установления ssh-сеанса удаленной переадресации портов с сервером .
Также стоит добавить некоторые параметры конфигурации ssh на сервер (в моем случае на моем VPS); добавив следующий файл, если он еще не существует:
[foouser@vps ~]$ cat /home/foouser/.ssh/config
Host *
TCPKeepAlive yes
ClientAliveInterval 30
ClientAliveCountMax 9999
Примечание: вы можете заменить
Процесс SystemD (Synology NAS)
Еще у меня есть эта команда (та, которая запускает SSH-туннель как системный процесс, если кому интересно, вот скрипт:
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 Tunnel:
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 каждый раз, когда оно exits (то, как вы это делаете, очень зависит от платформы, сценарий "while true", предложенный Jawa, является одним из способов, на OS X I фактически настраивает элемент launchd).
Всегда используйте ServerAliveInterval
Опция SSH, если проблемы с туннелем генерируются сеансами NAT с истекшим сроком действия.
Всегда используйте метод возрождения в случае, если подключение полностью отключается, у вас есть по крайней мере три варианта:
- программа autossh
- скрипт bash (
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'
скрипт 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
Согласно справочной странице для 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.
ExitOnForwardFailure yes
является хорошим дополнением к другим предложениям. Если он подключается, но не может установить переадресацию порта, он так же бесполезен для вас, как если бы он вообще не подключался.
Немного взломать, но мне нравится использовать экран, чтобы сохранить это. В настоящее время у меня есть дистанционный форвард, который работает уже несколько недель.
Пример, начиная локально:
screen
ssh -R ......
Когда удаленная пересылка применена, и у вас есть оболочка на удаленном компьютере:
screen
Ctrl + a + d
Теперь у вас есть непрерывный пульт дистанционного пересылки. Хитрость заключается в том, чтобы запустить экран на обоих концах
Как autossh
не соответствует нашим потребностям (существует с ошибкой, если он не может подключиться к серверу с первой попытки), мы написали приложение для чистого bash: https://github.com/aktos-io/link-with-server
По умолчанию он создает обратный туннель для порта sshd (22) NODE на сервере. Если вам нужно выполнить какие-либо другие действия (например, переадресация дополнительных портов, отправка писем по соединению и т. Д.), Вы можете разместить свои сценарии on-connect
а также on-disconnect
папки.
Мне нужно было поддерживать SSH-туннель в долгосрочной перспективе. Мое решение запускалось с сервера Linux, и это всего лишь небольшая программа на C, которая запускает ssh с использованием аутентификации на основе ключей.
Я не уверен насчет повешения, но у меня туннели умирают из-за тайм-аутов.
Я хотел бы предоставить код для респауна, но я не могу найти его прямо сейчас.
Недавно у меня возникла эта проблема, потому что эти решения требуют повторного ввода пароля каждый раз, если вы используете пароль для входа в систему, я использовал sshpass в цикле вместе с текстовым приглашением, чтобы избежать ввода пароля в командный файл.
Думал, что поделюсь своим решением по этой теме на случай, если у кого-то возникнет такая же проблема
#!/bin/bash
read -s -p "Password: " pass
while true
do
sshpass -p "$pass" ssh user@address -p port
sleep 1
done
Вы могли бы очень просто использовать
tmux
на вашем удаленном сервере. Это позволяет вам открыть "сеанс" на удаленном компьютере, где вы будете запускать удаленный код.
Затем вы можете выйти (или потерять соединение в вашем случае) с удаленного компьютера, не беспокоясь, учитывая, что ваш код безопасно работает на вашем
tmux
сеанс.
Наконец, когда вы повторно подключаетесь к удаленному серверу, вам нужно только
tmux attach
вернуться к сеансу, который вы ранее открывали, и все.
В то время как есть такие инструменты, как autossh, которые помогают перезапустить сессию ssh... что я считаю действительно полезным, это запустить команду 'screen'. Это позволяет вам возобновить сеансы SSH даже после отключения. Особенно полезно, если ваше соединение не так надежно, как должно быть.
... не забудьте отметить, что это "правильный" ответ, если он поможет вам k!;-)
Я использую простую команду терминала после подключения SSH
watch -n 60 echo 1
Он поддерживает мое SSH-соединение все время, если только не возникнут проблемы с Интернетом.
Нет необходимости устанавливать дополнительные пакеты, такие как autossh.
У меня были похожие проблемы с моим предыдущим провайдером. Для меня это было то же самое с любым TCP-соединением, посещением веб-сайтов или отправкой почты.
Решением было настроить VPN-соединение по UDP(я использовал OpenVPN). Эта связь была более терпимой к тому, что вызвало разъединения. Затем вы можете запустить любой сервис через это соединение.
Все еще могут быть проблемы с соединением, но, так как туннель будет более терпимым, любой сеанс ssh будет ощущать кратковременную задержку, а не отключение.
Для этого вам потребуется онлайн-сервис VPN, который вы можете настроить на своем собственном сервере.
Тупой способ, который просто работает
Вместо использованияautossh
Я настоятельно рекомендую вам использовать sshpass с ssh через скрипт Python, который автоматически переподключается при отключении сеанса ssh. Это может помочь в некоторых рабочих процессах, к которым вы предпочитаете иметь доступ.python
выполнить некоторые задачи, когда произойдет отключение.
Установить
Если вы хотите использовать для аутентификации пароль 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 hello@5.tcp.ngrok.io -p 22")
# perform x task here, like alert the user, you have choices.
print("[+] Reconnecting Again")