Instruction — la largeur des enveloppes par défaut
Objet. Question ouverte n° 1 d’ADR-150. Elle bloque
AirSandboxManager(ADR-112) et le temps 2 du lanceur (la mise en cage à l’exec).TRANCHÉ le 2026-08-10 (BDFL) : option B. Enveloppes filesystem écrites, familles sans applicateur vides par défaut — donc tout octroi qui les invoque est refusé. Mise en œuvre :
crates/air-bundle/src/ceiling.rs. Ce que l’instruction n’avait pas vu est consigné au §8 : c’était le vrai obstacle.Les §1 à §7 sont conservés tels qu’ils ont servi à décider — on ne réécrit pas une instruction après coup, sans quoi la décision paraît plus évidente qu’elle ne l’était.
1. Ce que la question demande — et ce qu’elle ne demande pas
L’énoncé d’ADR-150 est : « trop étroites, l’utilisateur se heurte à des murs ; trop larges, elles ne protègent rien ». Formulé ainsi, on croit chercher un curseur.
Ce n’est pas cela. Une enveloppe n’est pas un niveau, c’est un plafond déclaré par
l’image, exprimé dans le vocabulaire des entitlements, et que l’étape 3 de l’arbitrage
compare à l’octroi (Entitlements::covers). Décider « la largeur », c’est écrire le
contenu de ce plafond, famille par famille.
Et il n’y en a pas une seule. ADR-046 A2 en distingue déjà deux, et c’est acquis :
| État | Condition | Enveloppe |
|---|---|---|
| Refusé | aucun octroi valide | — |
| Confiné étroit | octroi accordé, provenance étrangère | « le profil le plus restrictif compatible avec l’octroi » |
| Confiné selon enveloppe | octroi accordé, signature Air | « l’enveloppe accordée » |
La décision porte donc sur au moins deux jeux de valeurs, pas sur un curseur.
2. Ce qui est déjà tranché, et qu’il ne faut pas rouvrir
- Aucune exécution sans profil (ADR-046 A1), PID 1 excepté — et l’exception tient à la monotonie du confinement, pas à un privilège.
- L’absence d’octroi est un refus, pas une mise en cage (A2).
- L’ordre d’arbitrage signature → octroi → enveloppe (ADR-150 D3), et le fait qu’une image qui rétrécit son enveloppe arrête des octrois jusque-là valides (D3, conséquence 3).
- Le critère de placement : « cette configuration décide-t-elle quel code s’exécute ? »
(
etude-administration-et-placement-configuration-fr.md§3.2). L’enveloppe y répond oui, donc elle vit dans l’image, couverte par verity — c’est ce qui la rend crédible comme plafond. - Le mécanisme de comparaison existe et est testé :
Entitlements::covers, l’égalité stricte des chemins, l’inclusion des ports.
3. Le fait qui change la question
Le vocabulaire déclare quatre familles ; la cage n’en applique qu’une.
| Famille (ADR-010) | Déclarable dans un manifeste | Applicable par air-sandbox aujourd’hui |
|---|---|---|
| filesystem | oui | oui — Landlock ABI v1 (crates/air-sandbox/src/landlock.rs) |
| network | oui | non — aucun LANDLOCK_ACCESS_NET, aucun namespace réseau |
| AirCom | oui | non — la distribution de capabilities n’existe pas |
| devices | oui | partiellement — seulement en tant que chemins sous /dev, via Landlock |
Côté seccomp, air-sandbox n’expose pas une composition à partir d’entitlements mais deux
profils figés — pre_auth et sftp_worker —, taillés pour air-sshd. Il n’existe aucun
chemin entre « l’octroi accorde ceci » et « voici l’allow-list de syscalls ».
Le Landlock est délibérément borné à l’ABI v1 : landlock_create_ruleset échoue EINVAL
si on lui demande de gérer un droit inconnu du noyau, et la pose de cage est fail-closed —
un EINVAL tuerait le worker sur un noyau parfaitement valide. Élargir à v4 (réseau) n’est
donc pas un ajout de constante : c’est une négociation d’ABI à concevoir.
Conséquence. Fixer aujourd’hui la largeur des quatre familles, ce serait décider pour trois familles dont rien ne sait appliquer la décision. Une enveloppe qui borne ce que la cage ne pose pas n’est pas une protection : c’est une promesse écrite dans un fichier.
C’est le principal apport de cette instruction, et il était invisible depuis l’énoncé.
4. Ce que font les autres, et ce qu’on peut en retenir
À gros traits, sans prétendre à l’exhaustivité :
- OpenBSD
pledge/unveil— des promesses par catégories (stdio,rpath,inet…) plutôt qu’une énumération de syscalls, et un dévoilement de chemins séparé. Ce qui est transposable : le grain. Une catégorie se relit ; une allow-list de 300 syscalls, non. - iOS / macOS — entitlements signés dans le binaire, profils de sandbox distincts. Proche de notre modèle manifeste/octroi, à ceci près que l’octroi y est implicite (la signature vaut accord). C’est exactement ce qu’ADR-150 D1 refuse.
- Android — la leçon est déjà tirée par ADR-150 : avant la version 6, déclarer valait obtenir.
- Flatpak — l’enseignement le plus utile est un échec :
--filesystem=homeest devenu la permission que presque tout le monde demande, ce qui a vidé le modèle de son sens. Une enveloppe trop commode devient la valeur par défaut de fait.
Retenir : le grain doit être compréhensible (catégories, pas syscalls), et la largeur commode ne doit pas exister, sans quoi elle devient la norme.
5. Options de découpage
Option A — tout trancher maintenant, pour les quatre familles
Écrire les enveloppes complètes, y compris pour ce qui n’est pas applicable.
- Pour : la décision est prise une fois, l’ADR se referme.
- Contre : trois familles sur quatre seraient des promesses non tenues ; le premier qui s’y fiera découvrira que l’enveloppe réseau n’a jamais rien borné. C’est précisément le genre de tolérance que le projet refuse ailleurs.
Option B — trancher pour ce qui est applicable, borner le reste à rien (recommandée)
Décider maintenant les enveloppes filesystem (et le grain seccomp), et poser que les familles sans applicateur sont vides par défaut — donc qu’un octroi qui les invoque est refusé à l’étape 3, faute de plafond qui les couvre.
- Pour : cohérent avec le fail-closed du reste du chantier ; débloque
AirSandboxManagerpour ce qu’il sait faire ; aucune promesse creuse. Le refus est explicite et journalisé (OutsideEnvelope), donc l’utilisateur qui bute comprend pourquoi. - Contre : une application demandant du réseau ne se lancera pas tant que l’applicateur n’existe pas. C’est un mur — mais un mur visible et daté, pas une passoire silencieuse.
Option C — attendre que les applicateurs existent
Ne rien décider avant Landlock réseau, AirCom et devices.
- Pour : on décide une fois, en connaissance de cause.
- Contre :
AirSandboxManageret le temps 2 du lanceur restent bloqués indéfiniment, alors que la famille filesystem — la plus importante pour le confinement d’une application — est prête depuis ADR-112.
6. Ce qu’il reste à écrire si l’option B est retenue
- Le grain du profil seccomp. Aujourd’hui : deux profils figés. Demain : soit des
catégories à la
pledgedérivées des entitlements, soit un profil unique très étroit plus des dérogations. C’est une conception à part entière, et elle conditionneAirSandboxManager. - Les chemins de l’enveloppe filesystem, pour chacune des deux largeurs d’A2. À écrire comme des règles, pas comme une liste : l’égalité stricte des chemins (ADR-150 §5.1) impose que le manifeste écrive exactement ce que l’enveloppe autorise.
- Comment l’enveloppe voyage dans l’image — même famille de question que le placement de l’octroi, mais côté immuable : un artefact binaire, signé avec l’image, lu par l’arbitre.
- Ce que voit l’utilisateur qui bute. Le refus est muet vers le demandeur (D4) mais l’administrateur doit pouvoir répondre « ton application demande X, l’image plafonne à Y ».
7. Questions au BDFL
- Découpage — option A, B ou C ? (Recommandation : B.)
- Familles sans applicateur — vides par défaut (donc octroi refusé), ou tolérées et simplement non appliquées ? La seconde est un refus de la doctrine fail-closed ; elle est posée ici pour être écartée explicitement, pas par omission.
- Grain seccomp — catégories à la
pledge, ou profil étroit + dérogations ? - Landlock v4 (réseau) — chantier à ouvrir maintenant, ou après le premier jet des enveloppes filesystem ? Rappel : ce n’est pas une constante à ajouter, c’est une négociation d’ABI (le noyau minimal supporté devient un paramètre de sécurité).
8. Ce que l’instruction n’avait pas vu — le rapprochement des chemins
En écrivant les enveloppes, un obstacle est apparu, invisible depuis l’analyse.
L’étape 3 comparait l’octroi à l’enveloppe avec Entitlements::covers, qui rapproche les
chemins par égalité stricte (ADR-150 §5.1). C’est juste entre une demande et un
octroi — la question y est « la machine a-t-elle accordé ce que le développeur n’avait
pas écrit », et l’égalité n’a pas de cas limite.
Mais l’enveloppe est écrite une fois, dans l’image, pour des applications qu’on ne
connaît pas encore. Avec l’égalité stricte, autoriser /var/lib/org.exemple.editeur
exigerait que l’image nomme cette application — une image par application. Le plafond
ne peut pas s’exprimer en chemins littéraux.
Deux questions différentes méritent deux rapprochements :
| Comparaison | Question | Rapprochement |
|---|---|---|
| demande ↔ octroi | la machine a-t-elle accordé plus que demandé ? | égalité stricte (inchangé) |
| enveloppe ↔ octroi | l’image permet-elle cette classe ? | couverture par gabarit |
Le gabarit n’est pas un glob : pas de *, pas de ?, donc aucun moteur d’expansion et
rien à normaliser. Deux variantes typées — Exact, Subtree — plus un marqueur
{bundleId} substitué avant comparaison, qui permet d’écrire « son propre répertoire de
données » sans nommer personne.
Le piège évité, et il est classique : "/vart".starts_with("/var") est vrai. Une
couverture de sous-arbre écrite avec starts_with autoriserait /vart/secrets parce que
l’image permet /var. Le rapprochement se fait donc composant par composant. Deux
tests le verrouillent, prouvés mordants — remplacer par starts_with les fait tomber.
9. Ce que les enveloppes refusent, et pourquoi
Ce qu’elles permettent se lit dans le code. Ce qu’elles refusent est la décision :
- l’exécution, partout, y compris pour un bundle signé Air. ADR-046 A1 : la signature décide de la largeur, jamais de l’exemption. Un endroit à la fois inscriptible et exécutable annulerait le confinement ; un endroit seulement exécutable ouvre la chaîne « déposer ailleurs, lancer ici ». Un test vérifie qu’aucune règle par défaut ne l’accorde ;
- l’écriture sous
/etc— la configuration décide quel code s’exécute ; - le
$HOMEen bloc. C’est l’échec de Flatpak retenu au §4 :--filesystem=homeest devenu ce que presque tout le monde demande, et le modèle s’est vidé. Une application qui a besoin des documents passera par un portail, où l’utilisateur choisit le fichier. Ce portail n’existe pas encore : le manque est donc explicite, pas contourné.
Les deux largeurs d’ADR-046 A2 sont ordonnées — un test vérifie que l’enveloppe Air contient toute règle de l’étroite, et qu’elle ajoute réellement quelque chose.
10. Ce qui reste ouvert
Les questions 3 et 4 du §7 ne sont pas tranchées :
- le grain du profil seccomp — catégories à la
pledge, ou profil étroit plus dérogations ? Il n’existe toujours aucun chemin entre « l’octroi accorde ceci » et « voici l’allow-list » ; - Landlock v4 (réseau) — quand ouvrir le chantier. Rappel : c’est une négociation d’ABI, pas une constante à ajouter.
Tant qu’elles sont ouvertes, AirSandboxManager reste partiellement bloqué : les enveloppes
existent, le poseur de cage ne sait pas encore les consommer entièrement.
Licence du document : MPL 2.0