Aller au contenu
ecdsa.com
← Tous les articles

Certificats SSH vs authorized_keys : AC et principaux

Avec authorized_keys, passer à l’échelle signifie copier les clés sur chaque serveur et ne jamais oublier de les retirer. Les certificats OpenSSH remplacent cela par une seule AC de confiance, des clés signées à courte durée de vie et des noms au lieu d’empreintes. Voici la configuration complète, testée sur OpenSSH 10.0, avec chacun des échecs qu’elle produit.

En bref. Avec authorized_keys, chaque serveur liste toutes les clés publiques autorisées à se connecter, et l’accès dure jusqu’à ce que quelqu’un supprime la ligne. Avec les certificats OpenSSH, les serveurs font confiance à une seule AC utilisateur (TrustedUserCAKeys). Les utilisateurs font signer leur propre clé avec ssh-keygen -s, qui y ajoute une identité (-I), les comptes sur lesquels elle peut se connecter (principaux, -n) et une période de validité (-V). Les clients font confiance à une seule AC d’hôte (@cert-authority dans known_hosts) au lieu d’accepter l’empreinte de chaque hôte à la première utilisation. Les certificats à courte durée de vie rendent automatique le retrait des accès lors d’un départ, et les listes de révocation de clés (RevokedKeys) se chargent du reste. La contrepartie : vous gérez désormais une AC et devez protéger sa clé.

Ce que ne fait pas authorized_keys

~/.ssh/authorized_keys est simple et ne dépend d’aucun autre composant, d’où sa présence partout. Ses limites apparaissent quand les personnes et les serveurs se multiplient :

  • L’accès n’expire jamais. Une clé ajoutée pour un prestataire en 2023 fonctionne encore en 2026, à moins que quelqu’un ne la retire de chacun des serveurs où elle a été copiée. (expiry-time= existe pour chaque ligne, mais encore faut-il que quelqu’un le définisse.)
  • Aucune identité. La ligne contient une clé et un commentaire. La personne, l’équipe ou la tâche à laquelle elle correspond est notée dans un tableur, quand elle l’est quelque part.
  • La distribution vous incombe. Ajouter ou retirer une personne implique d’intervenir sur chaque serveur, avec un outil de gestion de configuration ou à la main.
  • Les clés d’hôte reposent sur la confiance à la première utilisation. Chaque client accepte l’empreinte de chaque serveur la première fois, et la réinstallation d’un serveur déclenche REMOTE HOST IDENTIFICATION HAS CHANGED partout.

Les certificats OpenSSH, disponibles depuis OpenSSH 5.4 (2010), règlent ces quatre problèmes. Ce ne sont pas des certificats X.509 : le format est propre à OpenSSH, une clé publique accompagnée d’une identité et de contraintes, signée par une clé d’AC qui est elle-même une clé SSH ordinaire. En 2025, la description du format est passée du fichier PROTOCOL.certkeys des sources d’OpenSSH à un Internet-Draft, aujourd’hui draft-ietf-sshm-cert au sein du groupe de travail SSHM de l’IETF.

Deux AC, deux sens

Il vous faut deux clés de signature distinctes, car elles répondent à des questions différentes :

  • L’AC utilisateur répond à la question « cette clé peut-elle se connecter sous ce compte ? ». Les serveurs lui font confiance.
  • L’AC d’hôte répond à la question « s’agit-il vraiment de web01 ? ». Les clients lui font confiance.
Création des AC (OpenSSH 10.0p2)
$ ssh-keygen -q -t ed25519 -f user_ca -C "Example user CA" -N ""
$ ssh-keygen -q -t ed25519 -f host_ca -C "Example host CA" -N ""

Le -N "" simplifie l’environnement de test. Une vraie clé d’AC a sa place dans un HSM, un jeton matériel ou un service de signature (le moteur de secrets SSH de HashiCorp Vault, smallstep, Teleport et les équivalents des fournisseurs cloud font tous cela). Quiconque détient la clé de l’AC utilisateur peut se connecter sous n’importe quel compte que les serveurs autorisent.

Certificats utilisateur

Signez la clé publique existante d’Alice pour huit heures, pour les comptes alice et deploy :

ssh-keygen -s : émettre un certificat utilisateur
$ ssh-keygen -s user_ca -I alice@example.test -n alice,deploy -V -5m:+8h -z 1001 id_ed25519.pub
Signed user key /home/alice-laptop/id_ed25519-cert.pub: id "alice@example.test" serial 1001 for alice,deploy valid from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
  • -I est l’identifiant de clé (key ID). sshd le journalise à chaque connexion : faites-en le nom d’une personne ou d’une tâche, pas un nom d’hôte.
  • -n liste les principaux, les noms sous lesquels ce certificat peut agir. Le manuel de ssh-keygen indique que les certificats sans principaux sont « valables pour tous les utilisateurs ou hôtes », mais sshd 10.0 refuse un tel certificat utilisateur avec TrustedUserCAKeys (Certificate lacks principal list). Définissez toujours -n.
  • -V -5m:+8h fixe le début de la validité à cinq minutes dans le passé (décalage d’horloge) et sa fin à huit heures dans le futur.
  • -z définit un numéro de série, auquel la révocation peut faire référence.

ssh-keygen -L affiche ce qui a été signé :

ssh-keygen -L -f id_ed25519-cert.pub
/home/alice-laptop/id_ed25519-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s
        Signing CA: ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60 (using ssh-ed25519)
        Key ID: "alice@example.test"
        Serial: 1001
        Valid: from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
        Principals: 
                alice
                deploy
        Critical Options: (none)
        Extensions: 
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc

Par défaut, un certificat utilisateur accorde les cinq extensions. -O clear les retire, et vous pouvez ensuite rajouter uniquement ce dont vous avez besoin, par exemple -O clear -O permit-pty. Les options critiques restreignent le certificat. force-command remplace la commande demandée par le client, quelle qu’elle soit, et source-address limite les adresses depuis lesquelles il peut être utilisé. Avec source-address=10.0.0.0/8, une connexion depuis 127.0.0.1 est refusée. Depuis une source autorisée, la commande forcée s’exécute quelle que soit la demande du client :

Options critiques en pratique (lignes du journal de sshd en LogLevel VERBOSE)
        Critical Options: 
                force-command echo backup only
                source-address 10.0.0.0/8
$ ssh deploy@web01.example.test
deploy@web01.example.test: Permission denied (publickey).
cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1).
Refused by certificate options
...
$ ssh deploy@web01.example.test "rm -rf /tmp/anything"
backup only
Starting session: forced-command (key-option) 'echo backup only' for deploy from 127.0.0.1 port 48976 id 0

Le client ssh détecte le certificat automatiquement lorsqu’il est placé à côté de la clé privée sous le nom id_ed25519-cert.pub. Sinon, indiquez-le avec CertificateFile. ssh-add charge les deux dans l’agent.

Configurer sshd

Tout le côté serveur tient en quelques lignes. Ce fichier de configuration additionnel a passé la vérification sshd -t et a servi à tous les tests de cette page :

/etc/ssh/sshd_config.d/ca.conf
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
RevokedKeys /etc/ssh/revoked_keys
PasswordAuthentication no
LogLevel VERBOSE
  • TrustedUserCAKeys contient une ou plusieurs clés publiques d’AC. Pendant une rotation d’AC, indiquez les deux.
  • AuthorizedPrincipalsFile associe les comptes à des principaux. Ici, /etc/ssh/auth_principals/deploy contient la seule ligne deploy : seuls les certificats qui listent le principal deploy peuvent donc se connecter sous deploy. Sans cette option, sshd exige que le nom du compte lui-même figure parmi les principaux. Passer par une association est plus souple : un principal tel que oncall peut être autorisé sur de nombreux comptes, et AuthorizedPrincipalsCommand peut le rechercher dans un annuaire.
  • RevokedKeys pointe vers une liste de révocation (voir plus bas).
  • LogLevel VERBOSE journalise, pour chaque connexion, l’identifiant de clé et le numéro de série du certificat. C’est la piste d’audit qu’authorized_keys n’a jamais eue.

Une connexion avec le certificat d’Alice, vue des deux côtés :

ssh -v (client) et journal de sshd (serveur), extrait
Server host certificate: ssh-ed25519-cert-v01@openssh.com SHA256:IuzvI4QsLmSx1+6p3yrvlRx2wdl+LUCY1zXqSzssLAc, serial 0 ID "web01" CA ssh-ed25519 SHA256:+iie/YHPulJReHN2GQ8UvrorBVrtayD2T1MGxeg/QCQ valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Host 'web01.example.test' is known and matches the ED25519-CERT host certificate.
Offering public key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Server accepts key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Authenticated to web01.example.test ([127.0.0.1]:22) using "publickey".

Accepted publickey for deploy from 127.0.0.1 port 57432 ssh2: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s ID alice@example.test (serial 1001) CA ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60

Si vous ne pouvez pas modifier sshd_config, un compte donné peut faire confiance à une AC au moyen de son propre authorized_keys. L’option principals= fonctionne comme le fichier des principaux :

~deploy/.ssh/authorized_keys : faire confiance à une AC pour un seul compte (clé abrégée)
cert-authority,principals="deploy-team" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK63...
$ ssh deploy@web01.example.test 'echo logged in as $(whoami)'
logged in as deploy

Certificats d’hôte

Signez la clé d’hôte de chaque serveur avec l’AC d’hôte. -h en fait un certificat d’hôte, et les principaux sont les noms que les clients utilisent pour se connecter :

Signer une clé d’hôte
$ ssh-keygen -s host_ca -I web01 -h -n web01.example.test,web01 -V -5m:+52w /etc/ssh/ssh_host_ed25519_key.pub
Signed host key /etc/ssh/ssh_host_ed25519_key-cert.pub: id "web01" serial 0 for web01.example.test,web01 valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08

Faites ensuite pointer HostCertificate vers le résultat (comme dans le fichier de configuration de sshd ci-dessus), et donnez aux clients une seule ligne dans known_hosts (ou /etc/ssh/ssh_known_hosts) au lieu d’une empreinte par hôte :

known_hosts : faire confiance à l’AC d’hôte pour un domaine
@cert-authority *.example.test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtSsqoqxwcl31DTJg3kTPsv9eLuuw5gU1BcvYybODlu Example host CA

Les serveurs nouveaux ou réinstallés sont alors considérés comme fiables dès la première connexion, sans demande de confirmation, à condition que leur certificat liste le nom utilisé par le client. C’est cette dernière condition qui surprend le plus souvent. Si le client se connecte sous un nom qui n’est pas un principal, il se rabat sur les clés d’hôte ordinaires et, avec la vérification stricte, il s’arrête :

Connexion sous le nom db01.example.test à un hôte certifié uniquement pour web01
$ ssh deploy@db01.example.test
Certificate invalid: name is not a listed principal
No ED25519 host key is known for db01.example.test and you have requested strict checking.
Host key verification failed.

Listez tous les noms qu’utilisent les clients, y compris les noms courts, les CNAME, les adresses IP si des personnes se connectent par IP, et les noms des répartiteurs de charge. ssh-keyscan -c récupère le certificat d’un serveur pour l’examiner, et des lignes @revoked dans known_hosts rejettent une clé d’hôte ou une AC précise.

À quoi ressemblent les échecs

Le client n’affiche jamais que Permission denied (publickey). pour les problèmes de certificat utilisateur. La raison se trouve dans le journal du serveur, en LogLevel VERBOSE :

SituationJournal de sshd (OpenSSH 10.0p2)
Compte absent des principaux du certificat (après association)Certificate does not contain an authorized principal
Certificat émis sans -n, pas d’AuthorizedPrincipalsFileCertificate lacks principal list
Certificat dont la période -V est dépasséeCertificate invalid: expired
Numéro de série ou clé figurant dans la KRLAuthentication key ED25519-CERT SHA256:... revoked by file /etc/ssh/revoked_keys
source-address ne correspond pascert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1).

Deux phénomènes ressemblent à des problèmes de certificat sans en être :

  • Décalage d’horloge. La validité est vérifiée par rapport à l’horloge du serveur. Émettez des certificats valables à partir de quelques minutes dans le passé (-V -5m:...) et maintenez les serveurs synchronisés par NTP.
  • Pénalités après des échecs répétés. Depuis OpenSSH 9.8, sshd pénalise les adresses dont l’authentification échoue à répétition (PerSourcePenalties, activé par défaut). Lors de tests de certificats depuis une même machine, une tentative échouée a entraîné la fermeture de la connexion suivante avant l’authentification :
sshd 10.0p2 après un échec de connexion par certificat
srclimit_penalise: 127.0.0.1/32: activating ipv4 penalty of 19 seconds for penalty: failed authentication
$ ssh deploy@web01.example.test
Connection closed by 127.0.0.1 port 22
drop connection #0 from [127.0.0.1]:55080 on [127.0.0.1]:22 penalty: failed authentication

Si ssh affiche Connection closed juste après une série d’échecs, attendez la fin de la pénalité avant de modifier à nouveau votre configuration.

Révocation par KRL

Une validité courte est le principal mécanisme de révocation : un certificat de huit heures expire tout seul. Pour le reste, OpenSSH dispose des listes de révocation de clés (KRL, depuis OpenSSH 6.2), un format binaire compact capable de révoquer par numéro de série, par identifiant de clé ou par clé. Pour révoquer le certificat d’Alice dont le numéro de série est 1001 :

ssh-keygen -k : construire une KRL, puis y confronter un certificat
$ printf 'serial: 1001\n' > /tmp/krl-spec
$ ssh-keygen -k -f /etc/ssh/revoked_keys -s user_ca.pub /tmp/krl-spec
Revoking from /tmp/krl-spec
$ ssh-keygen -Q -f /etc/ssh/revoked_keys id_ed25519-cert.pub
/home/alice-laptop/id_ed25519-cert.pub (alice@laptop): REVOKED

sshd lit RevokedKeys à chaque authentification : la connexion suivante échoue donc sans qu’il faille redémarrer. Ajoutez -u pour mettre à jour une KRL existante au lieu de la remplacer. Distribuez la KRL avec les mêmes outils que la clé de l’AC. Un serveur dont la KRL n’est pas à jour accepte ce dont il ignore la révocation. C’est une raison de plus de garder des certificats à courte durée de vie et de traiter la KRL comme un filet de sécurité pour les heures restantes.

Côte à côte

authorized_keysCertificats
Accorder un accèsCopier une clé sur chaque serveurSigner une clé une seule fois
Retirer un accèsLa supprimer sur chaque serveurLa laisser expirer, ou l’ajouter à la KRL
ExpirationFacultative, par ligne (expiry-time=)Chaque certificat a une période de validité
Identité dans les journauxEmpreinte de la cléIdentifiant de clé et numéro de série
Restrictionsfrom=, command=, restrict par lignesource-address, force-command, extensions par certificat
Confiance dans les hôtesEmpreinte par hôte (TOFU)Une seule ligne @cert-authority
Secret critiqueAucun secret centralLa clé privée de l’AC
Éléments à maintenirAucunÉmission, protection de l’AC, distribution de la KRL

Les restrictions par ligne d’authorized_keys sont bien réelles et ont été testées sur le même serveur :

Options d’authorized_keys, OpenSSH 10.0p2 (clés abrégées)
restrict,from="10.0.0.0/8",command="echo backup only" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
/home/deploy/.ssh/authorized_keys:1: Authentication tried for deploy with correct key but not from a permitted host (host=127.0.0.1, ip=127.0.0.1, required=10.0.0.0/8).

expiry-time="20261001" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).

Pour une poignée de serveurs et de personnes, authorized_keys avec restrict et expiry-time suffit. Les certificats deviennent rentables dès que les arrivées, les départs et les réinstallations d’hôtes ont lieu chaque semaine, ou lorsque vous devez savoir qui s’est connecté, et pas seulement avec quelle clé.

Migrer depuis authorized_keys

Inutile de prévoir un jour de bascule unique. sshd vérifie authorized_keys et les AC de confiance en parallèle, si bien que les deux types de connexion fonctionnent pendant la transition :

  1. Déployez d’abord la confiance envers l’AC. Installez TrustedUserCAKeys, les fichiers de principaux et une KRL vide sur tous les serveurs, et ne touchez pas à authorized_keys.
  2. Émettez des certificats pour une première équipe et laissez-la les utiliser pendant quelques semaines. Chaque connexion apparaît désormais dans le journal avec ID ... (serial ...).
  3. Repérez les retardataires. Une connexion via authorized_keys est journalisée sans identifiant de certificat, par exemple Accepted publickey for deploy from 127.0.0.1 port 58770 ssh2: ED25519 SHA256:tT5X32Wds5InYaLSh7mV+QRqAtxA4KXwlKV3hkf0YkI. Il s’agit généralement de processus automatisés : runners de CI, tâches de sauvegarde, surveillance. Donnez à chacun son propre certificat, à courte durée de vie ou étroitement restreint.
  4. Désactivez les fichiers de clés avec AuthorizedKeysFile none dès que plus rien de légitime ne les utilise. Prévoyez un accès de secours (« bris de glace »), par exemple un accès console ou une AC d’urgence distincte conservée hors ligne, pour le jour où le service de signature est en panne.
  5. Traitez le côté hôte séparément. Signez les clés d’hôte et ajoutez la ligne @cert-authority au fichier global des clients, /etc/ssh/ssh_known_hosts. Les anciennes entrées known_hosts par hôte peuvent rester. Elles continuent de fonctionner jusqu’à ce que les clés d’hôte changent.

L’exploitation au quotidien

  • Émettez les certificats depuis votre fournisseur d’identité. Un service de signature vérifie la connexion SSO de l’utilisateur (OIDC) et renvoie un certificat portant ses principaux, valable quelques heures. Personne ne manipule la clé de l’AC, et quitter l’entreprise met fin à l’accès SSH au plus tard le lendemain, sans toucher à aucun serveur.
  • Gardez si possible la clé utilisateur sur du matériel. ssh-keygen -t ed25519-sk crée une clé adossée à un authentificateur FIDO2. Les certificats fonctionnent avec elle comme avec n’importe quelle autre clé.
  • Effectuez la rotation de l’AC avec chevauchement. Ajoutez la nouvelle AC utilisateur en deuxième ligne de TrustedUserCAKeys, ou la nouvelle AC d’hôte en deuxième ligne @cert-authority. Émettez à partir de la nouvelle AC, puis retirez l’ancienne.
  • Surveillez aussi le côté hôte. Signez de nouveau les clés d’hôte avant l’expiration de leurs certificats. Un certificat d’hôte expiré oblige les clients à se rabattre sur les clés d’hôte ordinaires, qu’ils n’ont généralement pas :
ssh vers un hôte dont le certificat a expiré hier
Certificate invalid: expired
No ED25519 host key is known for web01.example.test and you have requested strict checking.
Host key verification failed.

Pour examiner un fichier de clé ou de certificat sans rien exécuter, l’inspecteur de clés SSH décode les clés OpenSSH et affiche leur type, leur taille et leur empreinte. L’audit de serveur SSH vérifie ce que propose un serveur, y compris ses algorithmes de clé d’hôte et de certificat, ainsi que l’échange de clés post-quantique.

Sources

  • Pages de manuel d’OpenSSH : ssh-keygen(1) (sections CERTIFICATES et KEY REVOCATION LISTS), sshd_config(5) (TrustedUserCAKeys, AuthorizedPrincipalsFile, RevokedKeys, HostCertificate, PerSourcePenalties), sshd(8) (formats authorized_keys et known_hosts).
  • draft-ietf-sshm-cert : format des certificats SSH (successeur du fichier PROTOCOL.certkeys d’OpenSSH). PROTOCOL.krl : le format KRL.
  • Notes de version d’OpenSSH : 5.4 (certificats), 6.2 (KRL), 9.8 (PerSourcePenalties).

Toutes les sorties de cette page proviennent d’OpenSSH 10.0p2 (paquets Debian 13) dans debian:trixie-20261005-slim, le 20 octobre 2026, avec des clés de test générées dans le conteneur.