ТЛ; др
Используйте эти опции 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
Заметки:
- Это быстрый и грязный сценарий, подтверждение концепции; мобильность не была моим приоритетом.
- Для ясности я опустил некоторые опции, которые вы изначально использовали.
- Если вы запустите скрипт дважды, два экземпляра будут убивать туннели друг друга снова и снова.