Proteger o SSH: autenticação por chave, bloquear o acesso root, desativar a palavra-passe

Publicado a 18 min de leitura

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
SistemaOpenSSHListener predefinido
Debian 13 (trixie)10.0p2ssh.service
Debian 12 (bookworm)9.2p1ssh.service
Ubuntu 24.04 LTS9.6p1ssh.socket
Ubuntu 22.04 LTS8.9p1ssh.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
Sistemassh.socketssh.serviceParticularidade
Ubuntu 24.04 LTSenableddisabledAtivação por socket por predefinição, ListenStream em 0.0.0.0:22 e [::]:22
Debian 13 (trixie)disabledenabledAccept=no
Debian 12 (bookworm)disabledenabledAccept=no
Ubuntu 22.04 LTS (e igualmente Debian 11)disabledenabledAccept=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.

  1. Iniciar sessão na área de cliente, abrir o servidor afetado e arrancar a consola.
  2. 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.
  3. Reverter a alteração: sudo rm /etc/ssh/sshd_config.d/01-hardening.conf
  4. Verificar e reiniciar: sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service
  5. Se o SSH não arrancar de todo, ajudam sudo systemctl status ssh.socket ssh.service e uma vista de olhos a sudo journalctl -u ssh -n 50 --no-pager. O valor de retorno 3 no status significa apenas que uma das duas unidades está inativa; no Debian, com o ssh.socket desativado, 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 100 e passphrase, chave pública através de ssh-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 em sshd_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 com reload nem com um restart ssh.socket isolado.
  • Esclareça antes com systemctl is-enabled ssh.socket ssh.service que 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 -T e 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?
Quase sempre por causa de um ficheiro em /etc/ssh/sshd_config.d/, normalmente o 50-cloud-init.conf. No Debian e no Ubuntu, a linha Include está no início do sshd_config, e o OpenSSH fica com o primeiro valor encontrado, não com o último. O que estiver no diretório ganha portanto contra o ficheiro principal. Verifique com sudo sshd -T o que vale de facto e crie o seu próprio ficheiro como 01-hardening.conf, para que seja lido antes de todos os ficheiros gerados automaticamente.
Tenho de reiniciar o serviço depois de alterar o sshd_config?
No Debian 13, no Debian 12 e no Ubuntu 22.04 sim, porque aí vem de fábrica o ssh.service ativado e o ssh.socket desativado. No Ubuntu 24.04 é o systemd que escuta por predefinição através do ssh.socket e arranca um sshd novo por cada ligação, que volta a ler a configuração de qualquer forma. Só Port e ListenAddress dizem respeito ao próprio listener. Que unidade está ativa na sua máquina mostra-o o systemctl is-enabled ssh.socket ssh.service. O comando sudo sshd -t && sudo systemctl try-restart ssh.socket ssh.service está correto nos quatro sistemas; já um systemctl restart ssh.socket isolado não: no Debian 13 e no Debian 12 falha e a nova configuração não fica ativa, no Ubuntu 22.04 comuta o host para ativação por socket sem que dê por isso.
Porque é que devo evitar o reload?
Em sistemas com o ssh.socket ativo, ou seja por predefinição no Ubuntu 24.04, o systemctl reload ssh falha com a mensagem fatal: Cannot bind any address, o serviço passa a estado de erro e liberta a sua porta. Um reinício não tem este problema e também não termina as sessões existentes, porque as ligações em curso são servidas por processos filhos próprios.
Como levo a minha chave do Windows para o servidor, se ali não existe o ssh-copy-id?
Através de um pipe: type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh utilizador@servidor "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys". Tenha o cuidado de ter comentado a chave com -C, caso contrário um carácter de retorno de carro acrescentado pela PowerShell pode danificar o bloco Base64. No servidor, o sed -i 's/\r$//' ~/.ssh/authorized_keys trata disso.
Como comprovo que a autenticação por palavra-passe está mesmo fechada?
Com dois passos. Primeiro sudo sshd -T, que devolve a configuração global resolvida e tem de mostrar passwordauthentication no e kbdinteractiveauthentication no. Segundo, uma contraprova a partir de uma nova sessão: ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password,keyboard-interactive utilizador@servidor tem de ser recusado de imediato com Permission denied (publickey). Se dentro dos parênteses estiver publickey,password, o acesso continua aberto.
O que faço se ficar sem acesso ao servidor?
Inicie sessão na área de cliente, abra o servidor e arranque a consola. Ela está ligada à saída de vídeo do sistema e é independente do SSH. Aí entre como root, remova o seu ficheiro com sudo rm /etc/ssh/sshd_config.d/01-hardening.conf, verifique com sudo sshd -t e reinicie com sudo systemctl try-restart ssh.socket ssh.service. Tenha a palavra-passe de root à mão antes de desativar a autenticação por palavra-passe.
PermitRootLogin no ou prohibit-password, qual a melhor escolha?
O prohibit-password continua a permitir o root por chave e exclui apenas as palavras-passe, o que mantém o acesso em caso de configuração errada. O no é mais rigoroso, mas pressupõe que existe um segundo utilizador com sudo e que a autenticação por chave desse utilizador já funciona de forma comprovada. Só mude depois de esse teste estar passado.

SSH Segurança de servidores Linux Debian Ubuntu OpenSSH ed25519 systemd cloud-init Tutorial