Как надежно поддерживать открытый туннель SSH?

235

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

Если бы я мог хотя бы контролировать туннель автоматически, я мог бы перезапустить туннель, когда он зависает, но я даже не придумал, как это сделать.

Бонусные баллы для того, кто скажет мне, как предотвратить зависание моего ssh-соединения, конечно же!

Пельтье
источник
Это твой туннель мертв из-за бездействия? У меня был эта проблема , когда туннелирование порты с телефона , так что я , наконец , закончился нерест фиктивных команд на связи , чтобы сделать его «живым» , используя watchкоманду типа: watch -n1 60 echo "wiiiii". Туннель не умрет, если сеть не сломана или вы не используете ее.
erm3nda
1
Связанный: unix.stackexchange.com/q/200239
sampablokuper

Ответы:

281

Похоже, вам нужен Autossh . Это будет контролировать ssh-туннель и перезапускать его при необходимости. Мы использовали его в течение нескольких лет, и это, кажется, работает хорошо.

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

Подробнее о параметре -M здесь

KeithB
источник
2
+1 за автосш, он делает то, что говорит на жести. Я полагаю, что часть его функциональности также заключается в отправке пакетов в стиле keep-alive, чтобы предотвратить любой тайм-аут.
akent
30
Не могли бы вы привести пример использования туннеля autosshв ответе?
Эхтеш Чоудхури
5
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 username@myoutsidebox.com Вы можете заметить, что я настроил это, используя -nNT, который не создает удаленный терминал, чтобы я мог поместить autossh в фоновый режим, и опцию -i для SSH, чтобы использовать файл .pem. Если вы собираетесь постоянно держать соединение открытым, я определенно рекомендую пройти дополнительную настройку.
Жакель
2
Что бы это ни стоило, похоже, что обычно лучше не указывать этот -Mпараметр: bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162
rinogo
2
Я сделал это, чтобы повторить попытку при изменении сети, у меня это хорошо работает: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 server@example.com
Люк Стэнли,
39

Все брандмауэры с сохранением состояния забывают о соединении после того, как некоторое время не видели пакета для этого соединения (чтобы таблицы состояний не заполнялись соединениями, где оба конца погибли, не закрывая соединение). Большинство реализаций TCP отправят пакет keepalive через долгое время, не услышав об этом с другой стороны (обычно 2 часа). Однако, если существует межсетевой экран с отслеживанием состояния, который забывает о соединении до того, как пакеты поддержки активности могут быть отправлены, долгоживущее, но простое соединение прекратит работу.

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

CesarB
источник
Интервал указывается в секундах, поэтому вы можете выполнить точную настройку. Если ваш брандмауэр с состоянием имеет время ожидания 5 минут, то для поддержания соединения достаточно 60 или 120 секунд. Это один из способов, с помощью которых я могу открыть сеансы SSH через домашний маршрутизатор.
Даррен Холл
Спасибо, это помогло. Но обратите внимание (из более низкого ответа здесь, superuser.com/a/146641/115515 ), что если вы укажете ServerAliveInterval, а не ServerAliveCountMax, вы можете обнаружить, что ssh намеренно отключился раньше, чем вы хотели.
metamatt
4
@metamatt, тот ответ с более низким рейтингом, на который вы ссылаетесь, имеет более низкий рейтинг по уважительной причине: ЭТО НЕПРАВИЛЬНО.
Ламбарт
24

На вашем компьютере Mac или Linux сконфигурируйте ваш ssh, поддерживайте работу сервера 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 минут.
metamatt
2
Я голосую за этот ответ, потому что спасибо за упоминание ServerAliveCountMax, и что произойдет, если вы укажете ServerAliveInterval без ServerAliveCountMax. Но, как и в предыдущих комментариях, я замечаю, что вычисление «перестанет отправлять после» неверно, и я думаю, что этот ответ будет лучше, если он просто предоставит информацию об этих опциях, а не расскажет, как применять их с помощью команд cd и echo ,
metamatt
20
Пониженное голосование, потому что не имеет смысла устанавливать ServerAliveCountMax в «большое число». ServerAliveCountMax указывает, сколько раз он будет пытаться отправить сообщение "keepalive", прежде чем отказаться. По умолчанию установлено значение 3, поэтому при использовании ServerAliveInterval 180 отправка будет прекращена ТОЛЬКО, если сервер НЕ ОТВЕТИЛСЯ через 9 минут, и в этом случае ваше соединение, вероятно, исправно и действительно не работает.
Ламбарт
22

Я использовал следующий скрипт Bash, чтобы продолжать порождать новые ssh-туннели, когда предыдущий умирает. Использование скрипта удобно, когда вы не хотите или не можете установить дополнительные пакеты или использовать компилятор.

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

Обратите внимание, что для этого требуется ключевой файл для автоматического установления соединения, но это также относится и к autossh.

Jawa
источник
2
Вы должны добавить любые причины, по которым вы будете использовать этот скрипт вместо autossh, или это просто так?
Кириас
4
Это не поможет, если сам ssh зависнет, не так ли?
Nafg
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, и в этом случае необходимо будет создать собственную политику для предоставления необходимых исключений.

IanB
источник
2
Это выглядит очень похоже на мою суть: gist.github.com/guettli/… Обратная связь приветствуется!
Геттли
Отлично подходит для systemdсистемы. Если использовать его, Restart=on-failureто ручное уничтожение SSH-клиента не приведет к перезапуску systemd в качестве 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 каждый раз, когда оно exits (то, как вы это делаете, очень зависит от платформы, сценарий «while true», предложенный Jawa, является одним из способов, на OS XI фактически настраивает элемент launchd).

user2793784
источник
9

Всегда используйте ServerAliveIntervalопцию SSH в случае, если проблемы с туннелем генерируются сеансами NAT с истекшим сроком действия.

Всегда используйте метод возрождения в случае, если подключение полностью отключается, у вас есть по крайней мере три варианта:

  • программа autossh
  • bash script ( 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
    

или всегда используйте оба метода.

claudiuf
источник
1
+1 для встроенного параметра, если вы не хотите использовать его для всех ваших SSH-соединений
user1146334
Вы пишете "на случай, если связь полностью отключается". Теперь я не понимаю, какие проблемы решает сам autossh, а что нет? Я думал, конечно, что он позаботится о любом нарушенном соединении, например, отключит кабель на несколько часов, но, возможно, нет?
Мадс Скьерн
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.
nachopro
источник
Каждые 15 секунд, кажется, довольно часто пингуют сервер.
Ламбарт
@Lambart, но если соединение действительно ненадежное и часто прерывает соединение, оно, по крайней мере, обнаруживает мертвое соединение и дает возможность повторить попытку раньше.
Бинки
4

ExitOnForwardFailure yesявляется хорошим дополнением к другим предложениям. Если он подключается, но не может установить переадресацию порта, он так же бесполезен для вас, как если бы он вообще не подключался.

jcomeau_ictx
источник
Это очень хорошая идея. Даже autossh бесполезен, если предыдущее соединение воспринимается как тайм-аут раньше на удаленной стороне, чем на локальном хосте, поскольку в этом случае локальный хост попытается подключиться снова, но переадресация не может быть установлена, поскольку порт все еще открыт.
Рауль Салинас-Монтеагудо
1

Мне нужно было поддерживать SSH-туннель в долгосрочной перспективе. Мое решение запускалось с сервера Linux, и это всего лишь небольшая программа на C, которая запускает ssh с использованием аутентификации на основе ключей.

Я не уверен насчет повешения, но у меня туннели умирают из-за тайм-аутов.

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

Баумгарт
источник
1

в то время как есть такие инструменты, как autossh, которые помогают перезапустить сессию ssh ... что я считаю действительно полезным, это запустить команду 'screen'. Это позволяет вам возобновить сеансы SSH даже после отключения. Особенно полезно, если ваше соединение не так надежно, как должно быть.

... не забудьте отметить, что это «правильный» ответ, если он поможет вам k! ;-)

Koss
источник
7
... но вопрос был в том, как сохранить открытые туннели SSH, а не только в терминальной сессии. Экран это здорово, хотя!
akent
Я уже использую экран, но он не решает мою проблему: - / Спасибо за ваш ответ, хотя.
Пельтье
1

Немного взломать, но мне нравится использовать экран, чтобы сохранить это. В настоящее время у меня есть дистанционный форвард, который работает уже несколько недель.

Пример, начиная локально:

screen
ssh -R ......

Когда удаленная пересылка применена, и у вас есть оболочка на удаленном компьютере:

screen
Ctrl + a + d

Теперь у вас есть непрерывный пульт дистанционного пересылки. Хитрость заключается в том, чтобы запустить экран на обоих концах

landypro
источник
1

Недавно у меня возникла эта проблема, потому что эти решения требуют повторного ввода пароля каждый раз, если вы используете пароль для входа в систему, я использовал sshpass в цикле вместе с текстовым приглашением, чтобы избежать ввода пароля в командный файл.

Я подумал, что поделюсь своим решением по этой теме на случай, если у кого-то возникнет та же проблема:

#!/bin/bash
read -s -p "Password: " pass
while true
do
    sshpass -p "$pass" ssh user@address -p port
    sleep 1
done
Brainfloat
источник
0

У меня были похожие проблемы с моим предыдущим провайдером. Для меня это было то же самое с любым TCP-соединением, посещением веб-сайтов или отправкой почты.

Решением было настроить VPN-соединение по UDP (я использовал OpenVPN). Эта связь была более терпимой к тому, что вызвало разъединения. Затем вы можете запустить любой сервис через это соединение.

Все еще могут быть проблемы с соединением, но, так как туннель будет более терпимым, любой сеанс ssh будет ощущать кратковременную задержку, а не отключение.

Для этого вам потребуется онлайн-сервис VPN, который вы можете настроить на своем собственном сервере.

hultqvist
источник
0

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

По умолчанию он создает обратный туннель для порта sshd (22) NODE на сервере. Если вам нужно выполнить какие-либо другие действия (например, переадресация дополнительных портов, отправка писем по соединению и т. Д.), Вы можете разместить свои скрипты on-connectи on-disconnectпапки.

ceremcem
источник