ADR-137 — Identité machine stable comme principal de certificat : empreinte de la host key, CA-attestée, découplée du hostname/IP
Statut : Accepté (2026-07-29, décision BDFL). RFC de direction
(ADR-015).
S’appuie sur ADR-109 (certificats SSH + CA),
ADR-108 (air-keystore, host key),
ADR-121 (provisioning first-boot),
ADR-134 (migration host key → keystore),
ADR-029 (nommage).
Catégorie : raffinement de sémantique d’ADR-109 (couche 2 + packaging). Compagnon de la séquence d’incréments ADR-109.
Contexte
Un certificat d’hôte SSH standard lie une clé à des principals = noms d’hôte. Or un nom
d’hôte (ou une IP, ou un nom de domaine) change dans la vie d’une machine. Une
authentification par certificat adossée au hostname casse dès qu’on renomme ou ré-adresse la
machine, alors que la machine — physiquement, logiquement — est la même.
Directive BDFL (2026-07-29). « Ancrer l’identité d’une machine sur une clé unique et stable, pas sur un hostname/IP mutable. À l’installation du keystore, initialiser réellement le keystore et générer la clé qui identifie le host. Le certificat porte cette clé dans le champ principal ; la machine peut changer de nom, le certificat reste valable. »
L’insight : dissocier l’adresse par laquelle on joint une machine (mutable, sans importance cryptographique) de son identité (stable). SSH prouve déjà, au KEX, la possession de la host key ; il suffit d’ériger l’empreinte de cette host key en identité de principal.
Décision
1. Identité machine fpr(K_pub) = empreinte SHA256 de la host key
fpr(K_pub) est l’empreinte SHA256 (représentation canonique SHA256:<base64 sans padding>,
forme OpenSSH) de la host key Ed25519 de la machine (celle de hostkeys.aircfg, [ADR-134]).
Aucune clé nouvelle : la host key est l’ancre d’identité, son empreinte est fpr(K_pub).
fpr(K_pub) est stable pour la vie de la machine (indépendant du hostname/IP/domaine).
2. Émission : valid_principals = [fpr(K_pub)]
Un certificat d’hôte émis pour une machine porte, en valid_principals, l’empreinte de la
clé qu’il certifie (fpr(K_pub)), au lieu d’un hostname. (Chemin
d’émission : incrément « émission » d’ADR-109 + CLI air-keysign.)
3. Vérification client : invariant de cohérence de soi + confiance CA
Le client (Air) vérifie un certificat d’hôte ainsi (fail-closed) :
- Signé par une CA d’hôtes de confiance (magasin
trusted_ca.aircfg, périmètre Host) — c’est la CA qui autorise : une machine CA-signée est un membre légitime de la flotte. - validité temporelle, non révoqué, options critiques connues (déjà [ADR-109] Inc.1).
- Cohérence de soi —
fpr(K_pub)∈valid_principalsoùK_pub= la clé certifiée : le client recalcule l’empreinte de la clé d’hôte que le serveur a prouvé posséder au KEX, et exige qu’elle figure dans les principals. Le certificat doit donc attester sa propre clé — il ne peut pas prétendre une identité qui n’est pas celle de la clé certifiée.
Le check hostname d’[ADR-109] Inc.1 (#522) est remplacé par cet invariant (3). La CA autorise ; l’identité est vérifiée, pas seulement étiquetée.
Pourquoi (3) plutôt qu’un principal purement étiquette (non vérifié) ? Coût nul (une empreinte à recalculer) contre un gain réel : l’identité machine devient une propriété vérifiée et honnête (robuste à une émission boguée/malveillante qui logerait un mauvais principal), et le journal « connecté à la machine
fpr(K_pub)» est fiable. Doctrine Air : vérifie, ne fais pas confiance — la confiance CA sert à l’autorisation, pas à l’intégrité de l’identité.
Le KEX prouvant déjà la possession de la host key, le triplet **{CA vouche} + {KEX prouve la clé}
- {principal == empreinte de cette clé}** identifie quelle machine (par
fpr(K_pub), stable) a répondu, sans épingler N machines : on épingle une CA.
4. Initialisation à l’installation (postinst)
Le postinst du paquet (keystore / air-sshd) initialise le keystore et génère la host key
si absente (Ed25519, hostkeys.aircfg en 0600) — donc fpr(K_pub) existe dès l’installation, sans
intervention. Comble le trou de provisioning ([ADR-121], postinst binaires-seuls). Idempotent :
une host key présente n’est pas régénérée (l’identité ne doit jamais changer sous les pieds).
5. Interop OpenSSH : via HostkeyAlias (pas Hostname)
Un client OpenSSH standard peut interopérer, en découplant l’adresse de l’identité avec
HostkeyAlias :
Host ma-machine
Hostname 203.0.113.10 # l'ADRESSE (mutable)
HostkeyAlias SHA256:dadSVa461jwd+hn0ed1Gdck5rIGRetfmDZNysbonqGc # l'IDENTITÉ = fpr(K_pub) (stable)
Le cert Air portant fpr(K_pub), OpenSSH se connecte par IP et matche le
principal contre le HostkeyAlias. Renommage/re-IP ⇒ on change Hostname, on garde
HostkeyAlias ⇒ l’auth tient. Hostname ne convient pas (OpenSSH y résout l’adresse). Le
client Air n’a besoin d’aucun alias : il recalcule fpr(K_pub) depuis la clé (invariant 3).
(Réserve : le matching exact de principal contre HostkeyAlias peut varier selon la version
d’OpenSSH — à valider sur une install réelle, cf. incrément interop d’ADR-109.)
Réserve CONFIRMÉE et tranchée (BDFL, 2026-07-30) — interop cert d’hôte par
HostkeyAliasINOPÉRANTE, documentée comme limite. L’incrément interop d’ADR-109 (Inc.5) l’a prouvé sur OpenSSH 10.2p1 : le client abaisse la casse duHostkeyAliasavant de le comparer auxvalid_principalsdu certificat. OrM = SHA256:<base64>est sensible à la casse (§3 : abaisser la casse ferait collisionner des identités) ⇒ le principal ne matche jamais (Certificate invalid: name is not a listed principal). Prouvé par contrôle (même montage, principal en minuscules signé par une CAssh-keygenpure ⇒ réussit). Décision BDFL : documenter la limite, ne PAS émettre de second principal en minuscules — Air n’affaiblit pas son modèle d’identité (résistance aux collisions deM) pour contourner une perte d’information d’un client tiers. Ce qui fonctionne : l’émission d’Air (ssh-keygen -Llit le cert, principal =M), la vérification Air-native (le client Air recalcule l’empreinte byte-à-byte, sans alias — invariant 3), et l’interop cert utilisateur OpenSSH (TrustedUserCAKeys, prouvée). Seul le pont@cert-authority+HostkeyAliaspour un client OpenSSH vérifiant un cert d’hôte Air est hors de portée — limite d’OpenSSH, pas d’Air.Chemin opt-in (à la demande de l’opérateur, [ADR-115] §6). La CLI
air-keystoreoffre, explicitement et jamais par défaut,cert issue-host --openssh-hostkey-alias-compatqui ajoutelowercase(M)comme second principal (le cert gardeMexact pour Air). Un client OpenSSH configuré@cert-authority+HostkeyAliasmatche alors la forme abaissée. Coût assumé et documenté :lowercase(M)a une résistance aux collisions réduite (repli de casse base64) ; Air ne l’utilise jamais pour sa propre vérification (invariant 3,Mexact). L’opérateur qui a besoin de l’interop l’active en connaissance de cause ; le défaut resteMexact seul.
Conséquences
Positives. Identité stable (renommage/re-IP sans casse) ; flotte simple (épingler une
CA, pas N machines) ; identité vérifiée (invariant 3), pas décorative ; provisioning à
l’install ; interop OpenSSH possible (HostkeyAlias).
Coûts / risques. Divergence de sémantique vs le host-cert « principal=hostname » d’OpenSSH
(atténuée par HostkeyAlias). L’fpr(K_pub) reste lié à la host key : une rotation de host key change
fpr(K_pub) (comme une réinstallation change l’identité — comportement voulu ; rotation = re-publication).
La confiance repose sur la CA : sa compromission autoriserait toute machine (inhérent au modèle
CA, hors périmètre).
Emplacements d’implémentation
| Morceau | Où | Incrément |
|---|---|---|
Helper fpr(K_pub) (forme SHA256:…) | air-sshd::host_identity (ou air-keystore) | identité machine |
Client : check hostname → fpr(K_pub), valid_principals | air-sshd/src/host_trust.rs (modifie #522) | identité machine |
postinst : keystore init + host-key-gen idempotent | packaging (maintainer scripts) | identité machine |
Émission : valid_principals = [fpr(K_pub)] | orchestration d’émission | ADR-109 Inc.4 + CLI |
Interop OpenSSH réel (HostkeyAlias) | test | ADR-109 Inc.5 |
Alternatives rejetées
- Clé « machine-id » dédiée (distincte de la host key) : deux clés à gérer pour une seule identité ; la host key est déjà l’ancre. Refusé (BDFL).
- Principal purement étiquette (non vérifié) : décoratif, non robuste à une émission menteuse. Refusé au profit de l’invariant (3).
- Pinning par-machine (le client épingle
fpr(K_pub)de chaque serveur) : ne passe pas à l’échelle d’une flotte ; la confiance CA suffit. Refusé (BDFL). - Principal = hostname (OpenSSH classique) : casse au renommage — le problème même que résout cet ADR.