Aller au contenu

ADR-0020 — Propagation du couloir en inter-app par contexte signé (jamais en-tête libre)

Date : 2026-06-17 (amendée 2026-06-21) Catégorie : A (convention de sécurité transverse) Décideur : Lead architecte DYNORS Statut : Acceptée (avec amendements 2026-06-21 — Sprint 0 immédiat, voir §Amendements) S'appuie sur : [[0008-secret-transit-sly-par-app]], [[0017-couloir-lane-unite-execution-explicite]], [[0018-exposition-hors-prod-sly-edge-chemin]], [[0019-modele-sirrat-dimension-couloir]] Sources : étude §Partie 7 Q21 ; dynors-platform/selebeyone/src/main/java/com/dynors/sly/filter/SlyForwardSigningFilter.java, SlyTransitHkdf.java, core/packages/core/commons/src/main/java/com/dynors/commons/interapp/SlyTransitFilter.java, core/packages/core/interapp-client/src/main/java/com/dynors/interapp/client/DefaultInterAppCallService.java


Contexte

Hors-prod, si l'on veut router l'inter-app (SLY Bus) par couloir, l'identifiant de couloir doit transiter de service à service. Un simple en-tête X-Sly-Lane fourni librement par l'appelant permettrait à un client de cibler un couloir non autorisé (franchissement d'isolation). Le Bus dispose déjà d'une signature de transit HMAC (SlyForwardSigningFilter + SlyTransitHkdf, ADR-0008).

Décision (acceptée 2026-06-21)

  1. L'en-tête libre est exclu. Un lane_code inter-app n'est accepté que s'il est : injecté par un composant de confiance (InterAppCallService), inclus dans la signature HMAC de transit, vérifié par le Bus, contrôlé contre (route, tenant, projet, environnement), non modifiable par un navigateur/client externe, journalisé et propagé dans les traces distribuées.
  2. Option C retenue — contexte signé. En-têtes X-Sly-Environment + X-Sly-Lane (amendement 2026-06-21 : préfixe X-Sly-* au lieu de X-Dynors-* pour cohérence avec les headers déjà injectés par SLY — explicite que c'est un header de transit signé, jamais accepté brut) inclus dans le HKDF/signature de transit, vérifiés par le Bus puis par chaque backend cible. Options A (hostname interne) et B (chemin interne) écartées (A coûteuse en certs ; B sans signature native).
  3. Distinguer trois plans (ne pas mélanger) : routage navigateur = Edge (ADR-0018) ; routage backend = Bus ; propagation du contexte env+couloir = contexte signé.
  4. Sprint 0 immédiat (amendement 2026-06-21) : on ne diffère plus « au scénario intermédiaire ». ADR-0017/0018/0019 sont acceptées, le couloir vit déjà côté Edge ; tant que le Bus l'ignore, un appel inter-app traverse silencieusement les couloirs — porte ouverte qu'on prétend fermée. La primitive (V3 HKDF + LaneContext + filtre strict + tests cross-lane) est posée sans attendre le premier usage actif.

Versioning HMAC — V3 explicite (amendement 2026-06-21)

Aspect V2 (actuel) V3 (cible ADR-0020)
HKDF salt dynors-sly-transit-v1 dynors-sly-transit-v3
Payload HMAC timestamp:requestId:path:sourceTech (4 champs) timestamp:requestId:path:sourceTech:env:lane (6 champs)
Header signature X-Sly-Signature-V2 X-Sly-Signature-V3
Headers contexte X-Source-Tech X-Source-Tech + X-Sly-Environment + X-Sly-Lane
Modes SlyTransitFilter SHARED | DUAL | PER_APP + DUAL_V2_V3 (migration) et PER_APP_V3 (cible)

Migration : mode DUAL_V2_V3 accepté pendant la fenêtre de migration ; cible PER_APP_V3 ; date butoir Gate SIRRAT 2026-11-30 (refus de promotion vers int/rmoa/prod d'une app non V3).

Politique de réception cross-lane (amendement 2026-06-21)

Cas Comportement
lane_appelant == lane_local Acceptation (cas normal).
lane_appelant != lane_local, pas de whitelist Refus 403 (SLY_CROSS_LANE_DENIED) + audit.
lane_appelant != lane_local, whitelist active Acceptation + audit + label MDC cross_lane=true.
Aucun X-Sly-Lane reçu, mode PER_APP_V3 Refus 401 (SLY_LANE_MISSING).
Aucun X-Sly-Lane reçu, mode DUAL_V2_V3 (rétro-compat) Acceptation uniquement si signature V2 valide ; warning logged.

Whitelist : nouvelle table SIRRAT sly_cross_lane_exception(source_app, target, source_lane, target_lane, reason, valid_until, created_by, signature_hash), gérée par maker-checker (action sensible, même règle qu'ADR-0010) ; jamais modifiable depuis une app. Pas de mode permissif global hors local/dev.

Source du lane côté appelant (amendement 2026-06-21)

Trois cas, tous couverts par les composants à livrer Sprint 0/1 :

Cas Source de lane Composant
Backend reçoit une requête HTTP entrante Header signé X-Sly-Lane vérifié par SlyTransitFilter, posé dans LaneContext nouveau LaneContextFilter (core/commons)
Backend démarre un appel sans contexte entrant (cron, batch, boot) dynors.profile.lane injecté Forge (constant pour le process) InterAppClientProperties
Consommateur d'événement bus async DomainEvent.metadata.lane, restauré dans MDC par EventCorrelation.runWithContext() extension EventCorrelation.KEYS

Conséquences

Positif : impossible de cibler un couloir non autorisé depuis un client ; cohérent avec ADR-0008 ; découplé de l'URL ; primitive posée avant le premier usage (pas de dette de sécurité). Coût : étendre le HKDF/signature et la vérification Bus aux champs env+lane (V3 explicite, mode DUAL pendant migration) ; faire évoluer InterAppCallService pour injecter le contexte ; ajouter LaneContext/LaneContextFilter à core/commons ; tests de non-franchissement obligatoires en CI.

Composants impactés : - dynors-platform/selebeyone/.../SlyForwardSigningFilter (signe V3 si configuré, dual sinon). - dynors-platform/selebeyone/.../SlyTransitHkdf (ajout computeHmacV3 + salt V3). - core/commons/.../SlyTransitFilter (modes DUAL_V2_V3, PER_APP_V3 ; vérification politique cross-lane ; lecture whitelist). - core/commons/.../LaneContext + LaneContextFilter (nouveaux). - core/interapp-client/.../DefaultInterAppCallService (pose X-Sly-Environment + X-Sly-Lane). - core/interapp-client/.../InterAppClientProperties (ajout lane constant process). - core/events/.../EventCorrelation (ajout lane aux KEYS — cohérent palier P2.5d de la refonte logging). - dynors-internal/sirrat (table sly_cross_lane_exception + UI admin + Gate 2 date butoir). - Configuration logging : MDC lane + label Loki promu (cohérent §2.5 architecture logging).

Alternatives considérées

  • X-Sly-Lane non signé → écartée (franchissement d'isolation).
  • Option A (hostname interne) / Option B (chemin interne) → à comparer ; A coûteuse en certs, B sans signature native.
  • Pas de couloir en inter-app hors-prod (couloirs uniquement navigateur) → option de repli valable au scénario minimal : l'inter-app reste mono-cible par environnement tant que le besoin de couloirs inter-app n'est pas avéré.

Plan d'exécution (amendé 2026-06-21 — Sprint 0 immédiat)

Sprint 0 — Primitive de sécurité (prérequis sans usage actif)

  1. core/commons : LaneContext (set/get/clear) + LaneContextFilter (OncePerRequestFilter, ordre juste après TenantContextFilter) ; valeur par défaut "ref" si pas de header signé.
  2. dynors-platform/selebeyone : SlyTransitHkdf.computeHmacV3() + nouveau salt dynors-sly-transit-v3.
  3. core/commons : SlyTransitFilter modes DUAL_V2_V3 et PER_APP_V3 ; lecture whitelist (depuis sly_cross_lane_exception).
  4. Tests de non-franchissement obligatoires en CI (3 scénarios : header forgé sans signature, signature valide mais lane mal annoncée, cross-lane sans whitelist → 401/403).

Sprint 1 — Émission + propagation bus

  1. core/interapp-client : DefaultInterAppCallService injecte X-Sly-Environment (constant process via InterAppClientProperties.lane) + X-Sly-Lane (depuis LaneContext.get()) avant le calcul de signature ; passage de computeHmacV3 côté appelant pour compat des tests.
  2. dynors-platform/selebeyone : SlyForwardSigningFilter signe en V3 si forwardRootKey et mode autorise V3 ; émet X-Sly-Signature-V3 + X-Sly-Environment + X-Sly-Lane.
  3. core/events : EventCorrelation.KEYS += "lane" (palier P2.5d cohérent refonte logging).
  4. Configuration logging : MDC lane + label Loki promu (déjà acté §2.3, §2.5 architecture logging).

Sprint 2 — Pilote PAIEMENT ↔ FISCAL

  1. Activation DUAL_V2_V3 sur PAIEMENT et FISCAL (sensibilité haute, justifie l'investissement). Tests E2E sur cercle int, validation des traces, audit des tentatives cross-lane bloquées.

Sprint 3 — Whitelist + outillage SIRRAT

  1. Table sly_cross_lane_exception (Liquibase) + UI admin SIRRAT (rôle SIRRAT_PLATFORM_ENGINEER) ; CRUD maker-checker (cf. ADR-0010 §2).
  2. Métriques Prometheus : sly_cross_lane_denied_total, sly_lane_missing_total, sly_signature_v3_validated_total.

Sprint 4 — Extension et bascule

  1. Activation DUAL_V2_V3 sur les autres apps (BOOKS, TRACIUM, NOTIFY, SuperGest, Medisen, JARAAF, DAWALALE…).
  2. 2026-11-30 — Date butoir : Gate SIRRAT 2 refuse promotion vers int/rmoa/prod d'une app encore en SHARED ou PER_APP (V2 seul). Cible atteinte : PER_APP_V3 partout.

Suivi

Critères de succès : - Aucun couloir inter-app sélectionnable via en-tête non signé (test cross-lane CI rouge sans secret). - Contexte env+lane présent dans la signature V3, dans les MDC, dans les traces, dans DomainEvent.metadata, dans les labels Loki. - Whitelist ne contient que des exceptions tracées (maker+checker, motif, expiration).

Signaux de régression : - Une app accepte un X-Sly-Lane sans X-Sly-Signature-V3 valide (régression vers header libre). - Une app reste en mode SHARED/PER_APP V2 après 2026-11-30 en int/rmoa/prod. - Une exception cross-lane sans valid_until ou sans signature_hash.

Amendements

Date Amendement Origine
2026-06-21 Headers renommés X-Sly-Environment + X-Sly-Lane (cohérence préfixe X-Sly-*). Revue critique.
2026-06-21 Versioning V3 explicite (salt dynors-sly-transit-v3, payload 6 champs, header X-Sly-Signature-V3, modes DUAL_V2_V3/PER_APP_V3). Revue critique.
2026-06-21 Politique cross-lane stricte par défaut + whitelist SIRRAT maker-checker ; pas de mode permissif global. Revue critique.
2026-06-21 LaneContext + LaneContextFilter dans core/commons (patron TenantContextFilter). Revue critique.
2026-06-21 DefaultInterAppCallService injecte env+lane avant signature. Revue critique.
2026-06-21 EventCorrelation.KEYS += lane (cohérent palier P2.5d logging). Revue critique.
2026-06-21 Tests de non-franchissement obligatoires en CI (3 scénarios). Revue critique.
2026-06-21 MDC lane + label Loki promu (cohérent §2.5 logging). Revue critique.
2026-06-21 Date butoir Gate SIRRAT 2 : 2026-11-30 refus de SHARED/V2 seul hors local/dev. Revue critique.
2026-06-21 Statut Proposée → Acceptée + Sprint 0 immédiat (pas différé) : sécurité d'abord. Revue critique.

Références

  • ADR-0008 (transit SLY V2 HKDF) — primitive sur laquelle on étend
  • ADR-0017 (couloir/lane unité d'exécution) — origine de la dimension
  • ADR-0018 (exposition hors-prod par chemin) — couloir côté Edge
  • ADR-0019 (modèle SIRRAT × couloir) — persistance de la dimension
  • ADR-0009 amendée (axe gouvernance, deployment_audience) — composition profil
  • ADR-0010 amendée (maker-checker) — modèle de gouvernance pour la whitelist cross-lane
  • Architecture logging §2.3, §2.5 — lane MDC + label Loki promu
  • Code : dynors-platform/selebeyone/.../SlyForwardSigningFilter.java, SlyTransitHkdf.java
  • Code : core/packages/core/commons/.../SlyTransitFilter.java
  • Code : core/packages/core/interapp-client/.../DefaultInterAppCallService.java
  • Code : core/packages/core/events/.../EventCorrelation.java