Authentification interne HMAC entre dashboard et gateway
Le dashboard et la gateway communiquent via un canal HMAC authentifié séparé du canal public. Ce document décrit le mécanisme de identité de l'appelant (per-caller identity) et la procédure de déploiement associée. Il est destiné aux opérateurs et aux revues d'incident.
Aperçu
Chaque appel interne (/internal/*, /api/v2/internal/webhooks/*, /api/v2/bulk/status-by-user/*) porte quatre en-têtes HMAC :
| En-tête | Description |
|---|---|
x-internal-signature | Signature HMAC-SHA256 du canonique |
x-internal-timestamp | Unix timestamp, fenêtre ±60 s |
x-internal-nonce | UUID unique par requête (anti-rejeu) |
x-internal-caller (phase B) | Identité de l'appelant (dashboard, ops, ...) |
Le canonique signé est METHOD:PATH:BODY:TS:NONCE[:CALLER]. Le segment :CALLER est ajouté en phase B (migrate) — voir §Procédure de déploiement.
Procédure de déploiement (expand → migrate → contract)
Le changement de format HMAC suit le pattern expand → migrate → contract sur trois PR distincts, pour rendre l'ordre de déploiement indifférent.
Phase A — Expand (cette PR)
- Le gateway accepte deux formats : 5-field (legacy, sans
:CALLER) et 6-field (avec:CALLER). - Le flag
GATEWAY_INTERNAL_ALLOW_LEGACY_CANONICALvauttruepar défaut — les requêtes legacy sont acceptées. - Le dashboard continue d'émettre le format 5-field sans
x-internal-caller. Aucun appel interne ne tombe. - Le middleware
requireCaller('dashboard')est monté sur chaque route interne.
Phase B — Migrate (PR de suivi)
- Le dashboard émet
x-internal-caller: dashboardet signe le canonique 6-field. - Le gateway accepte déjà les deux formats. Aucun appel interne ne tombe.
- Surveiller la ligne de log
internal.legacy_canonical_usedcôté gateway. Quand elle disparaît pendant 24 h, le dashboard est entièrement migré.
Phase C — Contract (PR de suivi)
- Mettre
GATEWAY_INTERNAL_ALLOW_LEGACY_CANONICAL=0dans l'env gateway. Aucun déploiement de code requis. Le branche 5-field est fermée par env. - Vérifier pendant 24 h : zéro ligne
internal.legacy_canonical_used. Si OK, ouvrir un PR qui supprime la branche 5-field et la ligne de log.
Variables d'environnement
| Variable | Défaut | Effet |
|---|---|---|
GATEWAY_INTERNAL_SECRET | (requis) | Secret HMAC partagé. Plusieurs valeurs séparées par , acceptées (sharding). |
GATEWAY_INTERNAL_ALLOWED_CALLERS | dashboard | Allowlist des appelants. Valeurs séparées par ,. |
GATEWAY_INTERNAL_ALLOW_LEGACY_CANONICAL | true (unset ou 1) | Quand true, accepte les requêtes 5-field sans x-internal-caller. Quand 0, exige le header. |
INTERNAL_INTERNAL_SECRETS | (unset) | Legacy : sharded-secret list. Conservé pour rétro-compatibilité. |
INTERNAL_HMAC_WINDOW_SEC | 60 | Fenêtre de timestamp symétrique (±60 s). |
Logs
| Événement | Niveau | Quand |
|---|---|---|
internal.caller_list_loaded | info | Une fois par démarrage du process. |
internal.authenticated | info | Une fois par requête authentifiée. |
internal.legacy_canonical_used | warn | Une fois par process — au premier appel 5-field legacy. C'est la télémétrie de rollout : quand elle disparaît, le dashboard est migré. |
Procédure d'incident
Tous les appels internes 401 après un déploiement ?
- Vérifier que
GATEWAY_INTERNAL_SECRETest défini des deux côtés. - Vérifier que le canonique calculé par le dashboard correspond à celui attendu par le gateway. Le canonique est
METHOD:PATH:BODY:TS:NONCE(phase A) ouMETHOD:PATH:BODY:TS:NONCE:CALLER(phase B+). - Si
GATEWAY_INTERNAL_ALLOW_LEGACY_CANONICAL=0et le dashboard n'émet pasx-internal-caller, le remettre à1en attendant la migration du dashboard. Le flag est l'interrupteur d'urgence — il n'y a pas de bypass par code.
Un déploiement d'urgence pour fermer la branche legacy ?
Mettre GATEWAY_INTERNAL_ALLOW_LEGACY_CANONICAL=0. Aucun déploiement de code requis.