Files
MaksTinyWorkshop ef24d85d57 capitalisation: triage 95_a_capitaliser + création domaine infra
Triage des 27 propositions du buffer de capitalisation (skill
capitalisation-triage), avec vérification des doublons contre la base.

Intégré dans knowledge/ (23 entrées):
- backend: redis (compensation incrBy non-atomique), nestjs (injection
  cassée sous tsx watch; guard write mode dégradé), async (test rollback
  pipeline multi-fichiers), contracts (idempotence POST), auth (disclosure
  comptes soft-deleted), prisma (index partial soft-delete), llm-providers
  (nouveau: OAuth vs API key, prompt caching).
- frontend: tests (garde-fous parking Later), navigation (fichiers
  non-route sous src/app Expo Router), general (type client vs payload
  backend), state (fallback catch-all mapping DB→UI).
- workflow: story-tracking (statut BMAD vs narratif obsolète).
- product: general (nouveau: doc feature store sans UI).
- infra: NOUVEAU DOMAINE (traefik, tailscale, docker, docker-networking,
  reverse-proxy-paths, sidecar tailscale) + 00_INDEX.md.

Autres:
- 90_debug_et_postmortem.md: post-mortem réseau Docker partagé hors compose.
- Rejeté 3 doublons (types enum contracts, getter PrismaService, $transaction).
- Buffer 95_a_capitaliser.md purgé et restauré à son état initial.
- _projects.conf: MAJ statuts epics + ajout app-rl799.
2026-06-25 10:31:22 +02:00

110 lines
5.2 KiB
Markdown

---
title: Infra — Risques & vigilance : Traefik
domain: infra
bucket: risques
tags: [traefik, docker, reverse-proxy, bun, websocket, basic-auth, ios]
applies_to: [implementation, review, debug, architecture]
severity: high
validated_on: 2026-06-25
source_projects: [_Assistant_Cuisine]
---
# Infra — Risques & vigilance : Traefik
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/infra/risques/README.md` pour l'index complet.
---
<a id="risque-traefik-v3-docker29-api"></a>
## Pièges Traefik v3 + Docker Engine 29
### Risques
- **Incompatibilité d'API Docker** : Docker 29 a élevé la minimum API version. Traefik < 3.6 utilise un client Docker SDK figé en `1.24` ; le daemon refuse la connexion et aucune route ne fonctionne.
- **`PathPrefix(/api)` trop large** : un routeur `Host(...) && PathPrefix(/api)` vole les routes `/api/*` de toutes les autres apps du même domaine (Homepage `/api/services`, Next.js `/api/...`, n'importe quel SPA) → 401/404 silencieux.
- **Bun `new Response(body, {status: 3xx})` perd le header `Location`** : un proxy HTTP en Bun qui forward un upstream 302/303 retransmet le status mais le client reçoit un 200 vide (ou un status sans `Location`). Le navigateur ne suit pas la redirection.
### Symptômes
- Container Traefik qui tourne mais aucune route active, logs en boucle :
```
ERR Failed to retrieve information of the docker client and server host
error="Error response from daemon: client version 1.24 is too old.
Minimum supported API version is 1.40, please upgrade your client to a newer version"
```
- Une app exposée sur le même domaine que le dashboard Traefik se met à retourner 401/404 sur ses propres `/api/*`.
- Un proxy Bun qui forward une redirection : le client reçoit un 200 vide ou un `Location` nul.
### Bonnes pratiques / mitigations
**Incompatibilité API Docker**
- Utiliser **Traefik 3.6.1+** (client Docker avec négociation auto de version). Les tags `:v3.5` / `:v3.2` ne marchent pas, même avec `DOCKER_API_VERSION=1.44` en env (le client n'honore pas cette variable dans Traefik).
- Pinner explicitement la version (ex. `traefik:v3.6.15`), jamais le tag mouvant `:v3.6`.
- Vérifier la compatibilité Traefik ↔ Docker Engine **avant** un upgrade Docker.
**`PathPrefix(/api)` trop large**
```yaml
# ❌ trop large — capture tous les /api/* du domaine
- "traefik.http.routers.dashboard-api.rule=Host(`example.com`) && PathPrefix(`/api`)"
# ✅ ne capture que les endpoints natifs Traefik
- "traefik.http.routers.dashboard-api.rule=Host(`example.com`) && PathRegexp(`^/api/(overview|version|rawdata|support-dump|entrypoints|http|tcp|udp)(/|$$)`)"
```
Les endpoints natifs Traefik sont fixés : `overview`, `version`, `rawdata`, `support-dump`, `entrypoints`, `http/*`, `tcp/*`, `udp/*`. Sur un domaine partagé entre plusieurs apps : **jamais** de `PathPrefix(/api)` générique, toujours une regex explicite.
**Bun `new Response` perd le `Location`**
```ts
// ❌ perd le Location dans Bun (~1.1.x)
return new Response(upstream.body, {
status: upstream.status, // 303
headers: upstream.headers, // contient Location, ignoré au final
});
// ✅ Hono c.redirect() ou équivalent
if (upstream.status >= 300 && upstream.status < 400) {
const loc = upstream.headers.get("location");
if (loc) return c.redirect(loc, upstream.status as 301 | 302 | 303 | 307 | 308);
}
```
**Règle** : tout proxy HTTP qui forward des redirects upstream doit traiter explicitement la branche 3xx avant la branche "body normal".
- Contexte technique : Traefik v3 / Docker 29 / Bun + Hono — _Assistant_Cuisine 04-05-2026
---
<a id="risque-basic-auth-websocket-webkit-ios"></a>
## Basic auth + WebSocket : re-prompts répétés (surtout WebKit/iOS)
### Risques
- Une app derrière une basic auth (Traefik, nginx) qui utilise des WebSockets (code-server, Jupyter, ttyd, et beaucoup d'apps "live") déclenche **plusieurs prompts d'authentification** à l'ouverture, même quand les credentials viennent d'être saisis.
### Symptômes
- 3 prompts d'authentification successifs à chaque ouverture de l'app, observés notamment sur tablette iOS / iPadOS.
- Persiste sur Chrome iOS et Firefox iOS (tous contraints d'utiliser WebKit sur iOS). Sporadique sur macOS, rare sur desktop Linux/Windows.
### Cause
Safari (et tous les navigateurs iOS, contraints à WebKit) ne **propage pas systématiquement les credentials basic auth aux requêtes WebSocket** d'une page déjà authentifiée. Chaque WS qui échoue redéclenche un prompt.
### Bonnes pratiques / mitigations
Remplacer la basic auth par une auth à **cookie de session** :
- Soit l'auth native de l'app si elle existe (code-server `HASHED_PASSWORD`, JupyterHub login form, etc.).
- Soit un système central type Authelia / Authentik / traefik-forward-auth qui pose un cookie partagé entre apps.
Le cookie passe automatiquement avec les requêtes WebSocket (même origine) → plus de re-prompt.
**Règle** : pour toute app qui utilise des WebSockets ET qui doit être protégée, ne **pas** utiliser la basic auth. Toujours cookie-based. La basic auth reste OK pour des UI sans WS (dashboards en pull, API REST simples).
- Contexte technique : Traefik / basic auth / WebKit iOS — _Assistant_Cuisine 04-05-2026