#72 passé en `closed` : mergé dans `develop` via `ae01297`, vert hors sandbox, branche supprimée. Carnet complété avec le périmètre livré et les arbitrages. État `.ideai/` indépendant du code de #68, committé directement sur `develop`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.2 KiB
issueRef, version, updatedBy, updatedAt
| issueRef | version | updatedBy | updatedAt | ||||
|---|---|---|---|---|---|---|---|
| #72 | 3 |
|
1784236365445 |
Ticket #72 — Confiance reverse proxy : carnet de chantier
Statut : livré et mergé dans develop (7fbaa8e) le 2026-07-16.
Commits : 75d1ce8 (code), 42804fc (doc), ae01297 (merge --no-ff). Branche supprimée.
Ce qui a été livré
trusted_proxiesdansServerConfig+ flag CLI--trusted-proxy(répétable, IP ou CIDR).- Refus au démarrage d'un bind non-loopback distant sans proxy autorisé.
- Guard runtime sur les trois surfaces — statique,
/api/*, WebSocket : lepeer_addrest vérifié avant toute lecture de header. X-Forwarded-Proto: httpsobligatoire en mode proxy.X-Forwarded-Forn'autorise jamais rien — journalisé uniquement.- B0 : réconciliation du port effectif dans
run_server(le chemin CLI standalone souffrait du même bug que l'embedded :origin_allowedcomparait àhttp://127.0.0.1:0). - Diagnostics :
UntrustedProxyPeer,ForwardedProtoRejected,ForwardedHostMismatch.
Ajustement en cours de route — le hard reject X-Forwarded-Host a été RETIRÉ
Cadré initialement comme un refus, il est devenu un warning de diagnostic. Raison (Architect) : redondant avec la vérification d'Origin en égalité stricte déjà appliquée à toutes les routes /api/*, et il cassait la configuration par défaut de nginx, qui n'envoie pas cet en-tête. Le vrai contrôle d'accès reste pairing/session + pair proxy autorisé. Gain trop faible pour la friction créée.
Le repli de lecture accepte X-Forwarded-Host, Forwarded host= ou Host.
Vérification réelle (Main, hors sandbox)
cargo test -p web-server → 66 passed / 0 failed. Re-vérifié sur develop après merge : 66 · 41 (backend) · 35 (app-tauri), 0 échec.
Comportement exercé sur un serveur réellement lancé, pas seulement en test :
- proxy autorisé +
X-Forwarded-Proto: https+ host absent (= nginx par défaut) → passe le guard,forwardedHostMismatchen diagnostic. - host différent → passe également, warning seulement.
X-Forwarded-Protoabsent → 403, message : « Configure the reverse proxy to send it; for nginx add: proxy_set_header X-Forwarded-Proto $scheme; ».
⚠️ Piège d'environnement — un faux vert s'est produit ici, deux fois
DevBackend et QA sont tous deux bloqués par EPERM sur TcpListener::bind. Un cargo test -p web-server sandboxé rend un vert qui ne prouve rien : sur #68 B1, un « 57 passed » sandboxé a masqué un vrai bug produit (port éphémère vs origin_allowed). Tout vert sur ce crate doit être produit hors sandbox. C'est consigné dans les messages de commit, pas seulement ici.
Revue de sécurité (Git, au-delà du vert)
Guard câblé sur les trois surfaces, aucune route qui y échappe. Le chemin de production propage le vrai pair (handle_tcp_connection → Some(peer_addr.ip())). Le repli peer_ip.unwrap_or(config.listen.ip()) n'est atteignable que depuis des appelants #[cfg(test)]. CIDR correct, y compris prefix == 0 (qui aurait débordé au shift sans traitement à part).
Changement de comportement ASSUMÉ
Le durcissement casse volontairement les configurations existantes : un bind non-loopback distant sans --trusted-proxy est désormais refusé au démarrage, avec un message qui dit quoi faire. Consigné dans l'historique git.
Origine du ticket
Trouvé par Git, pas par une revue d'architecture : c'est lui qui a établi que --trust-reverse-proxy n'avait aucun effet runtime — il n'était lu que pour exiger sa propre présence. La trilogie déclarait une topologie sécurisée sans jamais la garantir.
Point ouvert, remonté par Git — à arbitrer par l'utilisateur
#73 (TLS intégré) rendrait une partie de #72 obsolète. Si idea-serve termine lui-même TLS, la cérémonie proxy — --trusted-proxy, le guard, ces diagnostics — ne concerne plus que les déploiements qui gardent un proxy devant. Ce n'est pas du travail perdu (#72 corrige un vrai trou aujourd'hui, et B0 était un bug réel), mais l'ordre des deux mérite un regard.