#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>
56 lines
4.2 KiB
Markdown
56 lines
4.2 KiB
Markdown
---
|
|
issueRef: "#72"
|
|
version: 3
|
|
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
|
|
updatedAt: 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_proxies` dans `ServerConfig` + 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 : le `peer_addr` est vérifié **avant** toute lecture de header.
|
|
- `X-Forwarded-Proto: https` obligatoire en mode proxy.
|
|
- `X-Forwarded-For` n'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_allowed` comparait à `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, `forwardedHostMismatch` en diagnostic.
|
|
- host **différent** → passe également, warning seulement.
|
|
- `X-Forwarded-Proto` absent → **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.
|