Pourquoi des certificats plutôt que des clés
SSH fait confiance au premier contact. Connectez-vous à une nouvelle machine et il vous montre une empreinte en vous demandant si vous êtes sûr. Personne ne vérifie. Tout le monde tape yes. C’est la même habitude que de passer outre un avertissement du navigateur, et c’est exactement ce dont une machine placée au milieu a besoin que vous fassiez.
Dans l’autre sens, ce n’est pas mieux. Chaque serveur garde un fichier authorized_keys par utilisateur, et chacun grossit. Des clés sont ajoutées et jamais retirées. Quelqu’un s’en va, et sa clé reste sur une machine dont personne ne se souvient jusqu’au jour où ça compte. Reconstruisez un serveur et chaque client crie que la clé d’hôte a changé, alors les gens apprennent aussi à effacer l’avertissement.
Une autorité de certification SSH met fin aux deux. L’AC signe la clé de chaque hôte, et les clients font confiance à l’AC, pas à une liste d’empreintes. L’AC signe la clé de chaque utilisateur pour quelques heures, et les serveurs font confiance à l’AC, pas à une liste de clés. Rien à accepter à la première connexion, rien à nettoyer quand quelqu’un part, et un certificat utilisateur qui meurt de lui-même d’ici demain.
C’est une installation différente du côté X.509 de step-ca couvert dans Votre propre autorité de certification : step-ca dans un conteneur, et c’est pourquoi elle a son propre article. Même conteneur, autres clés, autres fichiers sur chaque machine. Tout ce qui suit a été lancé avec step-ca 0.30.2 et OpenSSH 10.0 dans un pod Podman.
| Sans AC | Avec step-ca | |
|---|---|---|
| Un client rencontre un nouveau serveur | Affiche une empreinte, vous tapez yes | Vérifie le certificat d’hôte contre l’AC, ne dit rien |
| Un serveur est reconstruit | Chaque client prévient que la clé a changé | Signez la nouvelle clé, les clients ne remarquent rien |
| Un utilisateur obtient un accès | Sa clé va dans authorized_keys sur chaque serveur | L’AC signe sa clé, chaque serveur l’accepte |
| Un utilisateur s’en va | Retrouver et supprimer sa clé sur chaque serveur | Cesser de signer ; son certificat est mort en 16 heures |
| Ce que garde chaque serveur | Un authorized_keys par utilisateur | Une clé publique d’AC |
Activer l’AC SSH
Les clés SSH sont créées à la première mise en place de step-ca, ou jamais. Posez DOCKER_STEPCA_INIT_SSH=true au premier démarrage1, et le journal montre deux clés de plus :
SSH Host CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy...
SSH User CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy...
| Clé | Signe | Qui lui fait confiance |
|---|---|---|
| AC d’hôte | La clé d’hôte de chaque serveur | Chaque client, dans known_hosts |
| AC utilisateur | La clé de connexion de chaque personne | Chaque serveur, dans sshd_config |
Il ajoute aussi un provisioner sshpop, qui permet ensuite à un hôte de renouveler son propre certificat. Si l’AC tourne déjà sans SSH, il n’y a aucun interrupteur à basculer. C’est une nouvelle AC.
Signer l’hôte
Prenez la clé d’hôte existante du serveur, signez-la, et rendez le certificat au serveur :
step ssh certificate --host --sign \
--provisioner admin --provisioner-password-file prov-pass \
nas.home.example ssh_host_ecdsa_key.pub
L’ordre compte. Avec les options du provisioner après le nom d’hôte et le fichier de clé, step a refusé :
too many positional arguments were provided in 'step ssh certificate <key-id> <key-file>'
Mettez toutes les options en premier et il signe. Le certificat, tel que le lit ssh-keygen -L :
| Champ | Valeur |
|---|---|
| Type | ecdsa-sha2-nistp256-cert-v01@openssh.com host certificate |
| Key ID | nas.home.example |
| Principals | nas.home.example |
| Validité | 30 jours, de 2026-10-09T18:34:30 à 2026-11-08T17:35:30 |
Le principal est le nom qu’un client doit utiliser pour se connecter. Connectez-vous par un nom qui ne figure pas sur le certificat et le client ne l’acceptera pas, donc signez tous les noms auxquels l’hôte répond.
Le serveur
Deux lignes dans sshd_config2 :
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub
TrustedUserCAKeys /etc/ssh/ssh_user_ca_key.pub
HostCertificate remet le certificat d’hôte aux clients, et sa clé « must match a private host key already specified »2. TrustedUserCAKeys est la clé publique de l’AC utilisateur, « trusted to sign user certificates for authentication »2. Lancez sshd -t avant de redémarrer. Une faute de frappe dans l’un des deux chemins et sshd dit Could not load host certificate, ce qu’il vaut bien mieux découvrir maintenant que depuis une session bloquée.
Il n’y a aucun authorized_keys sur le serveur de test. ls /home/damien/.ssh/authorized_keys renvoie No such file or directory, et les connexions ci-dessous fonctionnent quand même.
L’utilisateur
L’AC crée la paire de clés et la signe d’un coup :
step ssh certificate damien id_ecdsa \
--provisioner admin --provisioner-password-file prov-pass
| Champ | Valeur |
|---|---|
| Type | user certificate |
| Principals | damien |
| Validité | 16 heures |
| Extensions | permit-agent-forwarding, permit-port-forwarding, permit-X11-forwarding, permit-pty, permit-user-rc |
Seize heures, c’est tout l’intérêt. Ça couvre une journée de travail et ça meurt dans la nuit. Révoquer un utilisateur revient surtout à ne pas signer pour lui demain.
Le client
Une ligne dans known_hosts remplace toutes les empreintes qu’il collecterait autrement3 :
@cert-authority *.home.example ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAy...
Elle dit : tout hôte sous home.example dont le certificat est signé par cette clé est authentique. C’est le marqueur @cert-authority qui en fait une ligne d’AC plutôt que la clé d’un seul hôte.
Ce que le test a montré
Les quatre depuis un client qui ne détient rien d’autre que le certificat utilisateur et cette unique ligne de known_hosts, avec StrictHostKeyChecking=yes, pour que rien ne puisse être accepté à la volée :
| Test | Résultat |
|---|---|
Clé signée avec son certificat, en tant que damien | logged in as damien, sortie 0 |
| Même clé, certificat retiré | Permission denied (publickey,password,keyboard-interactive) |
Certificat utilisé pour se connecter en root | Permission denied : le certificat ne nomme que damien |
| Client sans la ligne de l’AC d’hôte | Host key verification failed. |
Le troisième est celui que les gens ratent. Un certificat n’ouvre pas tous les comptes de la machine, seulement les principals qui y sont écrits. Une clé dans le authorized_keys de quelqu’un ne sait pas faire ça.
Le renouvellement
Un hôte renouvelle son propre certificat avec la clé qu’il détient déjà. C’est le provisioner sshpop, qui « allows a client to renew, revoke, or rekey an SSH certificate using that certificate for authentication with the CA »4. Sans mot de passe5 :
step ssh renew --force ssh_host_ecdsa_key-cert.pub ssh_host_ecdsa_key
| Numéro de série | Valide jusqu’au | |
|---|---|---|
| Signé | 15166325869329416997 | 2026-11-08 17:35:30 |
| Renouvelé | 6678957789797316768 | 2026-11-08 17:35:59 |
Un nouveau numéro de série et 30 jours neufs. Mettez-le sur une minuterie hebdomadaire et l’hôte n’expire jamais. Rechargez sshd ensuite, sinon l’ancien certificat reste en mémoire.
Les utilisateurs renouvellent en redemandant. Un step ssh certificate par jour, ou à la première connexion de la journée, et c’est tout le travail.
Là où ça coince
| Cas | Ce qui se passe | Que faire |
|---|---|---|
| AC en panne | Les certificats existants continuent de marcher ; personne n’en obtient de nouveau | Faire tourner l’AC sur quelque chose qui reste debout ; les utilisateurs ont 16 heures de marge |
| Le certificat d’hôte expire | Les clients refusent l’hôte, comme un inconnu | Une minuterie de renouvellement, et sshd rechargé après |
| Un utilisateur doit partir aujourd’hui, pas demain | Son certificat marche jusqu’à son expiration | RevokedKeys dans sshd_config accepte une liste de clés révoquées2 |
| Nom d’hôte absent des principals | Le client refuse l’hôte | Signer tous les noms auxquels il répond |
| AC SSH non créée à l’installation | Aucun moyen de l’ajouter | Une nouvelle AC, montée avec SSH activé |
| Les clés SSH de l’AC volées | Qui les détient peut se connecter partout et se faire passer pour n’importe quel hôte | Garder l’AC sur sa propre machine, sauvegardée, hors ligne si possible |
Le dernier est le prix à payer. Chaque fichier authorized_keys était son propre petit risque. Il y en a maintenant un gros, à un seul endroit. C’est mieux, parce qu’un seul endroit peut être surveillé et verrouillé, mais seulement si quelqu’un le fait.
La confiance là où on peut la vérifier
L’invite d’empreinte et le fichier authorized_keys ont un point commun. Les deux demandent à une personne de prendre une décision de confiance sans aucune information, chaque jour, pour toujours. Les gens ne sont pas bons à ça et ne le seront jamais, alors ils cessent de décider et se mettent à taper yes.
Une AC déplace cette décision à un seul endroit, une seule fois, là où elle peut être prise correctement. C’est toute l’idée, et c’est la même que derrière chaque norme qui vaut la peine : faire la chose soigneusement une fois, l’écrire, et cesser de demander aux gens de la faire de tête cent fois par semaine. La machine vérifie la signature. Vous vérifiez l’AC. C’est une division du travail qui tient.
smallstep/certificates — docker/entrypoint.sh —
DOCKER_STEPCA_INIT_SSH=trueajoute--sshàstep ca init. ↩︎OpenBSD manual — sshd_config(5) —
HostCertificate: « The certificate’s public key must match a private host key already specified by HostKey » ;TrustedUserCAKeys: « public keys of certificate authorities that are trusted to sign user certificates for authentication » ;RevokedKeys: « Keys listed in this file will be refused for public key authentication ». ↩︎ ↩︎ ↩︎ ↩︎OpenBSD manual — sshd(8), SSH_KNOWN_HOSTS FILE FORMAT — le marqueur « @cert-authority » « to indicate that the line contains a certification authority (CA) key ». ↩︎
Smallstep — Provisioners, SSHPOP — « An SSHPOP provisioner allows a client to renew, revoke, or rekey an SSH certificate using that certificate for authentication with the CA. » ↩︎
Smallstep — step ssh renew — la commande utilisée pour renouveler le certificat d’hôte. ↩︎