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

ADR-146 — Forwarding serveur en privsep par médiation du monitor

Statut : Accepté (2026-07-31, décision BDFL). Le quoi (« le serveur en privsep doit faire du forwarding ») et le comment (« par médiation du monitor », option B) ont été tranchés par le BDFL. Cet ADR fixe la conception avant tout code, sur le modèle des mini-ADR de conception ADR-124/ADR-129/ADR-139.

S’appuie sur ADR-122 (topologie privsep), ADR-124 (canal RPC pré-auth monitor↔enfant), ADR-129 (transition post-auth), ADR-131 (worker post-auth confiné), ADR-132 (io_uring DONTFORK), ADR-089 (autorité SecurityManager), ADR-117 (redirections et multiplexing), ADR-118 (politique de forwarding côté serveur), ADR-097 (§C.5 multiplexing), ADR-128 (options et permissions).

Catégorie : conception d’architecture couche 2 (air-sshd). Aucun code ici : topologie du canal, découpage en incréments, invariants de sécurité. Aucun descellement, aucun re-sceau.

Contexte

En privsep, air-sshd sert la session post-auth via un worker confiné : bascule setuid vers l’utilisateur cible, sandbox en mode drop-only ou cage SFTP. Ce worker est mono-fil, sans réacteur — il travaille sur un ppoll bloquant (blocking.rs) — et il refuse aujourd’hui tout forwarding (connection/mod.rs:350, connection/mod.rs:585, garde is_deferred()).

Deux murs expliquent ce refus.

  1. Pas de concurrence. Le forwarding exige de suivre en même temps le transport SSH et le socket cible. Le worker ne sait faire qu’une chose à la fois.
  2. La sandbox tue le réseau. Les syscalls socket, connect, bind et listen sont hors des profils pre_auth et sftp_worker ; seul recvmsg est permis au worker.

À cela s’ajoute une contrainte doctrinale qui ferme la sortie de secours évidente : aucun anneau io_uring en étage confiné (ADR-131 §153, ADR-132). Un anneau contourne seccomp et échappe à Landlock — il est l’évasion, pas un moyen de l’éviter.

Conséquence pratique : le serveur Air réellement déployé (celui en privsep) ne sait pas faire de forwarding, alors que le chemin monolithique, lui, le sait.

Décision

Médiation par le monitor (option B). Deux volets.

1. Les syscalls réseau sont délégués au monitor

Le worker ne fait aucun socket, connect, bind ni listen. Il demande au monitor (le seul étage privilégié) via un canal RPC dédié — un socketpair établi avant la transition et survivant à la purge de descripteurs de celle-ci (transition.rs:309).

Le monitor, et lui seul :

  • applique la politiqueAllowTcpForwarding, PermitOpen, PermitListen — sous l’autorité du SecurityManager (ADR-089, ADR-118) ;
  • fait le syscall réseau ;
  • rend le descripteur au worker par SCM_RIGHTS.

Le worker reçoit donc un fd déjà ouvert, déjà autorisé, sans jamais gagner le droit de l’ouvrir lui-même.

2. La concurrence vient d’une pompe bloquante multi-fd

Dans le worker, une pompe bloquante multi-fd — extension directe du motif ppoll déjà en place pour le PTY interactif (worker.rs:336) — multiplexe les canaux SSH et route transport↔fd. Sans io_uring, ce qui respecte la doctrine anti-anneau.

Corollaire assumé : les moteurs monolithiques existants (forward_pump, tcpip_forward), écrits sur air-async/io_uring, ne sont pas réutilisés dans le worker. On écrit la version bloquante.

Découpage

IncrémentContenuRésultat visé
V2.6-1Canal RPC worker↔monitor, opération ForwardConnect (-L, direct-tcpip), pompe bloquante multi-fd pour le cas -N (forward-only)air-ssh -N -L fonctionne contre un serveur Air en privsep
V2.6-2-R : ForwardListen, boucle d’accept médiée par le monitor, forwarded-tcpipforwarding distant
V2.6-3Streamlocal Unix médié, puis coexistence session + forwards (le §C.5 d’ADR-097, portée (ii))parité fonctionnelle

La portée (i) — forwarding isolé — se traite d’abord ; la coexistence avec une session active vient après.

Invariants de sécurité

  • Le monitor est l’autorité de politique. Il vérifie lui-même, ne se fie jamais au worker, recheck systématiquement, et échoue fermé (ADR-122).
  • La RPC worker↔monitor est avare : deux à trois opérations, dans l’esprit de start_session. Pas de surface généraliste.
  • La sandbox du worker reste minimale : on n’ouvre pas socket/connect/bind au worker. recvmsg et SCM_RIGHTS sont déjà permis — rien à élargir.
  • Aucun anneau io_uring en étage confiné (ADR-131, ADR-132).
  • Un échec d’ouverture se solde par le refus du canal, jamais par la mort de la session.
  • port_forwarding reste la porte de politique, issue de la contrainte (ADR-128).

Alternatives rejetées

(A) Mini-réacteur bloquant dans le worker, faisant lui-même les syscalls réseau. Exigerait d’élargir la sandbox du worker à socket/connect/bind et de découpler les moteurs d’air-async. Rejeté : élargir la sandbox de l’étage confiné est strictement moins sûr que médier par l’étage privilégié.

(C) Réacteur io_uring dans le worker shell/exec. Rejeté : rompt la doctrine anti-anneau (ADR-131, ADR-132) et n’aide en rien SFTP.

Conséquences

  • Le serveur air-sshd déployé (privsep) fera enfin du forwarding, sans affaiblir le confinement du worker.
  • Nouvelle surface RPC privilégiée : à concevoir avec le même soin que le reste du monitor — codec sans-IO, fuzzé, avare en opérations.
  • La politique d’ADR-118 (AllowTcpForwarding, PermitOpen, PermitListen) s’appliquera côté monitor, à l’endroit où elle a l’autorité pour être opposable.