← Voltar para o Blog

Hardening de SSH: os ajustes que valem e os mitos que não

Trocar a porta 22 não protege quase nada. Estes são os ajustes que realmente mudam a superfície de ataque do seu servidor, na ordem em que compensa aplicá-los.

Equipe EasyOps Cloud · · 7 min de leitura

Um servidor com SSH aberto para a internet recebe entre dois e dez mil pedidos de autenticação por dia. Não é ataque direcionado — é varredura automatizada, constante, contra toda a faixa de IP pública que existe.

A boa notícia é que a defesa é curta e conhecida. A ruim é que metade do que se recomenda por aí não muda nada de relevante, e o tempo gasto ali é tempo que não foi gasto no que importa. Vamos separar os dois grupos.

O ajuste que sozinho resolve 95% do problema

Desligar autenticação por senha. Só isso já elimina a força bruta como categoria de risco, porque não existe senha para adivinhar.

A ordem importa, e inverter aqui é o jeito mais rápido de perder acesso à máquina. Primeiro instale a sua chave e teste que ela funciona:

# Do seu notebook
ssh-keygen -t ed25519 -C "seu@email"
ssh-copy-id usuario@203.0.113.50
ssh usuario@203.0.113.50    # tem que entrar sem pedir senha

Só depois de entrar sem senha, altere o servidor:

# /etc/ssh/sshd_config.d/99-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes

O arquivo em sshd_config.d/ é preferível a editar o sshd_config direto: ele sobrevive a atualização de pacote e deixa explícito o que foi alterado. Confirme a sintaxe antes de recarregar, porque um erro aqui derruba o serviço:

sudo sshd -t && sudo systemctl reload ssh

Mantenha a sessão atual aberta e teste em uma segunda janela. Se algo der errado, você ainda tem a primeira para desfazer. E se mesmo assim o acesso cair, o console no browser entra pela virtualização em vez da rede — continua funcionando com o sshd parado, o que transforma uma máquina perdida em cinco minutos de correção.

O tipo de chave também mudou. ed25519 é o padrão atual: chave menor, verificação mais rápida e sem os parâmetros que tornam RSA fácil de configurar mal. RSA só se precisar falar com sistema antigo — e aí com 4096 bits, nunca 1024.

Root: não é sobre o login, é sobre o rastro

PermitRootLogin no

O argumento comum é que root é alvo de força bruta. Com senha desligada isso já não importa. A razão real é outra: quando todo mundo entra como root, o log registra root em toda ação e você perde a resposta para "quem executou isto?".

Entrar com usuário nominal e escalar com sudo mantém o rastro em /var/log/auth.log e permite revogar o acesso de uma pessoa sem trocar a senha de todo o time.

Se algum automatismo precisa de root — e isso acontece —, prefira uma chave dedicada com comando restrito em vez de liberar o login inteiro:

# /root/.ssh/authorized_keys
command="/usr/local/bin/deploy.sh",no-port-forwarding,no-pty ssh-ed25519 AAAA...

Essa chave só consegue executar aquele script. Nem shell, nem túnel.

Restringir quem pode entrar

Por padrão, qualquer usuário do sistema com shell válido pode autenticar por SSH. Isso inclui contas de serviço criadas por pacotes que você instalou e esqueceu.

AllowUsers deploy crystian
# ou, gerenciando por grupo:
AllowGroups ssh-users

A forma por grupo escala melhor: adicionar alguém vira usermod -aG ssh-users fulano, sem editar configuração nem recarregar serviço.

Vale combinar com restrição de origem no firewall. Se todo acesso administrativo vem do IP do escritório ou de uma VPN, a regra de firewall que só libera a porta 22 para aquela faixa faz mais pelo servidor do que qualquer opção do sshd_config — porque o pacote nem chega a ser processado. Só não caia na armadilha do IP dinâmico: se o seu IP residencial muda, restringir por origem é um bloqueio agendado contra você mesmo.

O que fazer com o barulho que sobra

Com senha desligada, as tentativas continuam chegando e falhando. Elas não são mais risco de invasão, mas ainda consomem CPU, enchem log e atrapalham a leitura de auth.log.

O fail2ban resolve o incômodo:

sudo apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled  = true
maxretry = 4
findtime = 10m
bantime  = 1h
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

Vale ser honesto sobre o que ele entrega: com autenticação por senha desligada, o fail2ban é higiene de log, não camada de segurança. Ele é indispensável quando você não pode desligar senha; é conveniência quando pode.

Um detalhe que pega muita gente: bantime longo demais combinado com maxretry baixo bane o próprio administrador que errou a chave três vezes. Deixe o seu IP na lista de exceção:

ignoreip = 127.0.0.1/8 203.0.113.10

Segundo fator, quando faz sentido

Chave SSH é algo que você tem. Se o notebook for comprometido, a chave vai junto. Para acesso a servidor com dado sensível, o segundo fator fecha essa lacuna:

sudo apt install libpam-google-authenticator
google-authenticator    # como o usuário, não como root
# /etc/ssh/sshd_config.d/99-hardening.conf
AuthenticationMethods publickey,keyboard-interactive
KbdInteractiveAuthentication yes
# /etc/pam.d/sshd — no topo
auth required pam_google_authenticator.so nullok

O nullok permite que quem ainda não configurou o TOTP continue entrando — útil durante a transição, e a primeira coisa a remover quando todos já estiverem migrados.

Guarde os códigos de recuperação em outro lugar que não o servidor. Perder o celular sem eles significa depender do console de emergência para voltar.

Auditar o acesso que já existe

Endurecer a configuração não adianta se três ex-funcionários continuam com chave válida. Antes de qualquer ajuste, vale saber quem entra hoje.

Comece pelas chaves autorizadas de cada conta:

sudo awk -F: '$7 !~ /nologin|false/ {print $1, $6}' /etc/passwd |
while read -r u h; do
  [ -f "$h/.ssh/authorized_keys" ] && \
    echo "== $u" && ssh-keygen -lf "$h/.ssh/authorized_keys"
done

A saída traz o *fingerprint*, o tipo e o comentário de cada chave. O comentário é geralmente usuario@maquina e é a única pista de a quem a chave pertence — motivo para exigir comentário descritivo ao cadastrar uma chave nova.

Depois, veja quem realmente usou o servidor:

last -F -n 40                                    # sessões recentes
sudo journalctl -u ssh --since '30 days ago' \
  | grep 'Accepted' | awk '{print $9, $11}' | sort | uniq -c | sort -rn

Cruzar as duas listas costuma revelar duas coisas desconfortáveis: chave de gente que saiu da empresa, e conta de serviço com shell válido que ninguém lembra de ter criado.

A remoção é direta, e vale documentar o motivo em algum lugar que não seja o servidor:

sudo -u deploy sed -i '/fingerprint-ou-comentario/d' ~deploy/.ssh/authorized_keys
sudo usermod -s /usr/sbin/nologin conta-de-servico

Faça dessa auditoria um evento com data marcada — trimestral resolve. Chave SSH não expira sozinha, e é o único credencial da sua infraestrutura que tende a se acumular indefinidamente sem que nada avise.

Os mitos que não valem o esforço

  • Trocar a porta 22. Reduz o volume de log de varredura burra e nada mais. Um nmap -p- acha a porta nova em segundos. Se o objetivo é log limpo, o fail2ban faz melhor. Se é segurança, isto não é segurança.
  • Desabilitar o banner de versão. O sshd não permite omitir a versão do protocolo, e "esconder" a versão do OpenSSH não impede exploração de falha — impede apenas a listagem preguiçosa. Atualizar o pacote resolve de verdade.
  • Port knocking. Adiciona uma etapa frágil e difícil de operar em equipe para resolver um problema que a restrição por IP resolve melhor.
  • Limitar `MaxAuthTries` a 1. Parece rigoroso, mas o cliente SSH tenta cada chave do seu agente em sequência. Com três chaves carregadas, você é desconectado antes de chegar na certa. 3 a 6 é o intervalo razoável.

O conjunto mínimo defensável

# /etc/ssh/sshd_config.d/99-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
AllowGroups ssh-users
MaxAuthTries 4
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
sudo sshd -t && sudo systemctl reload ssh

Os dois ClientAlive derrubam sessão órfã de conexão que caiu — evita acumular processo de shell de gente que fechou o notebook. O LoginGraceTime curto reduz o tempo que uma conexão não autenticada segura recurso.

O X11Forwarding no fecha um encaminhamento que praticamente nenhum servidor precisa e que, quando habilitado, permite a quem já está dentro alcançar a interface gráfica de quem conectou. Pelo mesmo raciocínio, avalie AllowTcpForwarding no — só que aqui vale conferir antes: é exatamente esse recurso que faz funcionar o túnel para acessar um banco que só escuta em 127.0.0.1. Desligar sem avisar quebra a rotina de quem depende dele, e o erro que aparece do outro lado não menciona o servidor.

Aplicada a configuração, confirme o que ficou valendo de fato — o sshd combina arquivos de vários lugares, e o valor efetivo nem sempre é o que você acabou de escrever:

sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin|allowgroups'

Se você ainda está entrando com senha, o ponto de partida é trocar senha por chave: é o único item desta lista que muda a categoria do risco em vez de reduzir a margem. O resto é refino — e refino sobre senha exposta não protege ninguém.

Suba um servidor em minutos

Preço em real, suporte em português e dados no Brasil.

Criar conta