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-108 — air-keystore : manager du cycle de vie des clés et secrets (couche 1), face privilégiée air-keysign, interop OpenSSH

Statut : Accepté (2026-07-26, décision BDFL). RFC de direction (ADR-015). S’appuie sur ADR-077 (managers de domaine couche 1, médiateurs), ADR-034 (discipline crypto ; air-crypto = primitives pures), ADR-096 (magasin authorized_keys binaire), ADR-040 (enveloppe d’artefact AIRCFGv1 + Cap’n Proto), ADR-073 (doctrine config binaire), ADR-089 (réservation SecurityManager), ADR-019 (modèle d’erreurs), ADR-029 (nommage), ADR-101 (gate mot de passe admin), ADR-088 (std/PAL sans libc C). Compagnon d’ l’ADR Certificats SSH + CA (à venir, vague 1 #2) : celui-ci gère le matériel de clé ; l’ADR certificats gère le format wire et l’orchestration d’émission.

Catégorie : Architecture couche 1 (nouveau manager de domaine) + couche 2 (service privilégié air-keysign). Premier ADR de la vague 1 issue du triage des écarts OpenSSH.

Contexte

Le triage OpenSSH (serveur + client, 2026-07-26) a fait émerger un besoin transverse qu’aucune crate ne porte aujourd’hui : le cycle de vie des clés et secrets. Il est aujourd’hui éparpilléair-sshd charge sa clé d’hôte via air-crypto (AirSigningKey) en direct et lit authorized_keys via air-filesystem ([ADR-096]) ; rien ne gère la génération, l’import/export de formats de clés, la péremption, la révocation, ni un magasin d’hôtes connus (known_hosts). Or presque tous les [A] retenus en dépendent : certificats/CA (matériel de clé), RevokedKeys, known_hosts client, agent maison, hostbased.

air-crypto ne doit pas absorber cela : par [ADR-034] c’est une bibliothèque de primitives pures, sans I/O ni stockage (Ed25519 sign/verify, X25519 ECDH, hash/AEAD/Argon2/HMAC, aléa). Un composant qui gère l’état des clés (fichiers, formats, durées de vie) est, par [ADR-077], un manager de domaine couche 1 — une surface objet médiatrice, distincte des primitives.

Directive BDFL (2026-07-26). « Un seul air-keystore. On réutilise notre format binaire. La clé d’hôte privée : on en parle [→ décision 5]. Interop avec OpenSSH : oui, nécessaire. » Et, sur la doctrine de sourcing : « en crypto on ne réinvente pas, on utilise ce qui est fait — mais os-free ; les formats, on peut les faire nous-mêmes sérieusement » ([[doctrine-sourcing-crypto]] : formats/protocoles = DIY à faible risque, on possède).

Décision

1. air-keystore est un manager de domaine couche 1, stateful

Une seule crate air-keystore (pas d’éparpillement), manager de domaine au sens [ADR-077] : surface objet médiatrice, consommée par les toits (air-sshd, air-ssh, CLI, agent), jamais des free-functions. Stateful : elle fait de l’I/O — exclusivement via la couche 1 (air-filesystem pour lire/écrire les magasins), jamais la couche 0 en direct (règle dure ; un manque en couche 1 = additif couche 1 d’abord). Elle délègue toute primitive crypto à air-crypto (génération de clés, signature, dérivation, chiffrement d’enveloppe) — air-crypto reste primitives pures. Sous-modules nets : keys (matériel + génération), formats (sérialisation/interop), store (magasin scellé), hosts (known_hosts), revocation, ca (matériel CA). Monnaie d’erreurs AirError ([ADR-019]) ; no_std + alloc ; secrets zeroize en mémoire. Coverage 100 % (couche 1).

2. Stockage : format binaire scellé, réutilisé partout

Le magasin interne réutilise l’enveloppe d’artefact AIRCFGv1 + Cap’n Proto ([ADR-040]) et le motif du magasin authorized_keys binaire ([ADR-096]) — cohérent avec la doctrine de config binaire ([ADR-073]) et la base de comptes binaire. Écriture atomique, permissions 0600 pour les magasins de secrets. Aucun secret en clair texte. Les secrets au repos peuvent être chiffrés sous une clé dérivée (Argon2id via air-crypto) gardée par le [gate admin ADR-101] selon le magasin.

3. Interop OpenSSH requise — aux frontières, pas au cœur

air-keystore lit et écrit les formats OpenSSH : clés publiques (ssh-ed25519 AAAA…), clés privées (-----BEGIN OPENSSH PRIVATE KEY-----), PEM, et certificats OpenSSH. Ce n’est pas seulement notre format binaire : Air doit coexister avec l’écosystème (migration de clés existantes, ssh/git classiques, déploiement). Frontière nette : l’interop vit dans le sous-module formats (import/export) ; le stockage interne reste binaire scellé (§2). Ces parseurs acceptent des données externesfuzzing obligatoire (cargo-fuzz), parsing schema-first, zéro unwrap (Principe 3). Les formats étant du DIY à faible risque ([[doctrine-sourcing-crypto]]), Air les possède (pas de dépendance qui traînerait du C).

4. Périmètre fonctionnel

Génération (délègue air-crypto) · import/export (OpenSSH/PEM ↔ binaire scellé) · lookup · péremption (dates de validité, listage des clés/certs expirés) · révocation (liste de révocation, RevokedKeys, vérification d’appartenance) · magasin d’hôtes connus known_hosts (TOFU, empreintes, rotation UpdateHostKeys). Couvre ainsi, côté serveur : clé d’hôte, vérification CA, clés révoquées ; côté client : known_hosts, clés utilisateur, certificats client, et le futur agent maison. La vérification d’autorisation d’une clé (comparaison de secret) doit rester constant-time (dudect, prolonge dudect_authorization d’air-sshd, [[doctrine-sourcing-crypto]]).

5. Deux faces : bibliothèque non privilégiée + service air-keysign

La clé d’hôte privée et les signatures sensibles (hostbased) ne doivent jamais transiter par un binaire setuid (leçon OpenSSH ssh-keysign). air-keystore a donc deux faces :

  • Face bibliothèque non privilégiée (couche 1) — le gros de l’usage : génération, formats, lookup, known_hosts, révocation, matériel non secret.
  • Face service privilégiée air-keysign (couche 2, service socket-activé, root ou capability ciblée, jamais setuid) — détient les clés privilégiées et signe à la demande via une interface oracle-safe : elle valide la structure du défi (lié session_id/hôte/utilisateur — RFC 4252 §9 pour hostbased), refuse de signer des octets arbitraires, et restreint les appelants (peer credentials du socket Unix). Nécessaire seulement pour hostbased client et les opérations sur la clé d’hôte ; jamais sur le chemin publickey ordinaire. Ce service est un livrable packagé ultérieur (couche 2) ; le présent ADR fixe le contrat (2 faces, oracle-safe, no-setuid).

6. Frontières actées

  • Format wire des certificats SSH = couche 2 (air-ssh-proto), pas le keystore. La CA est une orchestration : keystore (détient la clé CA + cycle de vie/ révocation des certs émis) + air-crypto (signature) + air-ssh-proto (format). Pas de monolithe « CA ». (Détail dans l’ADR certificats, vague 1 #2.)
  • air-crypto reste primitives pures — jamais d’I/O ni de stockage (ça vit ici).
  • SecurityManager ([ADR-089]) reste distinct : il porte la politique (Allow/Deny) et consomme le keystore ; il ne le contient pas. De même, le futur air-sandbox = mécanisme, orthogonal.

7. Consommateurs et report en vague 2

Consommateurs prévus : air-sshd (clé d’hôte, autorisation, vérification CA, révocation), air-ssh (known_hosts, clés/certs client, agent), le futur air-agent (agent maison keystore-backed — ADR séparé), et l’outillage. Hors de cet ADR (reportés en vague 2, déjà tranchés) : la CLI air-keystore (équivalent ssh-keygen élargi + mode JSON in/out agent-safe) et l’extraction d’air-json (crate partagée fuzzée). Ils consomment l’API définie ici et servent de test bout-en-bout (dogfooding). Le nommage suit [ADR-029] (termes hérités d’OpenSSH — known_hosts, authorized_keys — conservés verbatim ; surface conçue par Air = noms explicites sans abréviation).

Conséquences

Positives.

  • Un point unique pour les clés/secrets : fin de l’éparpillement (clé d’hôte, authorized_keys, à terme known_hosts/certs/révocation passent par le même médiateur, [ADR-077]).
  • Débloque la vague 1 : certificats/CA, RevokedKeys, known_hosts, hostbased, agent — tous s’appuient sur ce socle.
  • Interop OpenSSH ⇒ migration et coexistence réelles avec l’écosystème, sans renier le stockage binaire scellé interne.
  • Pas de setuid : la face air-keysign oracle-safe ferme la classe de vulnérabilités des helpers de signature privilégiés.
  • Cohérent doctrine : formats possédés (DIY faible risque, os-free), primitives déléguées à air-crypto (pas de réinvention de la crypto).

Négatives / coûts assumés.

  • Nouvelle crate couche 1 à porter au niveau des exigences (100 % coverage, fuzz des parseurs OpenSSH, dudect sur la comparaison de secrets, zeroize, // SAFETY: si unsafe). Coût réel, assumé (Principe 1).
  • Surface d’interop = surface d’attaque : parser des formats OpenSSH externes est un vecteur ⇒ schema-first + fuzz obligatoires ; le risque est borné, pas nul.
  • Service privilégié air-keysign à concevoir avec soin (couche 2, socket-activé, peer-cred, oracle-safe) — introduit un composant privilégié de plus dans le TCB, justifié par l’élimination du setuid.
  • Migration des consommateurs existants (air-sshd clé d’hôte + authorized_keys) vers le manager : incrément dédié, piloté pour ne rien casser (tests de non-régression).

Mise en œuvre (référence, hors décision). Incréments : (a) crate air-keystore couche 1 (sous-modules §1) — génération/formats/store/hosts/revocation ; (b) fuzz des parseurs OpenSSH + dudect autorisation ; (c) migration air-sshd (clé d’hôte, authorized_keys) vers le manager ; (d) service air-keysign (couche 2) quand hostbased/ clé d’hôte privilégiée sera câblé ; (e) CLI + air-json = vague 2. Ces incréments ne sont pas engagés par cet ADR ; il en fixe les décisions normatives.

Alternatives rejetées

Aucune alternative n’a été consignée lors de l’instruction.

Amendement — « manager de domaine » revendiqué, fonctions libres livrées (2026-08-03)

Constat d’audit du 2026-08-03, dans le cadre de la remise en conformité des ADR nommant des Managers. L’état faisant autorité vit dans l’amendement d’ADR-077.

Le §1 déclare air-keystore manager de domaine couche 1 au sens d’ADR-077, et le manifeste de la crate affirmait jusqu’à ce jour une « surface objet médiatrice (AirKeystoreManager) consommée par les toits — jamais des free-functions ».

Les deux affirmations étaient fausses. Aucun type AirKeystoreManager n’a jamais existé. La crate expose des types par artefactAirHostKey, AirKnownHost, AirCaKey, AirKrl, AirRevocationStore, AirRevokedKey, AirSshKeyPair, AirSshPublicKey — et des fonctions libres par module : load, save, encode_artifact, decode_artifact, verify_host.

Ce que cela ne remet pas en cause. Le fond d’ADR-108 tient : une seule crate pour le domaine, délégation de toute primitive crypto à air-crypto, artefacts binaires, cycle de vie centralisé. Ce qui ne tient pas, c’est la forme de la surface — et surtout la phrase « jamais des free-functions », qui décrivait exactement le contraire de ce qui a été livré.

Ce qui reste à décider — et qui n’est pas tranché ici : soit air-keystore se dote du médiateur qu’il revendique, soit ADR-108 §1 est amendé pour assumer une surface par artefact. La seconde branche est défendable : contrairement au système de fichiers ou aux sockets, le domaine des clés n’a pas d’état global à médier, et un AirKeystoreManager risquerait d’être une façade sans substance. Mais il faut choisir, pas laisser la doctrine dire une chose et le code une autre.