Como manter confiável um túnel SSH aberto?


234

Eu uso um túnel SSH do trabalho para percorrer vários firewalls idóticos (tudo bem com meu chefe :)). O problema é que, depois de um tempo, a conexão ssh geralmente trava e o túnel está quebrado.

Se pelo menos eu pudesse monitorar o túnel automaticamente, poderia reiniciar o túnel quando ele travar, mas nem imaginei uma maneira de fazer isso.

Pontos de bônus para quem pode me dizer como impedir que minha conexão ssh seja interrompida, é claro!


Seu túnel está morto por inatividade? Eu tive esse problema quando tunelamento portas do meu telefone para que eu finalmente acabou gerando comandos fictícios na conexão para torná-lo "vivo" usando o watchcomando como: watch -n1 60 echo "wiiiii". O túnel não morre a menos que a rede esteja quebrada ou você não a use.
Erm3nda

Respostas:


280

Parece que você precisa de autossh . Isso irá monitorar um túnel ssh e reiniciá-lo conforme necessário. Nós o usamos há alguns anos e parece funcionar bem.

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C

Mais detalhes sobre o parâmetro -M aqui


2
+1 para autossh, faz o que diz na lata. Acredito que parte de sua funcionalidade também é enviar pacotes de estilo keep-alive para evitar qualquer tipo de tempo limite.
akent 8/09/09

30
Você poderia colocar o túnel de exemplo usando autosshna resposta?
Ehtesh Choudhury

5
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 username@myoutsidebox.com Você pode perceber que eu configurei isso usando -nNT, que não cria um terminal remoto, para que eu possa colocar autossh em segundo plano e a opção -i para o SSH usar um arquivo .pem. Se você vai manter uma conexão aberta o tempo todo, eu definitivamente recomendo fazer a configuração extra.
juckele

2
Pelo que vale, parece que normalmente é melhor omitir o -Mparâmetro: bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162
rinogo

2
Eu fiz isso para torná-lo repetir em caso de mudança de rede, ele funciona bem para mim: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 server@example.com
Luke Stanley

39

Todos os firewalls com monitoração de estado esquecem uma conexão depois de não ver um pacote para essa conexão por algum tempo (para impedir que as tabelas de estado fiquem cheias de conexões nas quais ambas as extremidades morrem sem fechar a conexão). A maioria das implementações de TCP enviará um pacote keepalive após um longo tempo sem ouvir do outro lado (duas horas é um valor comum). Se, no entanto, houver um firewall stateful que se esqueça da conexão antes que os pacotes keepalive possam ser enviados, uma conexão de longa duração, porém inativa, morrerá.

Se for esse o caso, a solução é impedir que a conexão fique inativa. O OpenSSH possui uma opção chamada ServerAliveInterval que pode ser usada para impedir que a conexão fique ociosa por muito tempo (como bônus, ele detectará quando o par morreu mais cedo, mesmo que a conexão esteja ociosa).


O intervalo especificado é em segundos, para que você possa fornecer algum ajuste fino. Se o seu firewall com estado tiver um tempo limite inativo de 5 minutos, 60 ou 120 segundos serão suficientes para manter a conexão aberta. É uma das maneiras pelas quais eu mantenho minhas sessões ssh através do meu roteador doméstico abertas.
Darren Hall

Obrigado, isso ajudou. Mas observe (de uma resposta de classificação inferior aqui, superuser.com/a/146641/115515 ) que, se você especificar ServerAliveInterval e não ServerAliveCountMax, poderá encontrar ssh desconectando intencionalmente mais cedo do que queria.
precisa saber é

4
@ metaeta, essa resposta de classificação inferior que você menciona é de classificação inferior por um bom motivo: ESTÁ ERRADO.
21814

24

Em sua própria máquina mac ou linux, configure seu ssh para manter o servidor ssh ativo a cada 3 minutos. Abra um terminal e acesse seu .ssh invisível em sua casa:

cd ~/.ssh/ 

crie um arquivo de configuração de 1 linha com:

echo "ServerAliveInterval 180" >> config

você também deve adicionar:

ServerAliveCountMax xxxx (high number)

o padrão é 3, para que ServerAliveInterval 180 pare de enviar após 9 minutos (3 do intervalo de 3 minutos especificado por ServerAliveInterval).


2
Observe que seu comando não é recomendado se você já possui um arquivo de configuração. Usar >> para redirecionar seria muito melhor!
Peltier

por que ServerAliveInterval 180nos dá 6 minutos? intuição me faz tente o seguinte: 180/60 == 3. Então, ServerAliveIntervalfunciona em múltiplos de 30 segundos?
Nemesisfixx

@mcnemesis: ServerAliveInterval 180 significa 3 minutos. ServerAliveCountMax padrão de 3 significa 3 desses intervalos, portanto, 9 minutos.
metamatt

2
Estou votando nesta resposta porque obrigado por mencionar ServerAliveCountMax e o que acontece se você especificar ServerAliveInterval sem ServerAliveCountMax. Mas, como nos comentários anteriores, noto que o cálculo em "irá parar de enviar depois" está errado, e acho que essa resposta seria melhor se apenas fornecesse as informações sobre essas opções, sem nos dizer como aplicá-las com os comandos cd e echo .
metamatt

20
Voto negativo, porque não faz sentido definir ServerAliveCountMax como um "número alto". ServerAliveCountMax especifica quantas vezes ele tentará enviar a mensagem "keepalive" antes de desistir. O padrão é 3, portanto, com ServerAliveInterval 180, ele interromperá o envio SOMENTE se o servidor NÃO RESPONDER após 9 minutos; nesse caso, sua conexão provavelmente estará bem e verdadeiramente morta.
Lambart

22

Usei o seguinte script do Bash para gerar novos túneis ssh quando o anterior morre. Usar um script é útil quando você não deseja ou não pode instalar pacotes adicionais ou usar o compilador.

while true
do
  ssh <ssh_options> [user@]hostname
  sleep 15
done

Observe que isso requer um arquivo de chave para estabelecer a conexão automaticamente, mas esse também é o caso do autossh.


2
Você deve adicionar algum motivo para usar esse script em autossh ou será que é mais fácil assim?
Kyrias 20/10/12

4
Isso não ajudaria se o próprio ssh congelar, ajudaria?
Nafg

1
Ajuda se você não conseguir instalar coisas no servidor. O autossh não vem pré-instalado e a burocracia às vezes é muito obtusa.
quarkex

Sim, preferível não ter que instalar as coisas. Faço isso dessa maneira há um ano como minha única maneira de manter uma máquina remota acessível (até mesmo configure o crontab para executá-lo na reinicialização). Nunca falhou e, mais importante, sei por que nunca falhará.
Sudo #

16

O Systemd é ideal para isso.

Crie um arquivo de serviço /etc/systemd/system/sshtunnel.servicecontendo:

[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Restart=always
RestartSec=20
User=sshtunnel
ExecStart=/bin/ssh -NT -o ServerAliveInterval=60 -L 5900:localhost:5900 user@otherserver

[Install]
WantedBy=multi-user.target

(Modifique o comando ssh para se adequar)

  • isso será executado como usuário, sshtunnelportanto, verifique se ele existe primeiro
  • problema systemctl enable sshtunnelpara configurá-lo para iniciar no momento da inicialização
  • problema systemctl start sshtunnelpara iniciar imediatamente

Atualização em janeiro de 2018 : algumas distros (por exemplo, Fedora 27) podem usar a política do SELinux para impedir o uso do SSH do systemd init; nesse caso, uma política personalizada precisará ser criada para fornecer as isenções necessárias.


2
Isso é muito parecido com o meu gist: gist.github.com/guettli/… Feedback é bem-vindo!
guettli

Excelente para um systemdsistema. Se alguém usar Restart=on-failure, a eliminação manual do cliente SSH não resultará em uma reinicialização por sistema como o cliente SSH com saída com êxito.
David Tonhofer 22/03/19

Se você quer começar ssh a partir de um roteiro (bash) dado como argumento para ExecStart, por exemplo, para construir a sshlista de argumentos, fazer verificações básicas etc, em seguida, chamá-lo a partir do script assim exec /bin/ssh -N .... Aqui está o meu comando: exec /bin/ssh -N -oExitOnForwardFailure=Yes -oTCPKeepAlive=no -oServerAliveInterval=5 -oServerAliveCountMax=6 -i "${LOCAL_PRIVATE_KEY}" -L "${TUNNEL_INLET}:${TUNNEL_OUTLET}" "${REMOTE_USER}@${REMOTE_MACHINE}"onde TUNNEL_INLET="127.0.0.1:3307"eTUNNEL_OUTLET="127.0.0.1:3306"
David Tonhofer 22/03

10

Parece-me que todos vocês estão interpretando mal o ServerAliveCountMax. Pelo que entendi os documentos, é o número de mensagens ativas do servidor que podem ficar sem resposta sem que a conexão seja encerrada. Portanto, em casos como o que estamos discutindo aqui, defini-lo como um valor alto garantirá que uma conexão travada não seja detectada e encerrada!

Simplesmente configurar ServerAliveInterval deve ser suficiente para resolver o problema com um firewall esquecendo a conexão, e deixar ServerAliveCountMax baixo permitirá que o final de origem observe a falha e termine se a conexão falhar de qualquer maneira.

O que você deseja é: 1) que a conexão permaneça aberta permanentemente em circunstâncias normais, 2) que a falha na conexão seja detectada e o lado de origem saia na falha e 3) que o comando ssh seja reemitido toda vez saídas (como você faz isso depende muito da plataforma, o script "while true" sugerido por Jawa é uma maneira, no OS XI realmente configuramos um item launchd).


9

Sempre use a ServerAliveIntervalopção SSH caso os problemas do túnel sejam gerados por sessões NAT expiradas.

Sempre use um método de reaparecimento no caso de a conectividade cair completamente, você tem pelo menos três opções aqui:

  • programa de autossh
  • bash script ( while true do ssh ...; sleep 5; done) não remove o comando sleep, sshpode falhar rapidamente e você reaparecerá muitos processos
  • /etc/inittab, para ter acesso a uma caixa enviada e instalada em outro país, atrás do NAT, sem encaminhamento de porta para a caixa, você pode configurá-lo para criar um túnel ssh para você:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • script inicial no Ubuntu, onde /etc/inittabnão está disponível:

    start on net-device-up IFACE=eth0
    stop on runlevel [01S6]
    respawn
    respawn limit 180 900
    exec ssh -i /path/to/rsaKey -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip
    post-stop script
        sleep 5
    end script
    

ou sempre use os dois métodos.


1
+1 para a opção in-line no caso de você não quer que ele para todas as suas conexões SSH
user1146334

Você escreve "no caso de a conectividade cair completamente". Agora eu não entendo, que problemas o autossh soluciona e o que não soluciona? Eu pensei, é claro, que trataria de qualquer conexão interrompida, como desconectar o cabo por algumas horas, mas talvez não?
Mads Skjern

6

Eu resolvi esse problema com isso:

Editar

~/.ssh/config

E adicione

ServerAliveInterval 15
ServerAliveCountMax 4

De acordo com a página de manual do ssh_config:

ServerAliveCountMax
         Sets the number of server alive messages (see below) which may be
         sent without ssh(1) receiving any messages back from the server.
         If this threshold is reached while server alive messages are
         being sent, ssh will disconnect from the server, terminating the
         session.  It is important to note that the use of server alive
         messages is very different from TCPKeepAlive (below).  The server
         alive messages are sent through the encrypted channel and there‐
         fore will not be spoofable.  The TCP keepalive option enabled by
         TCPKeepAlive is spoofable.  The server alive mechanism is valu‐
         able when the client or server depend on knowing when a connec‐
         tion has become inactive.

         The default value is 3.  If, for example, ServerAliveInterval
         (see below) is set to 15 and ServerAliveCountMax is left at the
         default, if the server becomes unresponsive, ssh will disconnect
         after approximately 45 seconds.  This option applies to protocol
         version 2 only.

 ServerAliveInterval
         Sets a timeout interval in seconds after which if no data has
         been received from the server, ssh(1) will send a message through
         the encrypted channel to request a response from the server.  The
         default is 0, indicating that these messages will not be sent to
         the server.  This option applies to protocol version 2 only.

A cada 15 segundos, parece bastante comum executar ping no servidor.
Lambart

@Lambart, mas se a conexão é realmente esquisita e descarta conexões com frequência, ela detecta pelo menos uma conexão inoperante e oferece a oportunidade de tentar novamente mais cedo.
binki

4

ExitOnForwardFailure yesé um bom complemento para as outras sugestões. Se ele se conectar, mas não conseguir estabelecer o encaminhamento de porta, é tão inútil para você quanto se não tivesse se conectado.


Esta é uma ideia muito boa. Até o autossh é inútil se a conexão anterior for percebida como esgotada antes no lado remoto do que no host local, pois nesse caso o host local tentará se conectar novamente, mas o encaminhamento não poderá ser estabelecido porque a porta ainda está aberta.
Raúl Salinas-Monteagudo

1

Eu tive uma necessidade de manter um túnel SSH a longo prazo. Minha solução estava sendo executada em um servidor Linux e é apenas um pequeno programa C que reaparece o ssh usando autenticação baseada em chave.

Não tenho certeza do enforcamento, mas tive túneis morrendo devido a tempos limite.

Gostaria de fornecer o código para o respawner, mas não consigo encontrá-lo agora.


1

Embora existam ferramentas como o autossh que ajudam a reiniciar a sessão ssh ... o que eu acho realmente útil é executar o comando 'screen'. Ele permite que você CONTINUE suas sessões ssh mesmo depois de desconectar. Especialmente útil se sua conexão não for tão confiável quanto deveria.

... não se esqueça de marcar esta é a resposta 'correta' se ela o ajudar! ;-)


7
... mas a pergunta era sobre como manter os túneis SSH abertos, não apenas uma sessão de terminal. Tela é ótimo!
akent 8/09/09

Eu já uso a tela, mas isso não resolve o meu problema: - / Obrigado pela sua resposta, no entanto.
9609 Peltier

1

Um pouco de hack, mas eu gosto de usar a tela para manter isso. Atualmente, tenho um encaminhamento remoto que está em execução há semanas.

Exemplo, iniciando localmente:

screen
ssh -R ......

Quando o encaminhamento remoto é aplicado e você possui um shell no computador remoto:

screen
Ctrl + a + d

Agora você tem um encaminhamento remoto ininterrupto. O truque é executar a tela nos dois lados


1

Recentemente, eu mesmo tive esse problema, porque essas soluções exigem que você digite novamente a senha todas as vezes, se você usar um login de senha, usei o sshpass em um loop junto com um prompt de texto para evitar ter a senha no arquivo em lotes.

Pensei em compartilhar minha solução nesse tópico, caso mais alguém tenha o mesmo problema:

#!/bin/bash
read -s -p "Password: " pass
while true
do
    sshpass -p "$pass" ssh user@address -p port
    sleep 1
done

0

Eu tive problemas semelhantes com meu ISP anterior. Para mim, era o mesmo com qualquer conexão TCP, visitando sites ou enviando correio.

A solução foi configurar uma conexão VPN por UDP (eu estava usando o OpenVPN). Essa conexão era mais tolerante com o que causou as desconexões. Em seguida, você pode executar qualquer serviço através dessa conexão.

Ainda pode haver problemas com a conexão, mas, como o túnel será mais tolerante, qualquer sessão ssh sofrerá uma pequena interrupção em vez de ser desconectada.

Para fazer isso, você precisará de um serviço VPN online, que pode ser configurado em seu próprio servidor.


0

Como autosshnão atende às nossas necessidades (existe com erro se não puder se conectar ao servidor na primeira tentativa), escrevemos um aplicativo bash puro: https://github.com/aktos-io/link- com servidor

Ele cria um túnel reverso para a porta sshd do NODE (22) no servidor por padrão. Se você precisar executar outras ações (como encaminhar portas adicionais, enviar e-mails na conexão, etc ...), poderá colocar seus scripts on-connecte on-disconnectpastas.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.