Переадресация удаленного порта SSH не удалась

27

Последующие действия: похоже, что быстрые серии отключений, совпадающие с несколькими месяцами работы каждого сервера, вероятно, случайны и служат только для выявления реальной проблемы. Причина, по которой ему не удалось восстановить соединение, почти наверняка связана со значениями AliveInterval (ответ Касперда). Использование параметра ExitOnForwardFailure должно позволить истечь тайм-аут перед повторным подключением, что должно решить проблему в большинстве случаев. Предложение MadHatter (сценарий уничтожения), вероятно, является лучшим способом убедиться, что туннель может повторно подключиться, даже если все остальное терпит неудачу.

У меня есть сервер (A) за брандмауэром, который инициирует обратный туннель на нескольких портах к небольшому VPS (B) DigitalOcean, чтобы я мог подключиться к A через IP-адрес B. Туннель непрерывно работал в течение примерно 3 месяцев, но неожиданно четыре раза за последние 24 часа произошел сбой. То же самое произошло некоторое время назад с другим провайдером VPS - месяцы безупречной работы, а затем внезапные множественные быстрые сбои.

У меня есть сценарий на компьютере A, который автоматически выполняет команду туннеля ( ssh -R *:X:localhost:X address_of_Bдля каждого порта X), но когда он выполняется, он говорит Warning: remote port forwarding failed for listen port X.

Зайдя в sshd /var/log/secureна сервере, вы увидите следующие ошибки:

bind: Address already in use
error: bind: Address already in use
error: channel_setup_fwd_listener: cannot listen to port: X

Решение требует перезагрузки VPS. До этого все попытки переподключения дают сообщение «Переадресация удаленного порта» и не будут работать. Теперь дело доходит до того, что туннель длится всего около 4 часов до остановки.

На VPS ничего не изменилось, и это одноразовый однопользовательский компьютер, который служит только конечной точкой обратного туннеля. Это работает OpenSSH_5.3p1 на CentOS 6.5. Кажется, что sshd не закрывает порты на своем конце, когда соединение потеряно. Я затрудняюсь объяснить, почему или почему это внезапно произойдет сейчас после месяцев почти идеальной работы.

Чтобы уточнить, мне сначала нужно выяснить, почему sshd отказывается прослушивать порты после сбоя туннеля, что, по-видимому, вызвано тем, что sshd оставляет порты открытыми и никогда не закрывает их. Это, кажется, главная проблема. Я просто не уверен, что заставило бы его вести себя таким образом после нескольких месяцев поведения, как я ожидаю (то есть закрытие портов сразу и повторное подключение скрипта).

Джастин Мрква
источник
Какой у Вас вопрос? Как устранить ошибку привязки порта, или как узнать, почему ssh умирает, или что-то еще?
MadHatter поддерживает Монику
Мне нужно выяснить, почему sshd отказывается открывать порты на VPS (ошибка привязки). Ошибка привязки порта, кажется, является корнем проблемы, и все должно работать, если я могу решить это.
Джастин Мрква
2
Для тех, кто задерживается, вместо того, чтобы вручную создавать сценарий для поддержания открытого соединения, просто используйте вместо этого autossh, который сделает это за вас. serverfault.com/questions/598210/…
oligofren

Ответы:

28

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

Существует три способа, по которым такие несуществующие соединения могут происходить:

  • Одна из двух конечных точек была перезагружена, а другой конец соединения был полностью свободен.
  • Одна из двух конечных точек закрыла соединение, но во время, когда соединение было закрыто, произошло временное отключение соединения. Отключение длилось несколько минут после закрытия соединения, и, таким образом, другой конец не узнал о закрытом соединении.
  • Соединение все еще полностью работоспособно на обеих конечных точках соединения ssh, но кто-то поместил устройство с сохранением состояния где-то между ними, что привело к превышению времени соединения из-за простоя. Это устройство с состоянием может быть либо NAT, либо брандмауэром. Брандмауэр, о котором вы уже упоминали, является основным подозреваемым.

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

Вы должны посмотреть на ClientAliveIntervalключевое слово для sshd_configи ServerAliveIntervalинтервал для ssh_configили ~/.ssh/config.

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

Если клиент переподключится до того, как соединение будет разорвано на сервере, вы можете оказаться в ситуации, когда новое ssh-соединение работает, но не имеет переадресаций портов. Чтобы избежать этого, вам нужно использовать ExitOnForwardFailureключевое слово на стороне клиента.

kasperd
источник
Я думаю, что это может быть проблемой. В частности, мой сценарий на A попытается переподключиться к B, если процесс ssh умирает (конечно, поскольку предупреждающее сообщение не прерывает процесс ssh, оно просто зависает, когда это происходит, но это проблема для другого дня). Но если A пытается подключиться к B слишком быстро, B может ждать, пока A подключится. Мне, вероятно, нужно убедиться, что время B всегда истекло, прежде чем A снова подключится. Объединение этого с предложением MadHatter об уничтожении процессов sshd перед повторным подключением, вероятно, охватит 95% возможных случаев.
Джастин Мрква
1
И говоря о предупреждающем сообщении, не убивающем SSH, это заставило меня задуматься ... и посмотреть на страницы руководства. Оказывается, -o ExitOnForwardFailure yesэто именно то, что мне было нужно. Так что это еще одна вещь, которую мне нужно выяснить. Чтобы подумать, я собирался написать скрипт Python для разбора этих предупреждающих сообщений. Это намного проще. : D
Джастин Мрква
Извините, что забыл о ExitOnForwardFailureнаписании моего ответа. Я добавил это к ответу сейчас.
kasperd
4
Нет проблем, и это было на самом деле -o ExitOnForwardFailure=yes(обратите внимание на знак равенства). Поэтому, если кто-то сталкивается с этим, не копируйте и не вставляйте из моего предыдущего комментария, это не сработает. : P
Джастин Мрква
Итак, я наблюдаю за сервером около 10 часов, и похоже, что он работает нормально; На данный момент я предполагаю, что этот ответ правильный (я уверен на 99% на основании того, что я видел), и что серия быстрых отключений была совпадением, связанным с сетевыми проблемами, которые случайно появились через несколько месяцев после запуск каждого сервиса. Спасибо всем за вашу помощь. ;)
Джастин Мрква
4

Вы можете найти процесс, который связывает порт на этом сервере с

sudo netstat -apn|grep -w X

Скорее всего, это полусуществующее sshd, но зачем делать предположения, когда вы можете иметь данные? Это также хороший способ для сценария найти PID для отправки сигнала 9, прежде чем пытаться снова запустить туннель.

MadHatter поддерживает Монику
источник
Я помню, как проверял это на предыдущем поставщике VPS, и я подтвердил, что sshd был процессом, слушающим эти порты. В следующий раз, когда это произойдет, я проверю это здесь, но, поскольку поведение и настройка точно такие же, я не ожидаю, что они будут другими.
Джастин Мрква
Отлично, поэтому ваш скрипт, который открывает туннель заново, убивает старого туннеля, прежде чем пытаться это сделать.
MadHatter поддерживает Монику
Никогда не выполняется более одного туннельного сценария (на A) одновременно, если это то, что вы говорите. С другой стороны, если вы хотите, чтобы скрипт удаленно выполнял команду на B для уничтожения случайных процессов ... это на самом деле неплохая идея. Но одна проблема - неоднократно убивать все соединения SSH, если я пытаюсь отладить. Если сценарий на А всегда убивает В из-за сбоя, то я не могу быть постоянно выгнан из В мошенническим сценарием А. : P Я должен проверить, чтобы убедиться, что он этого не делает. Но, как я уже сказал, неплохая идея. ;)
Джастин Мрква
Я не думал, что было. Вы говорите, что на удаленном сервере запущен скрипт, который пытается вызвать туннель и терпит неудачу из-за ошибки привязки, и я предполагаю, что он запускается только тогда, когда вам это нужно (т. Е. Когда существующий туннель бесполезен) потому что ты не сказал иначе. Все, что я предлагаю, - это убить определенный процесс, который удерживает порт открытым, прежде чем он попытается открыть новый туннель.
MadHatter поддерживает Монику
Сценарий, выполняющий ssh, находится только на сервере A, сервер B - простой сервер vanilla без дополнительных сценариев. Что я, вероятно, сделаю, это напишу сценарий уничтожения для размещения на сервере B, а затем удаленно вызову его из A, если он не сможет подключиться определенное количество раз подряд. Таким образом, это менее вероятно, чтобы вмешаться в другие соединения SSH. И я, вероятно, буду вести журнал сценария уничтожения каждый раз, когда он запускается и завершает работу, ничего не делая, если он вызывается слишком много раз слишком быстро. Лично кажется, что ограничение скорости любого скрипта, который убивает sshd, вероятно, разумно. : P
Джастин Мрква
3

Для меня, когда sshтуннель отключается, требуется некоторое время для сброса соединения, поэтому sshпроцесс продолжает блокироваться, оставляя меня без активных туннелей, и я не знаю почему. Обходное решение состоит в том, чтобы перевести sshв фоновый режим -fи порождать новые соединения, не дожидаясь сброса старых соединений. -o ExitOnForwardFailure=yesМожет быть использованы для Нта количества новых процессов. -o ServerAliveInterval=60Повышает надежность вашего текущего соединения.

Вы можете sshчасто повторять команду, скажем, в cronили в цикле в вашем скрипте, например, в следующем, мы запускаем sshкоманду каждые 3 минуты:

while (1)
do
    ssh -f user@hostname -Rport:host:hostport -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=60
    sleep 180
done
Стивен Куан
источник
более надежным решением было бы использование autossh
Marco
-o ExitOnForwardFailure=yesбыло то, что я искал, спасибо большое!
vadipp
1

По моему опыту ssh имеет немного утомительную привычку не выходить чисто, если «что-то» все еще работает в удаленной системе. Например, началось в фоновом режиме. Вы можете воспроизвести это:

ssh <server>
while true; do  sleep 60; done&
exit

Ваш ssh выйдет из системы, но на самом деле не закроет сеанс - пока не завершится удаленный процесс (чего не произойдет, потому что это цикл «while true»). Может случиться что-то похожее - ваш сеанс имеет «застрявший» процесс, который порождается ssh. Порт остается в использовании, и поэтому он не может быть повторно использован вашим локальным процессом.

Sobrique
источник
Полная команда SSH, которая выполняется на машине A, ssh -o ConnectTimeout=10 -o BatchMode=yes -gnN -R *:X:localhost:X root@$TUNSRV 1>>tunnel.log 2>&1 &так что SSH не выполняет ничего, кроме самого туннеля, в частности, из-за опции -N. Все, что остается открытым, выполняется на удаленном сервере B с использованием самого sshd.
Джастин Мрква