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

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 :

BriqueRô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-runtimeruntime process/thread (_start, TCB/TLS) — pas un exécuteur async
air-cryptoX25519, Ed25519, ChaCha20-Poly1305, AES-GCM, SHA-2, HMAC (profil SSH moderne complet)
air-config (capnp)configuration binaire (ADR-073)
air-processclone3, pidfd, drop_to_user, credentials — base du privsep et du largage de privilèges
air-terminal / termiosallocation et discipline de PTY
air-signal / signalfdréception des signaux (à brancher sur le réacteur)
air-accountcomptes (passwd/group) — base de la politique de login
SCM_RIGHTS (sendmsg/recvmsg, #259)passage de fd moniteur↔enfant (privsep)
AirLogjournalisation

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) :

  1. Réacteur : complétions io_uring → réveil de la bonne tâche (Waker).
  2. Exécuteur : ordonnancement et poll des Future, spawn.
  3. Feuilles d’I/O async : accept/read/write renvoyant des Future au-dessus d’air-socket.
  4. 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.

  1. 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.
  2. Schéma de conf air-sshd (via air-config/capnp) — clés d’hôte, port, utilisateurs autorisés. Indépendant du mécanisme d’autorisation admin (qui viendra s’y brancher).
  3. Couche transport SSH-2 sur air-async — identification, KEXINIT (moderne seul), KEX curve25519-sha256, dérivation, paquets chiffrés. Parseur fuzzé. Jalon : un client ssh réel complète le KEX et obtient un canal transport chiffré.
  4. ssh-userauth (publickey Ed25519) + politique de login (sur air-account).
  5. Privsep — moniteur privilégié + enfant non privilégié, échange via SCM_RIGHTS. Peut s’introduire autour des étapes 3-4.
  6. ssh-connection — canaux, pty-req/shell/exec, session/login (drop_to_user, PTY, environnement). Premier ssh user@air interactif.
  7. Supervision + provisionnement socket + IPC — intégration comme vrai service Air.
  8. 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.