Note d’architecture — prérequis d’air-sshd : graphe de dépendances et ordre de bataille
Statut : note de travail (non normative). Cadre les prérequis techniques d’air-sshd
(ADR-074) et leur ordonnancement. Les décisions
structurantes qui en découleront passeront par des ADR/RFC dédiés. S’appuie sur la
doctrine de configuration binaire (ADR-073)
et le socle réseau couche 1 (ADR-069).
1. Idée directrice
air-sshd n’est pas qu’un démon : c’est le premier « bon citoyen » de la couche
service d’Air. À ce titre, il est aussi une fonction de forçage pour la couche
supérieure — le runtime asynchrone, le modèle de service, la session/login — exactement
comme « compiler OpenSSH » a été la fonction de forçage de la couche réseau. La plupart
des prérequis ci-dessous sont donc des facilités partagées que tout service Air
réutilisera, pas des briques jetables propres à ssh.
2. Décision d’outillage : configuration via air-config (capnp), pas serde
Air dispose déjà de air-config / air-config-schema / air-config-compile, bâtis sur
Cap’n Proto (capnp 0.26, dépendance unique embedded-io, no_std-friendly) +
zeroize. C’est exactement le mécanisme de schéma binaire versionné visé par ADR-073.
air-sshd lit donc sa configuration via air-config. On n’introduit pas serde :
serde seul est propre (serde_core no_std, sans dépendance runtime ; serde_derive
proc-macro hôte au build), mais il n’est qu’un framework — il faudrait lui adjoindre un
format binaire (postcard/bincode), ce qui dupliquerait air-config et ajouterait des
dépendances (règle des 80 %, ADR-024). Verdict : capnp/air-config.
3. Fondations déjà en place (couche 0/1)
Réutilisées telles quelles par air-sshd :
| Brique | Rôle pour air-sshd |
|---|---|
| io_uring (couche 0, Temps 1 / 2b réseau) | soumission/complétion d’I/O asynchrone |
| air-socket + air-handle + air-poll (ADR-069) | accept/connect/read/write, registre de fd |
| air-runtime | runtime process/thread (_start, TCB/TLS) — pas un exécuteur async |
| air-crypto | X25519, Ed25519, ChaCha20-Poly1305, AES-GCM, SHA-2, HMAC (profil SSH moderne complet) |
| air-config (capnp) | configuration binaire (ADR-073) |
| air-process | clone3, pidfd, drop_to_user, credentials — base du privsep et du largage de privilèges |
| air-terminal / termios | allocation et discipline de PTY |
| air-signal / signalfd | réception des signaux (à brancher sur le réacteur) |
| air-account | comptes (passwd/group) — base de la politique de login |
| SCM_RIGHTS (sendmsg/recvmsg, #259) | passage de fd moniteur↔enfant (privsep) |
| AirLog | journalisation |
4. Les prérequis manquants
4.1 air-async — le runtime asynchrone partagé (pièce maîtresse)
« La runloop » recouvre en réalité quatre choses, aujourd’hui inexistantes (io_uring
Temps 1 a laissé l’async en différé ; air-runtime n’est pas un scheduler de futures) :
- Réacteur : complétions io_uring → réveil de la bonne tâche (
Waker). - Exécuteur : ordonnancement et poll des
Future,spawn. - Feuilles d’I/O async : accept/read/write renvoyant des
Futureau-dessus d’air-socket. - Timers : login grace, keepalive, intervalle de rekey.
C’est le plus gros manque et une facilité partagée → à sortir en composant à
part entière (air-async). Décision de conception à trancher tôt (ADR) : async/await
(exécuteur + intégration Waker) vs machine à états explicite. Test : sur l’hôte, avec
loom (concurrence déterministe) et fuzzing des feuilles d’I/O.
4.2 Modèle de service : supervision + provisionnement du socket
En amont de l’IPC : qui lance/surveille/redémarre un service Air, et d’où vient son socket d’écoute (activation par socket ? passage de fd ?). Il manque un gestionnaire de services Air (équivalent init/superviseur) et son contrat de cycle de vie.
4.3 IPC Air
Le bus d’IPC auquel un service se connecte — cité mais non développé. Prérequis pour le « bon citoyen », mais pas pour un premier prototype autonome d’air-sshd.
4.4 Modèle session/login + politique d’autorisation
Après l’auth : concept unifié de session (largage uid/gid via drop_to_user, PTY,
environnement, cwd, setrlimit, comptabilité type utmp/wtmp) et surtout la politique
qui décide qu’un utilisateur a le droit de se connecter (équivalent Air de PAM/nsswitch,
adossé à air-account). Les briques existent, l’orchestration non.
4.5 Mécanisme d’autorisation administrateur (config CLI)
La ressaisie du mot de passe administrateur d’ADR-073 (dérivation, stockage, vérification du secret) — laissé ouvert par ADR-073, à spécifier en RFC. Nécessaire au CLI de conf, pas au démarrage du démon (qui lit un binaire déjà produit).
5. air-sshd proprement dit (le composant cible)
Décisions internes à prendre tôt, même si ce ne sont pas des prérequis externes :
- Privsep : écouteur qui largue les privilèges + enfant non privilégié par connexion + moniteur privilégié (signature clé d’hôte, décision d’auth, alloc pty) qui repasse résultats/fd via SCM_RIGHTS. Architecture de sécurité centrale.
- Stockage de la clé d’hôte Ed25519 (identité du démon) : où, permissions, rotation — adossé à ADR-073.
- Parseur de paquets = entrée non fiable : source n°1 de CVE SSH → schema-first, fuzzé, zéro panic sur entrée malformée (comme sockaddr).
- Audit d’auth + anti-force-brute : événements d’auth (IP, empreinte de clé) async-safe, cohérents avec la doctrine binaire.
- Signaux : SIGTERM (drain), SIGHUP (recharge conf binaire), SIGCHLD (récolte enfants) — signalfd branché sur le réacteur.
6. Graphe de dépendances
io_uring (couche 0) air-runtime
| |
+-----------+--------------+
v
┌───────────┐
│ air-async │ (réacteur + exécuteur + timers) ← PIÈCE MAÎTRESSE
└───────────┘
|
air-crypto air-socket/air-handle | air-config(capnp) air-process air-terminal
| | | | | |
+-------+-------+----------+-------+------------+--------+--------+------+-------+
v
┌──────────────┐
│ air-sshd │
│ transport → │ KEX curve25519, paquets ChaCha20-Poly1305 (async)
│ userauth → │ publickey Ed25519 ── politique login (air-account)
│ connection │ canaux, pty, exec ── session/login, drop_to_user
│ (privsep) │ moniteur↔enfant via SCM_RIGHTS
└──────────────┘
|
supervision + socket ──────+────── IPC Air (déploiement « service »)
(gestionnaire de services) (bon citoyen)
Hors chemin critique du premier prototype (mais requis en production) : gestionnaire de services, IPC, mécanisme d’autorisation admin du CLI de conf.
7. Ordre de bataille
Chaque étape testée sur l’hôte d’abord (cf. §8), avec barrière + fuzz/loom là où c’est pertinent.
air-async— réacteur + exécuteur + timers + feuilles d’I/O async. ADR de conception, puis implémentation, loom, fuzz. Débloque tout le reste.- Schéma de conf
air-sshd(viaair-config/capnp) — clés d’hôte, port, utilisateurs autorisés. Indépendant du mécanisme d’autorisation admin (qui viendra s’y brancher). - Couche transport SSH-2 sur
air-async— identification,KEXINIT(moderne seul), KEXcurve25519-sha256, dérivation, paquets chiffrés. Parseur fuzzé. Jalon : un clientsshréel complète le KEX et obtient un canal transport chiffré. ssh-userauth(publickey Ed25519) + politique de login (surair-account).- Privsep — moniteur privilégié + enfant non privilégié, échange via SCM_RIGHTS. Peut s’introduire autour des étapes 3-4.
ssh-connection— canaux,pty-req/shell/exec, session/login (drop_to_user, PTY, environnement). Premierssh user@airinteractif.- Supervision + provisionnement socket + IPC — intégration comme vrai service Air.
- RFC autorisation admin + stockage clé d’hôte — sécurisation du CLI de conf.
8. Le point qui de-risque : host-first, le PAL n’est pas un verrou pour démarrer
Les crates couche 1 (air-socket, air-runtime, air-crypto, air-config) compilent
et tournent déjà sur l’hôte linux-gnu — c’est ainsi qu’on les teste. La logique
d’air-sshd (runtime async, transport, KEX, userauth, canaux) est du Rust pur,
prototypable et testable intégralement sur l’hôte avant livraison du PAL. Le
PAL/linux-air conditionne le binaire natif de production, pas le développement. On peut
donc mener air-async et air-sshd en parallèle du PAL, au lieu de sérialiser
« PAL d’abord ».
9. Résumé
L’oubli central : « la runloop » = un vrai runtime async partagé (air-async :
réacteur + exécuteur + timers), pièce maîtresse à bâtir avant tout. Viennent ensuite le
privsep, la session/login, la supervision + socket + IPC, le stockage de
clé d’hôte et l’audit. La conf passe par air-config (capnp), pas serde. Et le
PAL n’est pas un verrou pour commencer : host-first.