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

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 :

ÉtatConditionEnveloppe
Refuséaucun octroi valide
Confiné étroitoctroi accordé, provenance étrangère« le profil le plus restrictif compatible avec l’octroi »
Confiné selon enveloppeoctroi 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 manifesteApplicable par air-sandbox aujourd’hui
filesystemouioui — Landlock ABI v1 (crates/air-sandbox/src/landlock.rs)
networkouinon — aucun LANDLOCK_ACCESS_NET, aucun namespace réseau
AirComouinon — la distribution de capabilities n’existe pas
devicesouipartiellement — 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éspre_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=home est 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 AirSandboxManager pour 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 : AirSandboxManager et 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

  1. Le grain du profil seccomp. Aujourd’hui : deux profils figés. Demain : soit des catégories à la pledge dérivées des entitlements, soit un profil unique très étroit plus des dérogations. C’est une conception à part entière, et elle conditionne AirSandboxManager.
  2. 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.
  3. 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.
  4. 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

  1. Découpage — option A, B ou C ? (Recommandation : B.)
  2. 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.
  3. Grain seccomp — catégories à la pledge, ou profil étroit + dérogations ?
  4. 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 :

ComparaisonQuestionRapprochement
demande ↔ octroila machine a-t-elle accordé plus que demandé ?égalité stricte (inchangé)
enveloppe ↔ octroil’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 $HOME en bloc. C’est l’échec de Flatpak retenu au §4 : --filesystem=home est 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