SSH desabilita a porta remotamente em vez de matar o processo no servidor

1

Estou fazendo uma solicitação de encaminhamento automático de porta SSH remota do meu dispositivo a cada alguns minutos:

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

Se o encaminhamento de porta for bem-sucedido e eu reiniciar meu dispositivo e redefinir a conexão com a Internet, não consigo me conectar a ele ou fazer novo encaminhamento de porta (isso é normal e ok, pois meu IP é alterado e a porta parece estar ocupada) . Eu tenho que esperar cerca de 1-2 horas para que a porta esteja livre novamente ou eu preciso matar o processo no meu servidor.

Eu gostaria de enviar um script do meu dispositivo antes de desligá-lo, o que liberaria a porta, para que, após a reinicialização, a nova solicitação de encaminhamento de porta seja bem-sucedida.

Para resumir: Como posso cancelar remotamente o encaminhamento de porta do dispositivo que o solicitou, em vez de eliminar o processo no servidor?

    
por Jacek 08.01.2018 / 15:36

2 respostas

1

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 from TCPKeepAlive. […] 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. If ClientAliveInterval (see below) is set to 15, and ClientAliveCountMax 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 is 0, 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 para ssh ; 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.
por 09.01.2018 / 01:47
0

O encaminhamento pode ser cancelado usando o caractere de escape ssh ( ~ no início de uma linha, por padrão) ou ~? para ajuda

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.)

e ~# listarão as conexões, mas não de maneira adequada para eliminá-las por meio de um comando

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

, que pode ser encontrado via ~C e, em seguida, 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

então, em teoria, o seu poderia ser cancelado via ~C-KL22 para abrir a linha de comando e cancelar o LocalForward pelo número da porta.

Se isso resolver seu problema não está claro; as portas geralmente levam menos de 1-2 horas para limpar TIME_WAIT ou algo assim ...

    
por 08.01.2018 / 18:44