ADR-111 — air-log (AirLogManager) : sink de logs opérationnels (couche 1), journald natif sans C, anti-injection, distinct d’AirCom
Statut : Accepté (2026-07-26, décision BDFL). RFC de direction
(ADR-015).
S’appuie sur ADR-077 (managers de domaine
couche 1), ADR-001 (AirCom = bus IPC
événementiel), ADR-088 (sans libc C),
ADR-029 (nommage), ADR-019
(modèle d’erreurs), ADR-089
(SecurityManager).
Catégorie : Architecture couche 1 (nouveau manager de domaine). Vague 1 #4 du triage des écarts OpenSSH.
Contexte
OpenSSH journalise vers syslog/journald ses événements opérationnels (connexions,
échecs d’auth, erreurs). air-sshd ne publie aujourd’hui que sur AirCom — le bus IPC
événementiel programmatique d’Air ([ADR-001]), consommé par des programmes. Or un
administrateur attend un journal opérationnel consultable par les outils standard
(journalctl) : c’est une surface distincte d’AirCom (destinataire humain/ops vs
consommateur logiciel), et elle manque.
Deux exigences la rendent délicate : (1) rester sans libc C ([ADR-088]) alors que la
voie naturelle serait libsystemd (C) ; (2) on journalise des données contrôlées par
l’attaquant (nom d’utilisateur, commentaire de clé, IP) — vecteur classique
d’injection de logs.
Directive BDFL (2026-07-26). « Je vois un manager de log… distinct d’AirCom. » Détail tranché en discussion : journald natif d’abord + fallback ; pont AirCom opt-in ; API typée ; anti-injection obligatoire ; envoi drop-safe ; abstraction de sink.
Décision
1. air-log est un manager de domaine couche 1, distinct d’AirCom
Une crate air-log exposant un AirLogManager ([ADR-077]) : le sink de logs
opérationnels (destinés à l’admin / journalctl), séparé d’AirCom ([ADR-001], bus
événementiel programmatique). no_std + alloc, erreurs AirError ([ADR-019]), au-dessus
d’air-socket (couche 1) — jamais la couche 0 en direct. Pont opt-in : un
consommateur peut demander de mirrorer certains événements AirCom vers le log (ex.
AuthFailed → warning), mais les deux surfaces restent indépendantes.
2. journald natif, écrit par nous (sans C) + fallback résilient
Le sink par défaut parle le protocole journald natif — socket datagram
/run/systemd/journal/socket, champs structurés CLÉ=valeur (cadrage longueur-préfixé
pour les valeurs multi-lignes) — implémenté par nous (pas de libsystemd C, cohérent
[ADR-088]). Si journald est absent (systemd non présent), fallback résilient
(stderr / fichier). Le protocole natif est simple et documenté ; l’écrire nous coûte peu et
évite une dépendance C.
3. Anti-injection de logs — obligatoire
On journalise des données d’attaquant ⇒ toute valeur est neutralisée (retours à la ligne / caractères de contrôle échappés ou rejetés) avant émission. Le cadrage binaire longueur-préfixé de journald protège par construction (une valeur ne peut pas « sortir » de son champ) — argument supplémentaire pour journald natif plutôt que syslog texte. Fuzz de l’encodeur (valeurs arbitraires → trame journald bien formée).
4. API typée + envoi drop-safe
API typée : niveaux (emergency … debug) + champs structurés (user, peer,
method, session_id…), pas des chaînes libres. Émission datagram fire-and-forget,
drop-safe sous flood : un pic de logs ne doit jamais bloquer le réacteur d’un démon
(air-sshd sous DoS) — on jette plutôt que de bloquer, avec compteur de pertes.
5. Abstraction de sink
Le sink est abstrait (trait) : journald / fichier / futur journal Air natif (cf. air-launchd v2) — pour ne pas se lier à systemd. Le manager choisit le sink à la construction ; le code appelant ne dépend que de l’API typée.
Conséquences
Positives.
- Journal opérationnel standard (
journalctl) pour les démons Air, distinct du bus programmatique AirCom — chaque surface pour son public. - Sans C : protocole journald natif, pas de
libsystemd; tient sur*-linux-air. - Anti-injection par construction (cadrage journald) — classe de vulnérabilités fermée.
- Drop-safe : la journalisation ne devient pas un vecteur de DoS.
- Découplé de systemd (abstraction de sink) : portable vers un journal Air futur.
Négatives / coûts assumés.
- Protocole journald à maintenir (écrit maison) : suivre l’évolution du format natif ; coût faible (format stable), fuzz + tests de conformité.
- Nouvelle crate couche 1 aux exigences (coverage, fuzz encodeur,
AirError). - Pont AirCom à concevoir avec soin (opt-in, pas de double-comptage / boucle).
Mise en œuvre (référence, hors décision). Incréments : (a) crate air-log couche 1
(API typée + encodeur journald natif + fallback) ; (b) fuzz de l’encodeur + anti-injection ;
(c) sink abstrait ; (d) câblage air-sshd/air-ssh + pont AirCom opt-in. Non engagés par
cet ADR.
Alternatives rejetées
Aucune alternative n’a été consignée lors de l’instruction.