SSH отключает переадресацию портов удаленно, вместо того, чтобы убивать процесс на сервере
Я делаю автоматический запрос удаленного SSH-порта с моего устройства каждые несколько минут:
ssh -y -f -N -R port:localhost:22 user@server \
-o ExitOnForwardFailure=yes -o ServerAliveInterval=60 \
-o StrictHostKeyChecking=no
Если переадресация портов прошла успешно, и я перезагружаю свое устройство и сбрасываю подключение к Интернету, я не могу подключиться к нему или выполнить переадресацию нового порта (это нормально и нормально, так как мой IP-адрес изменен и порт, кажется, занят). Мне нужно подождать около 1-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
в хронтаб). Он пытается убить сохраненный 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
Заметки:
- Это быстрый и грязный сценарий, подтверждение концепции; мобильность не была моим приоритетом.
- Для ясности я опустил некоторые опции, которые вы изначально использовали.
- Если вы запустите скрипт дважды, два экземпляра будут убивать туннели друг друга снова и снова.
Переадресация может быть отменена с помощью 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
или такой...