1

Я делаю автоматический удаленный запрос переадресации порта SSH с моего устройства каждые несколько минут:

ssh -y -f -N -R port:localhost:22 user@server \
-o ExitOnForwardFailure=yes -o ServerAliveInterval=60 \
-o StrictHostKeyChecking=no

Если переадресация портов прошла успешно, и я перезагружаю свое устройство и сбрасываю интернет-соединение, я не могу подключиться к нему или выполнить переадресацию нового порта (это нормально и нормально, так как мой IP-адрес изменен, и порт кажется занятым). Мне нужно подождать 1-2 часа, чтобы порт снова был свободен, или мне нужно убить процесс на моем сервере.

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

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

2 ответа2

2

ТЛ; др

Используйте эти опции sshd на сервере (в файле sshd_config ):

ClientAliveCountMax 3
ClientAliveInterval 15

Клиентская история

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

Ответ прост: просто прервите ssh на клиенте. Даже если вы принудительно уничтожите его, сервер должен быть уведомлен о том, что соединение разорвано (потому что ядро выполняет эту работу).

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

Может быть, вы можете изменить дизайн на стороне клиента, чтобы ssh завершал работу до того, как что-либо будет сделано с сетевым подключением. Я не знаю деталей вашей клиентской системы, поэтому я не скажу вам точно, что вы можете сделать и как. Неполные примеры: systemd модули и их зависимости, если применимо; обертка после reboot и / или shutdown .


Серверная история

Я предполагаю, что сервер SSH является стандартным sshd . Конфигурация по умолчанию (sshd_config) указывает

TCPKeepAlive yes

От man 5 sshd_config:

TCPKeepAlive

Указывает, должна ли система отправлять сообщения поддержки активности TCP другой стороне. Если они отправлены, смерть соединения или сбой одной из машин будут замечены должным образом. Однако это означает, что соединения прекратят свое существование, если маршрут временно не работает, и некоторые люди считают это раздражающим. С другой стороны, если сообщения поддержки активности TCP не отправляются, сеансы могут зависать на сервере неограниченное время, оставляя «призрачных» пользователей и потребляя ресурсы сервера.

По умолчанию установлено значение yes (для отправки сообщений поддержки активности TCP), и сервер заметит, если сеть выйдет из строя или произойдет сбой хоста клиента. Это позволяет избежать бесконечно зависающих сессий.

Этот механизм не специфичен для SSH. Объяснение:

В Linux параметры поддержки активности TCP:

tcp_keepalive_intvl
tcp_keepalive_probes
tcp_keepalive_time

Их значения по умолчанию:

tcp_keepalive_time = 7200 (секунд)
tcp_keepalive_intvl = 75 (секунд)
tcp_keepalive_probes = 9 (количество проб)

Это означает, что процесс keepalive ожидает в течение двух часов (7200 секунд) активности сокета, прежде чем отправлять первый тест keepalive, а затем отправлять его каждые 75 секунд. Если ответ ACK не получен в течение девяти последовательных раз, соединение помечается как разорванное.

(Источник).

Это объясняет, почему вам «нужно подождать около 1-2 часов, чтобы порт снова был свободен».

Примеры того, как вы можете изменить эти значения (если у вас есть разрешения):

  • временно

    echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time
    

    или же

    sysctl -w net.ipv4.tcp_keepalive_time=300
    
  • навсегда отредактировав /etc/sysctl.conf , добавьте:

    net.ipv4.tcp_keepalive_time=300
    

    затем вызовите sudo sysctl -p чтобы применить изменения.

Но это общесистемные настройки. В целом, любое изменение повлияет больше, чем на sshd . Вот почему лучше использовать SSH-специфичное решение. Снова из man 5 sshd_config:

ClientAliveCountMax

Устанавливает количество живых сообщений клиента (см. Ниже), которые могут быть отправлены без sshd(8) получения каких-либо сообщений от клиента. Если этот порог достигнут во время отправки клиентских живых сообщений, sshd отключит клиента, завершив сеанс. Важно отметить, что использование клиентских живых сообщений сильно отличается от TCPKeepAlive . […] Механизм работы клиента полезен, когда клиент или сервер зависят от того, когда соединение стало неактивным.

Значением по умолчанию является 3 . Если для ClientAliveInterval (см. Ниже) установлено значение 15 , а значение ClientAliveCountMax оставлено по умолчанию, неотвечающие клиенты SSH будут отключены примерно через 45 секунд. Эта опция применяется только к версии протокола 2.

ClientAliveInterval

Устанавливает интервал ожидания в секундах, после которого, если от клиента не было получено никаких данных, sshd(8) отправит сообщение через зашифрованный канал для запроса ответа от клиента. По умолчанию 0 , что означает, что эти сообщения не будут отправлены клиенту. Эта опция применяется только к версии протокола 2.

Если только вы можете перенастроить sshd на сервере, это, на мой взгляд, самый элегантный способ. Пусть sshd_config содержит такие строки:

ClientAliveCountMax 3
ClientAliveInterval 15

Заметки:

  • -o ServerAliveInterval=60 вы используете, является аналогичной опцией для ssh ; это позволяет клиенту обнаруживать разрыв соединения. Это не влияет на сервер, хотя.
  • Вы можете рассмотреть autossh на клиенте.

Вернуться к клиенту

Предположим, вы не можете перенастроить ни сервер, ни клиент. Я знаю, что ты сказал

вместо того, чтобы убивать процесс на сервере

но в этом случае убийство может быть лучшим вариантом. Поскольку sshd разветвляется для обслуживания определенных соединений, достаточно просто завершить правильный процесс, не затрагивая остальные. Чтобы инициировать убийство с клиента, вы должны изменить способ вызова ssh . Вы сказали:

каждые несколько минут:

ssh -f …

Вместо этого вы можете запустить скрипт, похожий на следующий, только один раз (например, через @reboot в crontab). Сначала он пытается убить сохраненный PID (из sshd на сервере), затем установить туннель и сохранить свой sshd PID. Если туннель не может быть установлен или в конце концов завершен, сценарий некоторое время спит и зацикливается.

#!/bin/sh
port=12345
address=user@server
while :; do
  ssh $address '[ -f ~/.tunnel.pid ] && kill `cat ~/.tunnel.pid` && rm ~/.tunnel.pid'
  ssh -o ExitOnForwardFailure=yes -R ${port}:localhost:22 $address 'echo $PPID > ~/.tunnel.pid; exec sleep infinity'
  sleep 60
done

Заметки:

  • Это быстрый и грязный сценарий, подтверждение концепции; мобильность не была моим приоритетом.
  • Для ясности я опустил некоторые опции, которые вы изначально использовали.
  • Если вы запустите скрипт дважды, два экземпляра будут убивать туннели друг друга снова и снова.
0

Переадресация может быть отменена с помощью escape-символа ssh (по умолчанию ~ в начале строки) или ~? за помощью

Supported escape sequences:
 ~.   - terminate connection (and any multiplexed sessions)
 ~B   - send a BREAK to the remote system
 ~C   - open a command line
 ~R   - request rekey
 ~V/v - decrease/increase verbosity (LogLevel)
 ~^Z  - suspend ssh
 ~#   - list forwarded connections
 ~&   - background ssh (when waiting for connections to terminate)
 ~?   - this message
 ~~   - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

и ~# выведет список соединений, но не совсем так, чтобы их убить командой

The following connections are open:
  #3 client-session (t4 r0 i0/0 o0/0 fd 8/9 cc -1)

который можно найти через ~C а затем help

ssh> help
Commands:
      -L[bind_address:]port:host:hostport    Request local forward
      -R[bind_address:]port:host:hostport    Request remote forward
      -D[bind_address:]port                  Request dynamic forward
      -KL[bind_address:]port                 Cancel local forward
      -KR[bind_address:]port                 Cancel remote forward
      -KD[bind_address:]port                 Cancel dynamic forward

так что теоретически ваш можно отменить через ~C-KL22 чтобы открыть командную строку и отменить LocalForward по номеру порта.

Решит ли это вашу проблему - неясно; порты обычно занимают намного меньше чем 1-2 часа, чтобы очистить TIME_WAIT или что-то подобное ...

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .