Corrigindo desconexões SSH: Keepalive e Timeouts
Por que o SSH desconecta sozinho?
Conclusão: Na maioria das vezes, um NAT/firewall no caminho ou o sshd silenciosamente mata uma conexão ociosa (sem tráfego). Se morre com
Broken pipeapós uma breve pausa, o keepalive -- pequenos pacotes periódicos -- não está ativo. ConfigurarServerAliveIntervalno cliente ouClientAliveIntervalno servidor resolve a maioria dos casos.
Se o SSH "funciona enquanto estou digitando mas morre quando me afasto", timeout ocioso é quase sempre o culpado. Uma conexão TCP sobrevive ao silêncio por um tempo, mas dispositivos NAT, firewalls stateful e balanceadores de carga no caminho removem sessões que ficam quietas por muito tempo. Após a remoção, sua próxima tecla não tem para onde ir, e a sessão morre com mensagens como estas:
client_loop: send disconnect: Broken pipe
Write failed: Broken pipe packet_write_wait: Connection to 192.0.2.10 port 22: Broken pipe
Timeout, server not responding.
Por outro lado, se cai enquanto você está trabalhando ativamente, ou no mesmo tempo decorrido exato a cada sessão, suspeite de um limite de sessão explícito no servidor ou de um link instável (coberto abaixo). O primeiro passo mais rápido é determinar se morre ocioso ou morre em uso.
Pré-requisitos
- Cliente e servidor ambos Ubuntu / Linux típico (OpenSSH)
- Edite
~/.ssh/configno cliente;/etc/ssh/sshd_configno servidor - Aplicar mudanças no servidor precisa de
sudoe um reload do sshd
Queda ociosa ou queda em uso -- como distinguir?
Conclusão: Se morre enquanto fica ocioso, é um timeout ocioso (NAT / firewall /
ClientAliveInterval). Se morre mesmo enquanto você mantém a tela atualizando (ex.: executandotop), suspeite de qualidade do link ou um limite de sessão explícito. O primeiro é corrigido com keepalive; o segundo precisa de uma abordagem diferente.
Para dividir a causa em duas, primeiro teste se tráfego constante mantém a conexão viva.
# Mantem a conexao ocupada com saida periodica leve $ ssh user@server 'while true; do date; sleep 30; done'
- Para de cair -> o silêncio era a causa: um timeout ocioso. Keepalive resolve (próxima seção).
- Continua caindo -> suspeite de link instável (Wi-Fi / movel) ou timeout fixo no servidor (outras configurações do
sshd_config, PAM, ou limite rigido de balanceador de carga).
Manter a conexão com ssh -v e ler o log no momento da queda torna isso conclusivo.
$ ssh -v user@server
debug1: client_loop: send disconnect: Broken pipe
Se o tráfego de keepalive parou logo antes do corte, ou server not responding apareceu, indica qual lado desistiu primeiro.
Se o tempo-até-cair é quase constante toda vez, um timeout artificial é provável (conntrack do NAT, ou ClientAliveInterval x ClientAliveCountMax do servidor). Se varia, incline-se para um problema de qualidade do link.
Lado do cliente: como configurar ServerAlive?
Conclusão: Coloque
ServerAliveInterval 60no~/.ssh/configdo cliente primeiro. Mesmo sem tráfego, o ssh envia uma requisição pelo canal criptografado a cada 60 segundos, mantendo a sessão NAT viva e detectando um servidor morto. Funciona apenas do cliente, mesmo quando você não pode mexer no servidor (hosts compartilhados, etc.).
ServerAliveInterval é o número de segundos de silêncio do servidor após o qual o ssh envia uma requisição pelo canal criptografado. O padrão é 0 (desabilitado). ServerAliveCountMax (padrão 3) é quantos probes podem ficar sem resposta antes do ssh desconectar.
# ~/.ssh/config
Host *
ServerAliveInterval 60
ServerAliveCountMax 3Com isso, um pequeno pacote flui a cada 60 segundos para que a sessão NAT/firewall permaneça viva, prevenindo quedas ociosas. Ao mesmo tempo, se o servidor realmente morrer, o ssh desiste após 60s x 3 = ~180s em vez de deixar uma sessão zumbi.
Para testar ad hoc, use -o.
$ ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@server
Host * aplica a todas as conexões; limite a um bloco Host myserver para restringir. Mantenha ~/.ssh/config no modo 600 -- permissões muito frouxas podem fazer com que seja ignorado.
Definir ServerAliveInterval muito baixo significa que uma breve instabilidade esgota o ServerAliveCountMax e torna quedas mais prováveis. Em links instáveis, não reduza o intervalo agressivamente; aumente o count (ex.: Interval 30 / CountMax 6 dá ~180s de margem).
Lado do servidor: como configurar ClientAlive?
Conclusão: Se você gerencia o servidor, coloque
ClientAliveInterval 60em/etc/ssh/sshd_config. O sshd envia uma requisição pelo canal criptografado para cada cliente, então você previne quedas ociosas sem configurar cada cliente. Os mesmos parâmetros também permitem desconectar deliberadamente clientes ociosos -- a direção depende do CountMax que você combinar.
ClientAliveInterval é o espelho do ServerAliveInterval do cliente: o sshd envia um probe após N segundos de silêncio do cliente. Padrão 0 (desabilitado). ClientAliveCountMax tem padrão 3.
# /etc/ssh/sshd_config ClientAliveInterval 60 ClientAliveCountMax 3
Valide a sintaxe antes de aplicar. Quebrar a configuração do sshd aqui pode te trancar para fora, então sempre faça isso com uma segunda sessão aberta.
# Verificacao de sintaxe (captura erros antes de entrarem em vigor) $ sudo sshd -t # Recarregar a configuracao (sessoes existentes sao preservadas) $ sudo systemctl reload ssh
# sshd -t nao imprime nada quando a configuracao e valida
Note que o significado muda com a intenção:
- Manter conexões vivas:
ClientAliveInterval 60sozinho mantém o keepalive fluindo e supera a remoção do NAT. UmCountMaxmaior é aceitável. - Desconectar clientes ociosos (ex.: requisito de segurança):
ClientAliveInterval 300/ClientAliveCountMax 1derruba um cliente silencioso após cerca de 5 minutos (note queClientAliveCountMax 0em vez disso desabilita a terminação).
Um restart pode derrubar conexões existentes; prefira reload para aplicar mudanças. Execute sshd -t -> reload com uma segunda sessão SSH ainda aberta para se proteger contra bloqueio. Se o ufw bloquear até o SSH, veja Troubleshooting ufw e SSH.
Como ServerAlive difere do TCPKeepAlive?
Conclusão:
ServerAliveInterval/ClientAliveIntervaloperam sobre o canal criptografado SSH -- não podem ser falsificados e permitem ajustar o intervalo em segundos.TCPKeepAliveé keepalive na camada TCP, vinculado ao padrão do SO (tipicamente 2 horas de início ocioso) e falsificável. Para prevenção de queda ociosa, use a família ServerAlive da camada SSH.
As três configurações comumente confundidas, por camada:
| Configuração | Camada | Direção | Notas |
|---|---|---|---|
ServerAliveInterval |
SSH (criptografado) | cliente->servidor | Definido no cliente. Não falsificável |
ClientAliveInterval |
SSH (criptografado) | servidor->cliente | Definido no servidor. Não falsificável |
TCPKeepAlive |
TCP | ambas direções | Padrão yes. Intervalo depende do SO |
Mesmo com TCPKeepAlive yes (o padrão do OpenSSH), a vivacidade é verificada, mas o tempo ocioso antes do keepalive TCP iniciar segue a configuração do SO (no Linux, net.ipv4.tcp_keepalive_time, padrão 7200 segundos = 2 horas). Isso é lento demais para um NAT que corta em minutos. Para superar um timeout ocioso curto, o ServerAliveInterval com granularidade de segundos é a ferramenta certa.
# Para referencia: o inicio ocioso do keepalive TCP (segundos) $ sysctl net.ipv4.tcp_keepalive_time
net.ipv4.tcp_keepalive_time = 7200
Os manuais oficiais (man ssh_config / man sshd_config) afirmam que mensagens ServerAlive são "enviadas pelo canal criptografado e portanto não falsificáveis", enquanto TCPKeepAlive é falsificável. O keepalive da camada SSH vence tanto em segurança quanto em controle.
E se ainda cair após configurar o keepalive?
Conclusão: Se o keepalive está configurado e ainda cai, é um destes: (1) um timeout ocioso de NAT / LB menor que seu intervalo de keepalive, (2) o link em si oscilando, ou (3) outro timeout do servidor (PAM, limite rigido de balanceador de carga). A correção é tornar o intervalo de keepalive confiavelmente menor que esse valor de ociosidade.
Percorra os suspeitos habituais quando o keepalive não é suficiente.
# Procurar pistas de keepalive / disconnect / timeout no log detalhado $ ssh -v user@server 2>&1 | grep -iE 'alive|disconnect|timeout'
- NAT / firewall ocioso é curto: roteadores domésticos e LBs de nuvem podem expirar em aproximadamente 60-350 segundos. Defina
ServerAliveIntervalclaramente abaixo disso (se o LB é 60s, use30). - Link oscilando: links Wi-Fi / moveis caem fisicamente. Keepalive não salva esses -- use tmux / mosh (abaixo) para tolerar desconexões.
- Outro limite do servidor: um limite de sessão de balanceador de carga ou bastion, ou uma regra PAM, pode cortar em um tempo fixo. Verifique o motivo no servidor com
journalctl -u ssh.
# Verificar o motivo da desconexao no lado do servidor $ sudo journalctl -u ssh -n 100 --no-pager
Atrás de um balanceador de carga na nuvem (AWS NLB / GCP, etc.), o timeout ocioso do próprio LB domina (AWS NLB tem padrão de 350 segundos). Sempre defina o intervalo de keepalive abaixo desse valor.
Um intervalo de keepalive igual ou maior que o timeout ocioso é inútil. "LB é 60s, então ServerAliveInterval 60" ainda escorrega no limite. Mire em metade ou menos (30 ou abaixo neste exemplo).
O que usar para sobreviver a desconexões?
Conclusão: A correção mais profunda é tornar uma queda inofensiva. Execute
tmux/screenno servidor e sua sessão persiste mesmo quando o SSH morre -- reconecte eattachpara voltar. Em links instáveis,moshreconecta automaticamente.
Keepalive mantém você conectado; tmux / mosh evitam que uma desconexão cause dano. Combine ambos para robustez.
# Iniciar uma sessao tmux no servidor $ tmux new -s work # Apos o SSH cair, reconecte e volte para a sessao $ ssh user@server $ tmux attach -t work
Execute processos longos e comandos arriscados dentro de tmux / screen para que uma queda de link não derrube o processo junto. Se transferências grandes com scp / rsync continuam falhando no meio, use rsync retomável.
mosh (mobile shell) é baseado em UDP e recupera automaticamente entre mudanças de IP e breves interrupções -- ideal para links moveis ou instáveis. Requer a instalação do mosh no servidor e a abertura de suas portas UDP.
Resumo e checklist
Conclusão: Se morre ocioso, o primeiro passo é keepalive --
ServerAliveIntervalno cliente ouClientAliveIntervalno servidor. Mantenha o intervalo abaixo de qualquer timeout ocioso de NAT / LB, e combine com tmux / mosh para que uma queda seja inofensiva. Isso resolve quase todas as desconexões SSH inesperadas.
- [ ] Separar queda-ociosa vs queda-em-uso (silêncio vs qualidade do link)
- [ ] Definir
ServerAliveInterval/ServerAliveCountMaxno~/.ssh/configdo cliente - [ ] Se você gerencia o servidor, definir
ClientAliveIntervalem/etc/ssh/sshd_config, depoissshd -t->reload - [ ] Tornar o intervalo de keepalive confiavelmente menor que o timeout ocioso do NAT / LB
- [ ] Manter uma segunda sessão SSH aberta ao alterar configuração para se proteger contra bloqueio
- [ ] Em links instáveis, usar tmux / screen / mosh para tolerar desconexões
- [ ] Se ainda cair, verificar o motivo no servidor com
journalctl -u ssh