Lot 2 de la migration : la façade va authentifier elle-même ses routes /api/*. La clé de pilotage vit dans la base bbolt, verrouillée par ce process ; la façade la demande donc à GET /internal/v1/auth, à chaque requête — un changement de clé prend effet tout de suite, comme avant. La réponse contient un secret, d'où trois verrous : route branchée uniquement sur l'écoute TCP directe (le tunnel du relais sert le mux nu), jeton tiré au hasard par la façade à chaque démarrage et exigé en temps constant, 404 sans rien révéler sinon. Base illisible : 503, la façade fermera l'accès plutôt que de l'ouvrir. Spec du lot 2 jointe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3.8 KiB
Migration Rust — lot 2 : UI statique, authentification, /api/ping
Date : 2026-10-13 · Statut : validé · Prérequis : lot 1 (façade loki-rs).
But
Faire passer en Rust les premières routes réelles, et surtout poser le socle
dont toutes les routes /api/* suivantes auront besoin : l'authentification.
Périmètre
-
UI statique servie par Rust, embarquée dans le binaire à la compilation depuis
internal/loki/ui/(même source que lego:embed) :Route Content-Type Cache-Control Autre /,/index.htmltext/html; charset=utf-8no-store, max-age=0/marked.min.jsapplication/javascriptpublic, max-age=86400/sw.jsapplication/javascriptno-store, max-age=0Service-Worker-Allowed: //manifest.webmanifestapplication/manifest+jsonpublic, max-age=3600/fonts/<nom>.woff2font/woff2public, max-age=31536000, immutableGET et HEAD seulement (les autres méthodes restent relayées au Go). Compression gzip si le client l'accepte, sauf polices (comme
web_gzip.go) ;Vary: Accept-Encodingsur les types compressibles. Version compressée calculée une fois, à la première demande. -
API interne Go
GET /internal/v1/auth→{"web_key": "…", "version": "…"}.- Servie uniquement sur l'écoute TCP directe de
loki web(enveloppe autour du mux danscmdWeb) : le tunnel du relais sert le mux nu et ne la voit jamais. - Exige l'en-tête
X-Loki-Internalégal àLOKI_INTERNAL_TOKEN(≥ 32 caractères, comparaison en temps constant). Jeton absent, trop court ou faux → 404 (rien ne révèle l'existence de la route). - Base illisible → 503 : la façade fermera l'accès (jamais ouvert par
défaut sur une panne d'E/S, même règle que
requireWebAuth). - La façade tire un jeton aléatoire de 32 octets à chaque démarrage et le passe au Go par l'environnement.
- Servie uniquement sur l'écoute TCP directe de
-
Authentification de pilotage en Rust (
auth.rs), port à l'identique derequireWebAuth+crossSiteReject+localHostName+checkBearer:- clé relue à chaque requête (API interne) : un changement de clé prend effet immédiatement ;
- API interne en erreur (Go absent, 503, réponse illisible) → 503
{"error":"configuration illisible — réessaie dans un instant"}; - refus site tiers / hôte non local sans clé → 403
{"error":"<raison>"}(mêmes messages que le Go) ; - Bearer absent ou faux → 401
{"error":"non autorisé"}+WWW-Authenticate: Bearer realm="loki"; X-Loki-Vian'est jamais pris en compte côté Rust (effacé à l'entrée, le trafic du tunnel ne traverse pas la façade).
-
/api/pingen Rust :{"ok":true,"service":"loki","version":"<version Go>"}après authentification — première route/api/*servie par Rust. -
Durcissement de la façade :
/internal/*venant de l'extérieur → 404 ; en-têteX-Loki-Internalentrant effacé avant relais.
Hors périmètre
Toutes les autres routes /api/*, /v1, le flux chat : lots suivants.
Tests
- Go : route interne (sans jeton → 404, mauvais jeton → 404, bon jeton → 200 avec clé et version, autres chemins relayés au mux).
- Rust : cas de
TestCrossSiteRejectrepris un pour un ;local_host_name(IP, IPv6 entre crochets, nom sans point, suffixes locaux, hôte de confiance) ; Bearer ; statiques (types, cache, gzip, HEAD, police inconnue → relayée) ; intégration façade + faux backend interne : ping 200 sans clé, 401 avec clé sans Bearer, 200 avec bon Bearer, 503 si l'API interne tombe, 403 rebinding,/internal/v1/authdepuis l'extérieur → 404. - Bout en bout local : vrai
lokiGo derrière la façade,loki set-web-key, ping avec et sans clé.