Proteger o SSH: autenticação por chave, bloquear o acesso root, desativar a palavra-passe
ed25519 em Linux, macOS e Windows, a armadilha do cloud-init em /etc/ssh/sshd_config.d, a ativação por socket no Ubuntu 24.04, a prova com sudo sshd -T e a via de recurso pela consola.
Um servidor acabado de instalar entra nas listas dos scanners poucos minutos depois de ficar acessível pela primeira vez. O que ali chega é quase sempre o mesmo: tentativas automáticas de adivinhar nomes de utilizador e palavras-passe na porta 22. Quem desativa a autenticação por palavra-passe e admite apenas chaves retira o fundamento a toda esta classe de ataques. Não a enfraquece: elimina-a por completo.
Os passos básicos quase toda a gente conhece. O que falta nos guias habituais é a parte seguinte: porque é que PasswordAuthentication no fica regularmente sem efeito nas imagens cloud, porque é que um systemctl reload ssh num servidor com ssh.socket ativo já não faz aquilo que espera, e como comprovar (em vez de esperar) que a alteração ficou mesmo ativa. É exatamente disso que trata este artigo. Todas as indicações referem-se a Debian 13, Debian 12, Ubuntu 24.04 e Ubuntu 22.04.
A regra que salva tudo: duas sessões
Antes de mexer seja no que for na configuração SSH, abra uma segunda janela de terminal e inicie sessão no servidor também a partir dela. Essa segunda sessão fica aberta até ter testado com êxito a nova configuração através de uma terceira ligação, completamente nova.
A razão é técnica: reiniciar o serviço SSH não termina as sessões existentes. As ligações em curso são servidas por processos filhos já separados e sobrevivem ao reinício do processo pai. De uma configuração estragada só dá conta na ligação seguinte, e nessa altura a sessão antiga é o seu único caminho de volta. Quem a fecha para "voltar a entrar de forma limpa" arrepende-se com alguma regularidade.
Verifique além disso que versão do OpenSSH tem à sua frente, porque vários detalhes dependem dela:
ssh -V
| Sistema | OpenSSH | Listener predefinido |
| Debian 13 (trixie) | 10.0p2 | ssh.service |
| Debian 12 (bookworm) | 9.2p1 | ssh.service |
| Ubuntu 24.04 LTS | 9.6p1 | ssh.socket |
| Ubuntu 22.04 LTS | 8.9p1 | ssh.service |
Se o serviço de servidor faltar por completo, por exemplo numa imagem mínima, instale-o:
sudo apt update
sudo apt install -y openssh-server
Gerar a chave: ed25519 em Linux, macOS e Windows
Escolha ed25519. Chave curta, verificação rápida, ampla margem de segurança, e todas as versões do OpenSSH aqui tratadas a suportam. RSA só precisa para sistemas antigos que não aceitem outra coisa, e nesse caso com pelo menos 4096 bits.
Linux e macOS
ssh-keygen -t ed25519 -a 100 -C "hani@notebook" -f ~/.ssh/id_ed25519
-a 100 aumenta o número de rondas KDF para a passphrase e encarece consideravelmente os ataques offline ao ficheiro da chave. -C define um comentário que fica depois em authorized_keys e lhe revela que chave vem de que dispositivo. Atribua uma passphrase. Uma chave sem passphrase é um ficheiro que qualquer pessoa pode levar consigo, basta sentar-se um momento ao seu computador.
Para não ter de escrever a passphrase em cada ligação, é o agent que trata de a guardar em memória:
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519
No macOS coloque antes a passphrase no porta-chaves. A antiga opção -K chama-se --apple-use-keychain desde o macOS 12:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Para que isto sobreviva a um reinício, em ~/.ssh/config deve constar:
Host *
UseKeychain yes
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
Windows
O Windows 10 a partir da versão 1809, o Windows 11 e o Windows Server a partir de 2019 já trazem o cliente OpenSSH. Na PowerShell:
ssh -V
ssh-keygen -t ed25519 -a 100 -C "hani@windows"
A chave fica em C:\Users\SeuNome\.ssh\. Se o cliente faltar, instale-o como funcionalidade do Windows:
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
No Windows, o agent é um serviço de sistema e vem desativado de origem:
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
Levar a chave pública para o servidor
Para o servidor vai exclusivamente o ficheiro com a extensão .pub. O ficheiro sem extensão é a chave privada e nunca sai do seu computador.
A via cómoda em Linux
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@203.0.113.10
O ssh-copy-id cria ~/.ssh, define as permissões corretas, acrescenta a chave a authorized_keys e verifica ao mesmo tempo se ela já lá está. No macOS a ferramenta não vem incluída em todas as versões. Confirme rapidamente com command -v ssh-copy-id e, se faltar, passe para a via manual.
A via Windows sem ssh-copy-id
O cliente OpenSSH da Microsoft não inclui ssh-copy-id. O original é um script de shell e nunca foi portado. O mesmo resultado obtém-se com um pipe:
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh root@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Aqui esconde-se um detalhe que custa muitas horas: ao encaminhar dados para um programa externo, a PowerShell acrescenta os fins de linha no formato Windows. Em authorized_keys fica então um carácter de retorno de carro invisível no fim da linha. Enquanto tiver comentado a chave com -C, esse carácter vai parar ao campo de comentário e não incomoda. Sem comentário fica colado ao bloco Base64, e a autenticação falha sem qualquer mensagem útil. Por isso: defina sempre um comentário e, na dúvida, faça uma limpeza no servidor.
sed -i 's/\r$//' ~/.ssh/authorized_keys
A via manual, que funciona em todo o lado
mkdir -p ~/.ssh && chmod 700 ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
Depois acrescente o conteúdo do ficheiro .pub como uma única linha. Se o ficheiro já estiver no servidor, por exemplo porque o carregou, faz-se diretamente assim:
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
Verifique a seguir o que foi mesmo parar ao ficheiro:
ssh-keygen -lf ~/.ssh/authorized_keys
A saída lista o fingerprint e o comentário de cada entrada válida. O que aqui não aparecer ficou partido em várias linhas ou está danificado. Compare o fingerprint com o da sua chave local:
ssh-keygen -l -f ~/.ssh/id_ed25519.pub
Se os dois coincidirem, a transferência correu de forma limpa. Teste agora, antes de desativar seja o que for, se a autenticação por chave funciona de todo. Só quando uma nova ligação passa sem pedido de palavra-passe é que se avança.
A armadilha: /etc/ssh/sshd_config.d e cloud-init
Este é o ponto em que a maioria dos guias termina, e é também o motivo mais frequente para a frase "eu desativei isso e continua na mesma".
Desde o Debian 11 e o Ubuntu 22.04, logo no topo de /etc/ssh/sshd_config existe uma linha que lê um diretório inteiro. Confirme por si próprio em que posição está:
grep -n Include /etc/ssh/sshd_config
Nos quatro sistemas aqui tratados, a linha Include está no início, não no fim. E agora chega a particularidade do OpenSSH, que quase nenhuma outra linguagem de configuração conhece: ganha o primeiro valor encontrado, não o último. O que estiver num ficheiro dentro de /etc/ssh/sshd_config.d/ é portanto lido antes do ficheiro principal e bate qualquer linha posterior em sshd_config.
O cloud-init usa exatamente esse diretório. No primeiro arranque escreve /etc/ssh/sshd_config.d/50-cloud-init.conf e, conforme o aprovisionamento, lá dentro está PasswordAuthentication yes. Pode depois escrever PasswordAuthentication no em sshd_config as vezes que quiser: a autenticação por palavra-passe continua aberta. Comece por obter uma visão geral:
ls -la /etc/ssh/sshd_config.d/
sudo grep -riE 'passwordauthentication|permitrootlogin|kbdinteractive' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
Daqui resulta a recomendação verdadeira: nem sequer toque em sshd_config. Crie antes um ficheiro próprio, cujo nome se ordene alfabeticamente antes de tudo aquilo que a automatização ali deposita. Os ficheiros são lidos por ordem alfabética, 01- vem antes de 50-, e como ganha o primeiro valor, o cloud-init pode reescrever o seu ficheiro as vezes que quiser sem anular o seu hardening. É esta a diferença face à recomendação muito difundida de criar um 99-hardening.conf: esse perde contra o cloud-init, e ainda por cima em silêncio. A mesma mecânica pode no entanto virar-se também contra si: um ficheiro com número mais baixo, por exemplo 00-cloud.conf, ganha contra o seu 01-, porque o OpenSSH fica com o valor lido em primeiro lugar. Vale por isso a pena espreitar o diretório também depois do hardening.
Escrever o hardening, com temporizador como via de retorno
Construa primeiro a rede de segurança. O comando seguinte remove automaticamente o seu novo ficheiro passados dez minutos e reinicia o SSH, caso não o tenha cancelado até lá:
sudo systemd-run --on-active=10min --unit=ssh-rollback /bin/sh -c 'rm -f /etc/ssh/sshd_config.d/01-hardening.conf; systemctl try-restart ssh.socket ssh.service'
Agora a configuração propriamente dita:
sudo tee /etc/ssh/sshd_config.d/01-hardening.conf >/dev/null <<'EOF'
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
PermitEmptyPasswords no
MaxAuthTries 3
EOF
sudo chmod 644 /etc/ssh/sshd_config.d/01-hardening.conf
Duas linhas merecem uma explicação. KbdInteractiveAuthentication no não é acessório: se ficar em yes, o PAM pode continuar a oferecer o pedido de palavra-passe pelo desvio da entrada interativa de teclado, apesar de PasswordAuthentication estar em no. É precisamente por isso que existem servidores que pedem uma palavra-passe mesmo com a autenticação por palavra-passe desligada.
E PermitRootLogin prohibit-password em vez de no: assim mantém-se o acesso como root por chave, enquanto as palavras-passe para root ficam excluídas. Quando tiver criado um utilizador próprio com sudo e tiver testado de forma comprovada a autenticação por chave desse utilizador, passe então para PermitRootLogin no. Antes disso, não.
O que não deve adotar, ainda que apareça em guias mais antigos: ChallengeResponseAuthentication. Desde o OpenSSH 8.7, a opção não passa de um alias obsoleto para KbdInteractiveAuthentication e nas versões atuais gera uma mensagem de descontinuação no log. Deixe-a de fora.
Verificação da sintaxe antes de ativar, sem exceções:
sudo sshd -t
Nenhuma saída significa: o ficheiro está correto do ponto de vista sintático. Não significa que faça, quanto ao conteúdo, aquilo que pretende. Disso trata já a seguir o sudo sshd -T.
Se o teste indicar antes Missing privilege separation directory: /run/sshd, é porque o sshd ainda não arrancou uma única vez desde que o sistema ligou, pois esse diretório só nasce com o ssh.service. Afeta sobretudo o Ubuntu 24.04 com ativação por socket e resolve-se com sudo mkdir -p /run/sshd ou sudo systemctl start ssh.service; num servidor onde está neste momento com sessão SSH iniciada, isso não chega a acontecer.
Ativar: ssh.service, ssh.socket e as diferenças entre distribuições
É aqui que os quatro sistemas divergem, e o velho hábito systemctl reload ssh é a resposta errada em todo o lado onde é o ssh.socket a segurar a porta.
O Ubuntu aposta na ativação por socket desde a 22.10 e o Ubuntu 24.04 entrega-a por predefinição: aí o ssh.socket está ativado e o ssh.service desativado. O Debian faz de fábrica exatamente o contrário. O Debian 13 e o Debian 12 vêm com o ssh.service ativado e o ssh.socket desativado, ao contrário da convicção generalizada de que o Debian 13 passaria para o socket numa instalação nova. Com a ativação por socket, é o próprio systemd que escuta na porta 22 e só arranca um sshd novo quando chega uma ligação. Isso tem um efeito secundário agradável: as alterações a sshd_config passam a valer de qualquer forma na ligação seguinte, porque cada processo de ligação volta a ler a configuração. Só precisa de recarregar para definições que digam respeito ao próprio listener, ou seja Port e ListenAddress.
Não adivinhe, portanto: pergunte ao sistema que unidade presta o serviço na sua máquina:
systemctl is-enabled ssh.socket ssh.service
| Sistema | ssh.socket | ssh.service | Particularidade |
| Ubuntu 24.04 LTS | enabled | disabled | Ativação por socket por predefinição, ListenStream em 0.0.0.0:22 e [::]:22 |
| Debian 13 (trixie) | disabled | enabled | Accept=no |
| Debian 12 (bookworm) | disabled | enabled | Accept=no |
| Ubuntu 22.04 LTS (e igualmente Debian 11) | disabled | enabled | Accept=yes, ou seja um processo próprio por cada ligação através de ssh@.service |
E depois um comando que está correto nos quatro sistemas:
sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service
O try-restart só reinicia uma unidade se ela estiver de facto ativa e deixa a outra intacta. Assim não tem de adivinhar. Ambas as partes precisam de root: o sshd fica em /usr/sbin e no Debian não consta do PATH de um utilizador normal, e o try-restart passa de qualquer maneira pelo systemd. Sem sudo, a chamada acaba, conforme o sistema, com sshd: command not found ou com sshd: no hostkeys available, porque as chaves de host em /etc/ssh só são legíveis pelo root.
De forma igualmente deliberada, nada de um systemctl restart ssh.socket isolado, mesmo que esse comando apareça em muitos guias. No Debian 13 e no Debian 12 falha com Job failed. See journalctl -xe for details. e no journal lê-se ssh.socket: Socket service ssh.service already active, refusing. A nova configuração não fica ativa e a contraprova continua a indicar Permission denied (publickey,password). No Ubuntu 22.04 e no Debian 11 o comando passa sem mensagem de erro, mas para o ssh.service e comuta o host para ativação por socket. Como o ssh.socket aí continua desativado, no reinício seguinte volta a arrancar o ssh.service: o modo de funcionamento alterna assim sem que dê por isso. O try-restart sobre ambas as unidades evita os dois casos.
Também de forma deliberada, nada de reload: num sistema em que é o ssh.socket a segurar a porta, um systemctl reload ssh responde com
fatal: Cannot bind any address.
A seguir o serviço fica em estado de erro e largou a sua porta. Um reinício não tem este problema e, do mesmo modo, não termina as sessões existentes.
Se quiser mudar a porta, chega a diferença seguinte entre distribuições. O Ubuntu 24.04 gera a configuração do socket a partir de sshd_config através de um gerador do systemd, e aí basta, depois da alteração da porta:
sudo systemctl daemon-reload
sudo systemctl try-restart ssh.socket ssh.service
Se, pelo contrário, tiver passado deliberadamente para ssh.socket no Debian, a porta fica na própria unit e uma alteração em sshd_config não produz efeito. Define-a através de um ficheiro de override com sudo systemctl edit ssh.socket, com um ListenStream= vazio para repor e uma segunda linha com o novo valor. Confirme depois o resultado na realidade, não na configuração:
sudo ss -tlnp
A prova: sshd -T e a contraprova
Um comando que corre sem erros não é prova nenhuma. A prova é a configuração global já resolvida, na qual todos os ficheiros incluídos estão contabilizados:
sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|permitrootlogin|usepam|port|authorizedkeysfile)'
Espera-se uma saída deste género:
port 22
permitrootlogin prohibit-password
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
usepam yes
authorizedkeysfile .ssh/authorized_keys .ssh/authorized_keys2
Se aí aparecer passwordauthentication yes apesar do seu ficheiro, existe no diretório um ficheiro que se ordena alfabeticamente antes do seu. Volte à secção sobre a ordem de leitura.
Para uma consulta individual basta o mesmo comando com um filtro mais estreito:
sudo sshd -T | grep -i passwordauthentication
Há duas razões para o sudo à frente. Primeiro, o sshd -T lê as chaves de host, que em /etc/ssh só são legíveis pelo root. Segundo, o próprio sshd fica em /usr/sbin, e no Debian esse diretório não faz parte do PATH de um utilizador normal, pelo que sem sudo a chamada acaba ali com sshd: command not found. Quem quiser evitar o caminho do sudo escreve o caminho completo /usr/sbin/sshd.
A partir do OpenSSH 9.3, ou seja no Ubuntu 24.04 (9.6p1) e no Debian 13 (10.0p2), existe adicionalmente o sshd -G. A opção avalia a mesma configuração, mas não exige chaves de host legíveis nem uma /run/sshd já existente, o que a torna útil para verificações em automatização e em containers. No Debian 12 (9.2p1), no Ubuntu 22.04 (8.9p1) e no Debian 11 (8.4p1) a opção ainda não existe e o sshd rejeita-a como opção desconhecida. Para um guia que sirva em todo o lado, o sudo sshd -T continua por isso a ser a escolha certa.
Agora a contraprova, e a partir de um terminal novo, enquanto a sessão antiga fica aberta. Force uma autenticação por palavra-passe e desative as chaves para esta tentativa:
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password,keyboard-interactive root@203.0.113.10
Está correto se for recusado de imediato e sem qualquer pedido de palavra-passe:
root@203.0.113.10: Permission denied (publickey).
Decisivo é o conteúdo dos parênteses. Se ali estiver (publickey,password) ou se aparecer um pedido de palavra-passe, a autenticação por palavra-passe continua aberta. Só quando a contraprova falha de forma limpa e uma ligação normal por chave continua a resultar é que fecha a sessão antiga e para o temporizador:
sudo systemctl stop ssh-rollback.timer
As mensagens de erro à letra
Permission denied (publickey). O servidor só aceita chaves e a sua não serve. Execute ssh -v e veja que ficheiro foi realmente oferecido. Causas mais frequentes: nome de utilizador errado, chave na authorized_keys do utilizador errado, ou bloqueou o root e continua a iniciar sessão como root.
Authentication refused: bad ownership or modes for directory /home/hani/.ssh Esta linha não aparece no seu ecrã, mas sim no log do servidor, visível através de sudo journalctl -u ssh -n 50 --no-pager. Não filtre porém por -t sshd: a partir do OpenSSH 9.8, ou seja no Debian 13 já de origem, as sessões correm no processo próprio sshd-session, e sob a tag sshd ficam apenas mensagens do listener, sem um único processo de autenticação. Quem quiser filtrar por tags usa sudo journalctl -t sshd -t sshd-session -n 50 --no-pager. O OpenSSH recusa chaves quando a pasta pessoal, .ssh ou authorized_keys são graváveis pelo grupo ou por outros. A correção:
chmod go-w ~ && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
WARNING: UNPROTECTED PRIVATE KEY FILE! ou então Load key "/home/hani/.ssh/id_ed25519": bad permissions. O mesmo problema do lado do cliente. chmod 600 ~/.ssh/id_ed25519 resolve-o.
Too many authentication failures O seu agent oferece à vez todas as chaves carregadas e ultrapassa com isso o MaxAuthTries. Limite a ligação a uma única chave: ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 hani@203.0.113.10.
sign_and_send_pubkey: no mutual signature supported Uma chave RSA antiga com assinatura SHA-1 encontra um servidor que já não a aceita. Gere uma chave ed25519 em vez de andar a mexer em PubkeyAcceptedAlgorithms.
Bad owner or permissions on C:\Users\hani\.ssh\config O cliente Windows verifica as permissões de acesso do seu ficheiro de configuração. Nas propriedades do ficheiro, em Segurança, remova a herança e todas as entradas exceto a sua conta de utilizador e SYSTEM.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! A chave de host do servidor é outra face à última vez. Depois de uma reinstalação isso é expectável, caso contrário não. Remova a entrada antiga de forma dirigida com ssh-keygen -R 203.0.113.10, e apenas se souber o motivo.
Sem acesso: a via da consola na área de cliente
Se as duas sessões desapareceram e já nenhuma ligação abre, não se trata de perda de dados, mas de um desvio. Todos os servidores root KVM e todos os servidores dedicados da KernelHost têm na área de cliente uma consola que assenta na saída de vídeo do sistema e é independente da pilha de rede do servidor. Consegue portanto entrar mesmo quando o SSH já não está à escuta.
- Iniciar sessão na área de cliente, abrir o servidor afetado e arrancar a consola.
- No pedido de início de sessão, entrar como root com a palavra-passe atribuída no aprovisionamento. O acesso pela consola não passa pelo SSH e não é afetado por
PermitRootLogin. - Reverter a alteração:
sudo rm /etc/ssh/sshd_config.d/01-hardening.conf - Verificar e reiniciar:
sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service - Se o SSH não arrancar de todo, ajudam
sudo systemctl status ssh.socket ssh.servicee uma vista de olhos asudo journalctl -u ssh -n 50 --no-pager. O valor de retorno 3 nostatussignifica apenas que uma das duas unidades está inativa; no Debian, com ossh.socketdesativado, isso é o normal.
Duas precauções poupam-lhe este caminho quase sempre. Anote a palavra-passe de root antes de desativar a autenticação por palavra-passe, porque a consola precisa dela. E verifique se existe uma firewall ativa antes de mudar a porta do SSH. Uma mudança de porta sem a devida regra de abertura deixa-o de fora com a mesma fiabilidade que uma sshd_config estragada, mas apresenta-se de outra maneira: em vez de Permission denied recebe Connection timed out.
Em resumo
- Deixe uma segunda sessão aberta até que uma terceira ligação, nova, funcione de forma comprovada.
- ed25519 com
-a 100e passphrase, chave pública através dessh-copy-id, no Windows através de um pipe. - Teste a autenticação por chave antes de deitar abaixo a autenticação por palavra-passe.
- Hardening em
/etc/ssh/sshd_config.d/01-hardening.conf, não emsshd_config. Ganha o primeiro valor encontrado, daí o número baixo. - Não se esqueça de
KbdInteractiveAuthentication no, caso contrário o desvio pelo PAM fica aberto. - Ative com
sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service, nem comreloadnem com umrestart ssh.socketisolado. - Esclareça antes com
systemctl is-enabled ssh.socket ssh.serviceque unidade está de facto ativa. O Ubuntu 24.04 usa o socket, o Debian 13 e o Debian 12 usam o serviço. - A prova vem de
sudo sshd -Te de uma tentativa forçada com palavra-passe a partir de uma nova sessão. - A via de emergência é a consola na área de cliente, e para isso tenha a palavra-passe de root à mão.
As camadas seguintes mais óbvias são descritas nos nossos artigos sobre fail2ban e sobre a firewall UFW. Mais importante do que ambas é aquilo que acabou de resolver.
Perguntas frequentes
Porque é que a autenticação por palavra-passe continua ativa apesar de PasswordAuthentication no?
Tenho de reiniciar o serviço depois de alterar o sshd_config?
Porque é que devo evitar o reload?
Como levo a minha chave do Windows para o servidor, se ali não existe o ssh-copy-id?
Como comprovo que a autenticação por palavra-passe está mesmo fechada?
O que faço se ficar sem acesso ao servidor?
PermitRootLogin no ou prohibit-password, qual a melhor escolha?
2026 KernelHost GmbH. Todos os direitos reservados. Este guia está protegido por direitos de autor. A sua republicação noutros sites, na íntegra, em parte ou de forma editada, não é permitida sem o nosso consentimento por escrito. Citações com indicação da fonte e ligação são expressamente bem-vindas.

