Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-162 — Amorçage de l’autorité locale : la CA naît à l’installation, et l’utilisateur naît certifié

Statut : Proposé (2026-08-21). Décide quand l’autorité de certification d’une machine Air est créée, comment le premier utilisateur est enrôlé, et ce que devient authorized_keys.

Catégorie : Identité et amorçage, couche 2. S’appuie sur ADR-109 (format de certificat, CA, révocation — fait), ADR-121 (séquence first-boot), ADR-094 (publickey Ed25519 moderne-only) et ADR-150 (manifeste, octroi, arbitrage).

Contexte

Ce qui est déjà là — relevé contre le code le 2026-08-21

CapacitéÉtat
Format de certificat, vérification de signatureair-ssh-proto (sans-IO, fuzzé)fait (ADR-109)
Création de CA --scope host|user|anyair-keystore ca createfait
Émission hôte et utilisateur, avec --principals, --validity, --serialair-keystore cert issue-host / issue-userfait
Vérification, --principal obligatoire pour un cert utilisateurair-keystore cert verifyfait
Autorités acceptées et portée (is_trusted_authority(ca, needed))air-keystorefait
Révocation par clé et par plage de numéros de sérieair-keystore (module krl)fait
Clé signant les octrois d’ADR-150machine_key.aircfg, air-keystore machine-key initfait
Magasin personnel à visibilité resserréefor_personal_store()fait

Ce qui manque, et c’est tout ce dont traite cet ADR

Rien ne crée d’autorité à l’installation. machine-key init initialise la clé qui signe les octrois — pas une CA. La CA existe comme verbe manuel (ca create), donc comme geste d’administration que personne n’a de raison de faire, et que rien ne rappelle.

Conséquence mesurable : air-sshd authentifie ses utilisateurs par une liste plate<home>/.config/air/sshd/authorized_keys, un fichier binaire par compte. Toute la machinerie d’autorité, de portée et de révocation existe et sert aux hôtes, pas aux utilisateurs. La moitié du mécanisme est écrite du mauvais côté de la connexion.

Ce qu’on refuse d’imiter

La distribution de clés publiques par fichier. Autoriser un utilisateur sur N machines demande N éditions, et le révoquer en demande N autres — dont on n’a aucune preuve qu’elles ont eu lieu. C’est le défaut qu’ADR-109 nomme déjà, et qu’il corrige pour les hôtes sans l’avoir fait pour les comptes.

Le mot de passe comme secours. Un système sans mot de passe qui garde un mot de passe « au cas où » n’est pas un système sans mot de passe : c’est un système dont le mot de passe est le moins souvent employé, donc le plus mal choisi.

Décisions

D1 — L’autorité naît à l’initialisation, jamais à la demande

L’initialisation d’une machine Air crée l’autorité locale et l’inscrit parmi les autorités acceptées. Ce n’est pas un geste d’administration optionnel : une machine sans autorité ne sait enrôler personne, donc n’a pas d’identité à offrir.

Deux autorités, pas une (tranché le 2026-08-21) : une de portée host, une de portée user. air-keystore ca create --scope le permet déjà, donc le choix ne coûte aucune ligne de code — seulement une clé de plus à produire et à sauvegarder.

La raison qui décide : une CA unique rend un contrôle existant inerte

is_trusted_ca vérifie deux choses :

#![allow(unused)]
fn main() {
entry.ca_key.ed25519_bytes() == ca_key.ed25519_bytes() && entry.scope.covers(needed)
}

Or AirCaScope::Any couvre tout. Avec une autorité unique de portée any, le second terme de la conjonction rend toujours vrai : le contrôle de portée a l’air de garder et ne garde rien. Ce serait introduire volontairement, à l’installation et sur le chemin d’authentification, la classe de défaut que les deux règles NO GO de ce projet poursuivent.

Le code a d’ailleurs déjà pris la peine de distinguer : AirCaScope::User « remplace authorized_keys », AirCaScope::Host « remplace le TOFU known_hosts ». Deux remplacements distincts ; les confondre sous une même clé efface cette distinction.

L’asymétrie des coûts

Deux CA maintenant coûtent une clé de plus. Passer de une à deux plus tard impose un réenrôlement complet. Quand un choix est gratuit dans un sens et coûteux dans l’autre, on prend le sens gratuit.

Ce que cette décision n’achète PAS, et qu’il faut dire

Sur un poste personnel, le cloisonnement au repos est largement illusoire : les deux ca_key.aircfg vivent dans le même répertoire, sous la même protection racine. Qui obtient root obtient les deux. Prétendre que deux clés protègent d’une compromission locale serait malhonnête.

Ce que deux CA achètent réellement :

  1. le contrôle de portée reste un contrôle, au lieu de devenir une tautologie ;
  2. les cycles de vie se séparent — un certificat d’hôte se réémet quand la machine est réinstallée, un certificat utilisateur quand une personne arrive ou part : deux cadences, deux clés ;
  3. l’option future est préservée sans réenrôlement — le jour où l’autorité utilisateur doit vivre ailleurs (hors ligne, sur une flotte, sur un support signant), elle se déplace.

Deux exigences qui accompagnent la décision

  • Les autorités portent un nom explicite (--key-id) : air-ca-host et air-ca-user, et non le défaut air-ca. Un cert inspect doit dire de quelle autorité vient un certificat sans qu’on ait à comparer des empreintes.
  • La topologie d’autorité est inscrite à l’installation, pour qu’un administrateur n’ait pas à la déduire du contenu du magasin.

D2 — L’utilisateur naît certifié, et sa clé privée ne naît pas sur la machine

À la création du premier compte, l’installateur :

  1. demande un support (clé USB, et à terme un support signant) ;
  2. y génère la paire — la privée naît sur le support et n’en sort jamais ;
  3. émet un certificat utilisateur depuis la CA user, dont les principals portent le compte ;
  4. dépose le certificat et la publique dans le magasin personnel du compte.

La machine ne détient donc aucun secret de l’utilisateur : elle détient de quoi vérifier, jamais de quoi se faire passer pour.

Ce que cet ADR n’exige pas : que le support signe. Un support de stockage simple rend un jeton au porteur — copiable sans trace. C’est un progrès net sur le mot de passe (long, aléatoire, jamais tapé, jamais réutilisé ailleurs) et une régression sur un point (un vol ne laisse pas d’indice). La surface est conçue pour que le passage à un support signant (FIDO2, carte, TPM) ne change aucune signature : c’est un fournisseur de clé de plus.

D3 — air-sshd vérifie contre l’autorité, plus contre une liste

authorized_keys cesse d’être la source d’autorisation : une clé est admise si elle porte un certificat valide, émis par une autorité acceptée dont la portée couvre ce compte, et non révoqué. is_trusted_authority(ca, needed) et le module krl font déjà ce travail.

authorized_keys reste lisible — un utilisateur qui y dépose une clé sait ce qu’il fait, et l’interopérabilité avec le reste du monde en dépend. Mais il devient une route parallèle, au sens du patron additif, appelée à tomber à un descellement.

Le gain se mesure à la révocation : une plage de numéros de série retire un accès partout à la fois, au lieu de N éditions dont rien ne prouve qu’elles ont eu lieu.

D4 — Le certificat désigne l’octroi, il ne le porte pas

Un certificat SSH peut transporter des options critiques et des extensions. ADR-109 §2 rappelle la règle du format : une option critique inconnue fait rejeter le certificat, une extension inconnue est ignorée.

Il s’ensuit que loger des Entitlements d’Air dans une extension serait dangereux — une implémentation tierce les ignorerait en silence et ouvrirait une session non bornée. Les y loger en option critique serait sûr mais rendrait le certificat inutilisable par un OpenSSH standard, qui le rejetterait : on perdrait l’interopérabilité qu’ADR-109 §2 a explicitement choisie.

Donc : le certificat reste standard et interopérable, et l’octroi vit côté machine, désigné par le certificat. C’est exactement le modèle d’ADR-150, où Grant.manifestSha256 lie sans transporter.

D5 — La présence humaine se prouve à l’octroi, pas à l’invocation

C’est la décision qui rend l’automatisation possible sans mot de passe.

Élever ses droits — accorder une autorité nouvelle — exige la présence : le support signe. Exercer une autorité déjà accordée n’exige rien : un humain a déjà décidé, une fois, et sa décision est un artefact durable, révocable, daté.

Un script nocturne s’exécute donc parce que son octroi a été signé avant, pas parce qu’un secret traîne quelque part. Et la question que les mots de passe ne savaient pas répondre — « quand a-t-il changé pour la dernière fois ? » — devient une date dans l’artefact.

Le curseur est par octroi, pas global. Un octroi porte un drapeau « exige la présence à l’invocation » : sauvegarde nocturne non, modification d’octroi ou changement de clé d’hôte oui. La consultation se fait par la couture SecurityManager d’[ADR-089], qui est déjà appelée au moment de l’opération — donc sans qu’aucune signature publique ne bouge.

Le résidu, dit franchement : un attaquant devant une console déverrouillée peut invoquer ce qui est déjà accordé. Il ne peut pas obtenir d’autorité ambiante — il n’y en a pas — ni accorder quoi que ce soit de neuf. Aucun schéma ne supprime ce résidu, sauf exiger la présence partout, ce qui supprime l’automatisation. Le drapeau par octroi est le seul endroit honnête où placer ce curseur.

D6 — Le recouvrement est décidé à l’installation, et ce n’est pas un mot de passe

Une machine dont l’unique support est perdu est une machine perdue. L’installateur impose un choix, et l’inscrit :

  • une seconde clé enrôlée à l’installation, rangée ailleurs ; ou
  • un code de recouvrement affiché une fois, à usage unique, révoqué à l’emploi ; ou
  • l’acceptation écrite que la perte du support impose une réinstallation.

Ne pas choisir n’est pas une option : le défaut serait découvert le jour de la perte.

Ce que cet ADR ne décide pas

  • Le support signant (FIDO2, PKCS#11, TPM) : un ADR à lui seul. D2 garantit seulement que son arrivée ne changera pas de signature.
  • Le sort d’[ADR-101] et d’air-admin-gate, qui reposent sur le mot de passe administrateur. Ils deviennent caducs, mais leur retrait est une campagne à part.
  • La CA de flotte. ca_key.aircfg est un secret local : qui a la racine forge ce qu’il veut. Acceptable sur un poste personnel, insuffisant sur une flotte.
  • Le format du magasin sur support amovible.

Conséquences

  • L’initialisation gagne deux étapes (créer les autorités, enrôler le premier compte) et une question obligatoire (le recouvrement).
  • air-sshd gagne un chemin de vérification par autorité ; authorized_keys devient une route parallèle, tracée comme telle.
  • La révocation devient centrale et prouvable.
  • La machine cesse de détenir un secret capable d’authentifier ses utilisateurs.

Alternatives rejetées

  • Garder authorized_keys comme source. Rejetée : la révocation y est un vœu.
  • Loger les octrois dans une extension de certificat. Rejetée en D4 : une extension inconnue est ignorée, donc une implémentation tierce ouvrirait une session non bornée.
  • Une CA unique any. Rejetée en D1 : sa portée Any couvrant tout, elle rendrait scope.covers(needed) toujours vrai — un contrôle existant deviendrait une tautologie. Et revenir à deux imposerait un réenrôlement complet.
  • Exiger la présence à chaque invocation. Rejetée en D5 : elle supprime l’automatisation, et un mot de passe retapé ne prouve pas davantage l’intention.
  • Un mot de passe de secours. Rejetée : c’est le mot de passe le moins employé, donc le plus mal choisi.

Licence du document : MPL 2.0