tl; dr
Use estas opções sshd
no servidor (no arquivo sshd_config
):
ClientAliveCountMax 3
ClientAliveInterval 15
História do lado do cliente
How can I remotely cancel port forwarding from the device that requested it, instead of killing the process on the server?
A resposta simples é: basta terminar o ssh
no cliente. Mesmo que você o mate, o servidor deve ser notificado de que a conexão foi finalizada (porque é o kernel que faz o trabalho ).
… se a notificação chegar ao servidor, ou seja, acho que esse é o problema. A rede em seu dispositivo de alguma forma cai antes de ssh
ser finalizado e não há como notificar o servidor de que esta conexão SSH em particular não existe mais.
Talvez você possa reformular sua configuração do lado do cliente para garantir que ssh
seja encerrado antes que qualquer coisa seja feita na conexão de rede. Eu não sei os detalhes do seu sistema do lado do cliente, então não vou dizer exatamente o que você pode fazer e como. Exemplos fracos: systemd
units e suas dependências, se aplicável; um wrapper sobre reboot
e / ou shutdown
.
História do lado do servidor
Eu assumo que o servidor SSH é o padrão sshd
. Sua configuração padrão ( sshd_config
) especifica
TCPKeepAlive yes
Em man 5 sshd_config
:
TCPKeepAlive
Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. On the other hand, if TCP keepalives are not sent, sessions may hang indefinitely on the server, leaving ''ghost'' users and consuming server resources.
The default is
yes
(to send TCP keepalive messages), and the server will notice if the network goes down or the client host crashes. This avoids infinitely hanging sessions.
Esse mecanismo não é específico do SSH. Explicação:
In Linux, TCP keepalive parameters are:
tcp_keepalive_intvl
tcp_keepalive_probes
tcp_keepalive_time
Their default values are:
tcp_keepalive_time = 7200
(seconds)tcp_keepalive_intvl = 75
(seconds)tcp_keepalive_probes = 9
(number of probes)This means that the keepalive process waits for two hours (7200 secs) for socket activity before sending the first keepalive probe, and then resend it every 75 seconds. If no
ACK
response is received for nine consecutive times, the connection is marked as broken.
( Fonte ).
Isso explica por que você "precisa esperar cerca de 1-2 horas para que a porta fique livre novamente".
Exemplos de como você pode alterar esses valores (se você tiver as permissões):
-
temporariamente
echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time
ou
sysctl -w net.ipv4.tcp_keepalive_time=300
-
permanentemente editando o arquivo
/etc/sysctl.conf
, adicione:net.ipv4.tcp_keepalive_time=300
, em seguida, invoque
sudo sysctl -p
para aplicar a alteração.
Mas estas são configurações de todo o sistema. Em geral, qualquer alteração afetará mais de sshd
. É por isso que é melhor usar uma solução específica de SSH. Mais uma vez em man 5 sshd_config
:
ClientAliveCountMax
Sets the number of client alive messages (see below) which may be sent without
sshd(8)
receiving any messages back from the client. If this threshold is reached while client alive messages are being sent,sshd
will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different fromTCPKeepAlive
. […] The client alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive.The default value is
3
. IfClientAliveInterval
(see below) is set to15
, andClientAliveCountMax
is left at the default, unresponsive SSH clients will be disconnected after approximately 45 seconds. This option applies to protocol version 2 only.
ClientAliveInterval
Sets a timeout interval in seconds after which if no data has been received from the client,
sshd(8)
will send a message through the encrypted channel to request a response from the client. The default is0
, indicating that these messages will not be sent to the client. This option applies to protocol version 2 only.
Se apenas você puder reconfigurar sshd
no servidor, esta é, na minha opinião, a maneira mais elegante. Deixe o sshd_config
conter linhas como:
ClientAliveCountMax 3
ClientAliveInterval 15
Notas:
-
-o ServerAliveInterval=60
que você usa é uma opção semelhante parassh
; Ele permite que o cliente detecte uma conexão quebrada. Isso não afeta o servidor. - você pode considerar
autossh
no cliente.
Voltar para o cliente
Suponhamos que você não possa reconfigurar o servidor nem o cliente. Eu sei que você disse
instead of killing the process on the server
mas neste caso, matá-lo pode ser a melhor opção. Como sshd
forks para atender conexões específicas, basta matar o processo correto sem afetar o resto. Para iniciar a eliminação do cliente, você deve modificar a maneira como invoca ssh
. Você disse:
every few minutes:
ssh -f …
Em vez disso, você pode executar um script semelhante ao seguinte, apenas uma vez (por exemplo, por meio de @reboot
no crontab). Ele tenta matar o PID salvo (de sshd
, no servidor) primeiro, em seguida, estabelecer um encapsulamento e salvar seu sshd
PID. Se o túnel não puder ser estabelecido ou for terminado eventualmente, o script durará um pouco e será repetido.
#!/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
Notas:
- Este é um script rápido e sujo, uma prova de conceito; portabilidade não era minha prioridade.
- Para maior clareza, omiti algumas opções que você usou originalmente.
- Se você executar o script duas vezes, as duas instâncias irão matar os túneis uma da outra e outra vez.