Diagnóstico de Conectividade SSH: Guia Prático para Sysadmins

Duarte Spínola  |  2026-07-12

O SSH é o protocolo fundamental para administração de servidores Linux, dispositivos de rede e, cada vez mais, Windows Server com OpenSSH. Quando falha, o sysadmin perde acesso remoto e tem de recorrer à consola iLO/iDRAC ou deslocar-se fisicamente ao datacenter. As anomalias mais comuns são: impossibilidade de conectar (firewall, serviço parado, porta alterada), autenticação que falha (chaves corrompidas, known_hosts em conflito, permissões incorrectas), lentidão (DNS reverso, GSSAPI, MTU), e desligamentos inesperados (timeout, fail2ban, ClientAliveInterval). O diagnóstico começa sempre pelo modo verbose do próprio cliente SSH, que mostra exactamente em que fase a conexão falha. Referência: OpenSSH — documentação oficial.

O SSH usa a porta TCP 22 por defeito. Em ambientes hardened, é comum alterar para uma porta não-standard (ex: 2222, 10022). Isto não substitui a necessidade de firewall e fail2ban, mas reduz o ruído de bots que fazem port scan ao 22.

1. Camadas de Diagnóstico SSH

Camada Sintoma Ferramenta de diagnóstico
Rede Connection timeout, host unreachable ping, tcping, nc -zv
Firewall Connection refused (porta fechada) iptables -L, ufw status, nft list ruleset
Serviço sshd Connection refused (serviço parado) systemctl status sshd, ss -tlnp
Autenticação (chave) Permission denied (publickey) ssh -v, ssh-keygen -l, permissões
Autenticação (password) Permission denied (password) ssh -v, PAM, /var/log/auth.log
known_hosts WARNING: HOST IDENTIFICATION HAS CHANGED ssh-keygen -R, verificar MITM
DNS reverso Conexão demora 10-30 segundos a estabelecer sshd_config: UseDNS, GSSAPI
MTU SSH conecta mas comandos hang a meio ping -M do -s 1472, ssh -o IPQoS
fail2ban Connection refused após várias tentativas fail2ban-client status sshd
Cliente Versão incompatível, cipher mismatch ssh -V, ssh -Q cipher

2. Passo 1 — O Modo Verbose é a Primeira Ferramenta

O cliente SSH tem três níveis de debug que mostram exactamente o que está a acontecer em cada fase da conexão. Antes de qualquer outra coisa, correr:

# Nível 1 — resumo da negociação
ssh -v utilizador@servidor

# Nível 2 — detalhe de autenticação e chaves
ssh -vv utilizador@servidor

# Nível 3 — detalhe máximo (debug de pacotes)
ssh -vvv utilizador@servidor

O output do verbose mostra a sequência de fases. Identificar onde para:

# Output típico do ssh -v:
debug1: Connecting to servidor [192.168.1.100] port 22. # 1. Rede
debug1: Connection established. # 2. TCP conectou
debug1: Local version string: OpenSSH_9.9p1 # 3. Versão do cliente
debug1: Remote protocol version 2.0, remote software # 4. Versão do servidor
debug1: SSH2_MSG_KEXINIT sent # 5. Início de negociação
debug1: SSH2_MSG_KEX_DH_GEX_REQUEST sent # 6. Key exchange
debug1: Host ‘servidor’ is known and matches ED25519 key # 7. known_hosts OK
debug1: Authenticating that user can log in. # 8. Autenticação
debug1: Next authentication method: publickey # 9. Tentar chave
debug1: Offering public key: /home/user/.ssh/id_ed25519 # 10. Chave oferecida
debug1: Server accepts key # 11. Chave aceite
debug1: Authentication succeeded (publickey). # 12. Autenticado
Onde para Camada afectada Próximo passo
Linha 1 (Connecting) Rede — não chegou ao servidor Verificar IP, rota, firewall (secção 3)
Linha 2 (Connection established) TCP OK — problema é SSH Continuar verbose
Linha 5-6 (KEXINIT/KEX) Key exchange — cipher mismatch Verificar ciphers suportados (secção 7)
Linha 7 (known_hosts) Host key changed Verificar se é legítimo (secção 5)
Linha 8-10 (Authenticating/publickey) Autenticação falha Verificar chaves e permissões (secção 4)
Linha 12 (Authentication succeeded) Autenticado mas sessão hang MTU, DNS, ou servidor (secções 6, 8)

3. Passo 2 — Conectividade de Rede

3.1 Testar TCP à porta 22

# Linux — testar conexão TCP à porta 22
nc -zv servidor 22

# Output OK:
# Connection to servidor 22 port [tcp/ssh] succeeded!

# Output erro:
# nc: connect to servidor port 22 (tcp) failed: Connection refused
# → Serviço sshd parado ou firewall a rejeitar

# Ou:
# nc: connect to servidor port 22 (tcp) failed: Connection timed out
# → Firewall a descartar pacotes (DROP) ou IP errado

# Alternativa com tcping (mede latência)
tcping servidor 22

3.2 Verificar se o serviço sshd está a correr

# No servidor — verificar estado do serviço
systemctl status sshd

# Verificar se está a escutar na porta 22
ss -tlnp | grep :22

# Output esperado:
# LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:((“sshd”,pid=1234,fd=3))
# LISTEN 0 128 [::]:22 [::]:* users:((“sshd”,pid=1234,fd=4))

# Se não está a escutar, iniciar serviço:
sudo systemctl start sshd
sudo systemctl enable sshd

# Verificar configuração do sshd (testar sintaxe)
sudo sshd -t
# Output vazio = configuração OK
# Output com erro = indicar linha do problema

3.3 Verificar firewall

# iptables
sudo iptables -L -n | grep 22
# Procure por: ACCEPT tcp dpt:22

# UFW (Ubuntu/Debian)
sudo ufw status
# Procure por: 22/tcp ALLOW Anywhere

# Se a regra não existe, adicionar:
sudo ufw allow 22/tcp
# Ou porta custom:
sudo ufw allow 2222/tcp

# nftables (Debian 12+, RHEL 9+)
sudo nft list ruleset | grep 22

# firewalld (RHEL/Fedora/Rocky)
sudo firewall-cmd –list-services
sudo firewall-cmd –add-service=ssh –permanent
sudo firewall-cmd –reload

3.4 Verificar se a porta foi alterada

# No servidor — verificar porta configurada
grep -i “^Port” /etc/ssh/sshd_config

# Output padrão:
# Port 22

# Se for diferente (ex: Port 2222), o cliente deve usar:
ssh -p 2222 utilizador@servidor

4. Passo 3 — Autenticação com Chaves

4.1 Verificar chaves no cliente

# Listar chaves disponíveis
ls -la ~/.ssh/

# Ficheiros esperados:
# id_ed25519 — chave privada (permissões 600)
# id_ed25519.pub — chave pública (permissões 644)
# id_rsa — chave RSA (legacy, preferir ed25519)
# known_hosts — chaves de servidores conhecidos

# Verificar fingerprint da chave
ssh-keygen -l -f ~/.ssh/id_ed25519.pub

# Output:
# 256 SHA256:abc123… user@cliente (ED25519)

4.2 Verificar permissões (causa #1 de falha de chave)

# Permissões críticas — se incorrectas, o SSH ignora a chave silenciosamente
chmod 700 ~/.ssh/
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/config
chmod 644 ~/.ssh/known_hosts

# No servidor — verificar permissões do authorized_keys
chmod 700 ~/.ssh/
chmod 600 ~/.ssh/authorized_keys

# Verificar dono (deve ser o próprio utilizador, não root)
chown -R $USER:$USER ~/.ssh/

O SSH é extremamente rigoroso com permissões. Se ~/.ssh/ tem permissões 777, ou se authorized_keys tem permissões 666, o sshd recusa a chave silenciosamente — não dá erro, simplesmente ignora a chave e passa para o próximo método de autenticação. O ssh -v mostra “Offering public key” mas não “Server accepts key”.

4.3 Verificar authorized_keys no servidor

# No servidor — verificar se a chave pública está no authorized_keys
cat ~/.ssh/authorized_keys

# Deve conter a chave pública do cliente:
# ssh-ed25519 AAAAC3Nz… user@cliente

# Se não está, copiar do cliente:
ssh-copy-id -i ~/.ssh/id_ed25519.pub utilizador@servidor

# Ou manualmente — no cliente:
cat ~/.ssh/id_ed25519.pub
# Copiar output, colar no servidor em ~/.ssh/authorized_keys

4.4 Debug de rejeição de chave

# Cliente — verbose mostra porque a chave é rejeitada
ssh -vvv utilizador@servidor

# Procurar estas linhas:
# debug1: Offering public key: /home/user/.ssh/id_ed25519
# debug2: we sent a publickey packet, wait for reply
# debug1: Authentications that can continue: publickey,password
# → Se “Server accepts key” não aparece, a chave não está no authorized_keys
# ou as permissões estão incorrectas

# No servidor — verificar logs de autenticação
sudo tail -f /var/log/auth.log # Debian/Ubuntu
sudo journalctl -u sshd -f # systemd
sudo tail -f /var/log/secure # RHEL/CentOS

Referência: ssh-keygen — documentação.

5. Passo 4 — Conflito known_hosts

Quando um servidor é reinstalado ou a sua chave SSH muda, o cliente recusa conectar com o erro:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!

5.1 Verificar se a mudança de chave é legítima

Antes de remover a entrada, confirmar que a mudança de chave é esperada:

  • O servidor foi reinstalado? Sim → legítimo
  • O IP foi reatribuído a outro servidor? Sim → legítimo
  • Nenhuma das anteriores → investigar (possível MITM)

5.2 Remover entrada antiga

# Remover entrada específica do known_hosts
ssh-keygen -R servidor

# Output:
# /home/user/.ssh/known_hosts updated.
# Original contents retained as /home/user/.ssh/known_hosts.old

# Reconectar — vai pedir para aceitar a nova chave
ssh utilizador@servidor
# The authenticity of host ‘servidor’ can’t be established.
# ED25519 key fingerprint is SHA256:xyz789…
# Are you sure you want to continue connecting (yes/no)? yes

Se a mudança de chave não é esperada, não aceitar a nova chave sem verificar com o administrador do servidor. Pode ser um ataque man-in-the-middle. Verificar a fingerprint do servidor através de canal seguro (consola iLO, telefone, etc.).

6. Passo 5 — Lentidão ao Estabelecer Conexão

6.1 DNS reverso (causa mais comum)

Por defeito, o sshd tenta resolver o IP do cliente para um nome (DNS reverso). Se o DNS reverso falha ou é lento, a conexão demora 10-30 segundos antes de pedir a password.

# Verificar se é DNS reverso — desactivar temporariamente no servidor
sudo sshd -t # testar config
sudo nano /etc/ssh/sshd_config

# Adicionar ou alterar:
UseDNS no

# Reiniciar sshd
sudo systemctl restart sshd

# Testar do cliente — deve conectar instantaneamente
time ssh utilizador@servidor exit

6.2 GSSAPI (segunda causa mais comum)

O GSSAPI (Kerberos) tenta autenticar via Kerberos antes de tentar chaves/password. Se não há Kerberos, timeout.

# No cliente — desactivar GSSAPI
ssh -o GSSAPIAuthentication=no utilizador@servidor

# Ou permanentemente em ~/.ssh/config:
# Host *
# GSSAPIAuthentication no

6.3 Verificar tempo de conexão

# Medir tempo total de conexão
time ssh -o ConnectTimeout=10 utilizador@servidor exit

# Decompor as fases:
ssh -v -o GSSAPIAuthentication=no -o UseDNS=no utilizador@servidor exit 2>&1 | \
awk ‘/debug1:/{print strftime(“%H:%M:%S”), $0}’

6.4 Desactivar features desnecessárias

# No cliente — ~/.ssh/config para conexões rápidas:
Host *
GSSAPIAuthentication no
GSSAPIDelegateCredentials no
UseDNS no # apenas afecta o cliente se fizer reverse lookup
PreferredAuthentications publickey
Compression yes # útil em WAN lenta
ServerAliveInterval 60
ServerAliveCountMax 3

Referência: ssh_config — documentação.

7. Passo 6 — MTU e Sessões que Hang

O SSH conecta mas comandos hang a meio da execução — especialmente em VPNs, túneis, ou ligações WAN com MTU reduzido. O TCP handshake passa (pacotes pequenos) mas comandos com output grande (ex: ls -la, cat ficheiro) hang porque os pacotes grandes são dropped.

7.1 Diagnosticar MTU

# Ping com tamanho máximo para detectar MTU problemático
ping -M do -s 1472 servidor
# 1472 = MTU 1500 – 28 bytes (IP+ICMP headers)

# Se falhar, reduzir:
ping -M do -s 1400 servidor
ping -M do -s 1300 servidor

# Quando encontra o maior que passa, usar no SSH:
ssh -o IPQoS=throughput -o [email protected] utilizador@servidor

# Ou definir MSS no cliente:
ssh -o ProxyCommand=”nc -M 1300 %h %p” utilizador@servidor

7.2 Desactivar compressão (piora em MTU baixo)

# Se compressão está activa, desactivar para diagnóstico
ssh -o Compression=no utilizador@servidor

8. Passo 7 — Desligamentos Inesperados

8.1 Timeout do servidor (ClientAliveInterval)

# No servidor — verificar timeout configurado
grep -i “ClientAlive” /etc/ssh/sshd_config

# Output comum:
# ClientAliveInterval 300 # 5 minutos
# ClientAliveCountMax 3 # 3 tentativas = 15 minutos total

# Se ClientAliveInterval é muito baixo, a sessão desliga por inactividade
# Aumentar:
sudo sed -i ‘s/^ClientAliveInterval.*/ClientAliveInterval 600/’ /etc/ssh/sshd_config
sudo systemctl restart sshd

8.2 Keepalive do cliente

# No cliente — enviar keepalive para evitar desligamento
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 utilizador@servidor

# Ou em ~/.ssh/config:
Host *
ServerAliveInterval 60
ServerAliveCountMax 3

8.3 fail2ban a bloquear

O fail2ban bloqueia IPs após várias tentativas falhadas. Se o utilizador tenta conectar com chave errada várias vezes, o IP é banido.

# Verificar se fail2ban bloqueou o IP
sudo fail2ban-client status sshd

# Output:
# Banned IP list: 192.168.1.50

# Desbloquear IP manualmente
sudo fail2ban-client set sshd unbanip 192.168.1.50

# Verificar regras de iptables criadas pelo fail2ban
sudo iptables -L f2b-sshd -n

8.4 TCP wrappers (hosts.deny)

# Verificar se o IP está bloqueado em TCP wrappers
cat /etc/hosts.deny | grep -i ssh
cat /etc/hosts.allow | grep -i ssh

# Se hosts.deny tem:
# sshd: ALL
# E hosts.allow não tem excepção para o IP, todas as conexões são recusadas

9. Passo 8 — Logs do Servidor

9.1 Ver logs de autenticação

# Debian/Ubuntu
sudo tail -f /var/log/auth.log | grep ssh

# RHEL/CentOS/Rocky
sudo tail -f /var/log/secure | grep ssh

# systemd (qualquer distro)
sudo journalctl -u sshd -f
sudo journalctl -u ssh -f

# Procurar tentativas falhadas
sudo journalctl -u sshd –since “1 hour ago” | grep -i “failed\|invalid\|refused”

9.2 Mensagens comuns nos logs

Mensagem Significado Solução
Connection from X port Y Conexão recebida Normal
Failed publickey for user from X Chave rejeitada Verificar authorized_keys e permissões
Invalid user user from X Utilizador não existe Verificar nome de utilizador
Connection closed by X Cliente desligou Normal se intencional
Connection reset by X Rede cortou Verificar firewall/VPN
error: kex_exchange_identification Banner exchange falhou Versão SSH incompatível ou rede
refused connect from X TCP wrappers bloqueou Verificar hosts.deny
max startups Demasiadas conexões simultâneas Aumentar MaxStartups em sshd_config

9.3 Aumentar logging do sshd para diagnóstico

# Temporariamente — aumentar log level para debug
sudo sed -i ‘s/^#LogLevel.*/LogLevel DEBUG3/’ /etc/ssh/sshd_config
sudo systemctl restart sshd

# Reproduzir o problema
# Analisar logs
sudo journalctl -u sshd –since “5 min ago” –no-pager

# Restaurar para INFO quando terminar
sudo sed -i ‘s/^LogLevel DEBUG3/LogLevel INFO/’ /etc/ssh/sshd_config
sudo systemctl restart sshd

Referência: sshd_config — documentação.

10. Passo 9 — SSH em Windows

10.1 OpenSSH no Windows Server

# Verificar se OpenSSH Server está instalado
Get-WindowsCapability -Online | Where-Object Name -like “OpenSSH.Server*”

# Instalar
Add-WindowsCapability -Online -Name “OpenSSH.Server~~~~0.0.1.0”

# Iniciar e activar serviço
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic

# Verificar firewall
Get-NetFirewallRule -Name *ssh*

# Se não existe regra, criar:
New-NetFirewallRule -Name sshd -DisplayName “OpenSSH Server” -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22

Referência: OpenSSH no Windows — Microsoft Learn.

10.2 Chaves em Windows

# Gerar chave em Windows
ssh-keygen -t ed25519

# As chaves ficam em:
# C:\Users\utilizador\.ssh\id_ed25519
# C:\Users\utilizador\.ssh\id_ed25519.pub

# authorized_keys em Windows Server (admin):
# C:\ProgramData\ssh\administrators_authorized_keys
# Permissões: apenas SYSTEM e Administrators

# authorized_keys em Windows Server (user normal):
# C:\Users\utilizador\.ssh\authorized_keys

Em Windows, as permissões de authorized_keys são diferente. Para administradores, o ficheiro fica em C:\ProgramData\ssh\administrators_authorized_keys e deve ter permissões apenas para SYSTEM e Administrators. Se um utilizador normal tiver permissão de escrita, o sshd ignora a chave.

11. Erros Comuns e Resolução

  • “Connection refused” — serviço sshd parado ou porta incorrecta. Verificar systemctl status sshd e ss -tlnp | grep :22.
  • “Connection timed out” — firewall a descartar pacotes (DROP) ou IP errado. Verificar iptables -L -n, ufw status.
  • “Permission denied (publickey)” — chave não está no authorized_keys, permissões incorrectas em ~/.ssh/ ou authorized_keys, ou chave privada errada. Usar ssh -vvv para ver qual chave é oferecida e se é aceite.
  • “REMOTE HOST IDENTIFICATION HAS CHANGED” — chave do servidor mudou. Confirmar se é legítimo e usar ssh-keygen -R servidor.
  • “No route to host” — IP errado, gateway em falta, ou VPN desconectada. Verificar ip route e ping gateway.
  • SSH demora 20-30 segundos a conectar — DNS reverso ou GSSAPI. Desactivar UseDNS no no servidor e GSSAPIAuthentication no no cliente.
  • SSH conecta mas comandos hang — problema de MTU. Testar com ping -M do -s 1472 e reduzir MTU se necessário.
  • “Bad permissions” (Windows) — authorized_keys com permissões incorrectas. Usar icacls para restringir a SYSTEM e Administrators.
  • Sessão desliga por inactividadeClientAliveInterval no servidor ou ServerAliveInterval no cliente. Configurar keepalive.
  • “Too many authentication failures” — cliente tem demasiadas chaves e todas são tentadas antes da correcta. Usar ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 para forçar apenas uma chave.
  • “no matching cipher found” — cliente e servidor não têm ciphers em comum. Verificar ssh -Q cipher no cliente e Ciphers em sshd_config no servidor.
  • fail2ban bloqueia repetidamente — chave SSH incorrecta ou password errada a falhar. Verificar fail2ban-client status sshd e desbloquear com fail2ban-client set sshd unbanip <IP>.

12. Checklist de Diagnóstico

  1. Modo verbosessh -vvv utilizador@servidor para identificar a fase que falha
  2. Redenc -zv servidor 22 para testar TCP à porta SSH
  3. Serviçosystemctl status sshd e ss -tlnp | grep :22 no servidor
  4. Firewalliptables -L, ufw status, ou firewall-cmd --list-services
  5. Porta — verificar Port em sshd_config se não é a padrão 22
  6. Permissõeschmod 700 ~/.ssh/, chmod 600 authorized_keys no servidor
  7. Chavessh-keygen -l -f ~/.ssh/id_ed25519.pub para verificar fingerprint
  8. authorized_keys — confirmar que a chave pública está no servidor
  9. known_hostsssh-keygen -R servidor se a chave do servidor mudou
  10. DNS reversoUseDNS no no servidor se conexão é lenta
  11. GSSAPIssh -o GSSAPIAuthentication=no se conexão é lenta
  12. MTUping -M do -s 1472 servidor se sessões hang
  13. Logsjournalctl -u sshd -f ou /var/log/auth.log para erros
  14. KeepaliveServerAliveInterval 60 no cliente para evitar desligamentos
  15. fail2banfail2ban-client status sshd se IP foi bloqueado

13. Artigos Relacionados