From 96615b46a72e7902f7ade2619b21649bf41b2b1b Mon Sep 17 00:00:00 2001 From: ertopogo Date: Mon, 6 Apr 2026 13:50:16 +0200 Subject: documentation zero trust --- .../zero-trust/scenarios/classic-perimeter.ts | 123 ++++++++++++++++++ .../demos/zero-trust/scenarios/east-west.ts | 126 ++++++++++++++++++ src/components/demos/zero-trust/scenarios/index.ts | 18 +++ .../zero-trust/scenarios/zero-trust-access.ts | 143 +++++++++++++++++++++ 4 files changed, 410 insertions(+) create mode 100644 src/components/demos/zero-trust/scenarios/classic-perimeter.ts create mode 100644 src/components/demos/zero-trust/scenarios/east-west.ts create mode 100644 src/components/demos/zero-trust/scenarios/index.ts create mode 100644 src/components/demos/zero-trust/scenarios/zero-trust-access.ts (limited to 'src/components/demos/zero-trust/scenarios') diff --git a/src/components/demos/zero-trust/scenarios/classic-perimeter.ts b/src/components/demos/zero-trust/scenarios/classic-perimeter.ts new file mode 100644 index 0000000..6a6347d --- /dev/null +++ b/src/components/demos/zero-trust/scenarios/classic-perimeter.ts @@ -0,0 +1,123 @@ +import type { Edge, Node } from "@xyflow/react"; +import { MarkerType } from "@xyflow/react"; +import type { ZTNodeData, ZTScenarioDefinition } from "../types"; + +const node = ( + id: string, + x: number, + y: number, + data: ZTNodeData, +): Node => ({ + id, + type: "ztNode", + position: { x, y }, + data, +}); + +const edge = ( + id: string, + source: string, + target: string, + label: string, + opts?: { dashed?: boolean; color?: string }, +): Edge => ({ + id, + source, + target, + label, + animated: true, + markerEnd: { type: MarkerType.ArrowClosed, width: 18, height: 18 }, + style: opts?.dashed + ? { strokeDasharray: "6 4", stroke: opts.color ?? "#94a3b8" } + : { stroke: opts?.color ?? "#64748b" }, + labelStyle: { fill: "#334155", fontWeight: 500, fontSize: 11 }, + labelBgStyle: { fill: "#f8fafc", fillOpacity: 0.95 }, +}); + +export const classicPerimeterScenario: ZTScenarioDefinition = { + id: "classic-perimeter", + title: "Périmètre réseau classique", + subtitle: "Confiance implicite « à l’intérieur » du LAN", + intro: + "Modèle souvent associé au VPN : une fois le tunnel établi, le trafic interne est largement considéré comme fiable. Les déplacements latéraux (east-west) peuvent rester peu contrôlés.", + nodes: [ + node("user", 40, 200, { + kind: "user", + label: "Utilisateur", + role: "Identité", + }), + node("laptop", 200, 200, { + kind: "device", + label: "Poste", + role: "Appareil", + }), + node("vpn", 360, 200, { + kind: "gateway", + label: "Passerelle / VPN", + role: "Réseau", + }), + node("lan", 560, 120, { + kind: "network", + label: "LAN « de confiance »", + role: "Segment interne", + }), + node("app", 560, 40, { + kind: "workload", + label: "Application", + role: "Charge de travail", + }), + node("db", 720, 200, { + kind: "data", + label: "Données", + role: "Stockage", + }), + ], + edges: [ + edge("e-u-l", "user", "laptop", "Session locale"), + edge("e-l-v", "laptop", "vpn", "Tunnel VPN", { color: "#0d9488" }), + edge("e-v-lan", "vpn", "lan", "Accès réseau étendu"), + edge("e-lan-app", "lan", "app", "HTTP/S — confiance zone"), + edge("e-app-db", "app", "db", "SQL — souvent large confiance"), + ], + steps: [ + { + id: "s1", + title: "Vue d’ensemble", + description: + "L’utilisateur joint le réseau via une passerelle. Le segment interne est souvent traité comme un tout.", + highlightNodes: ["user", "laptop", "vpn", "lan", "app", "db"], + highlightEdges: [], + pillars: ["network"], + practices: [ + "Cartographier les flux réels (y compris east-west), pas seulement l’accès distant.", + "Ne pas confondre « chiffrement du tunnel » et « confiance des identités ».", + ], + }, + { + id: "s2", + title: "Tunnel jusqu’au périmètre", + description: + "Le VPN chiffre le transport, mais ne remplace pas une décision d’accès fine à chaque ressource.", + highlightNodes: ["laptop", "vpn"], + highlightEdges: ["e-l-v"], + pillars: ["network", "device"], + practices: [ + "Exiger posture et conformité des appareils (MDM, santé du poste).", + "Journaliser les accès au réseau et corréler avec les accès applicatifs.", + ], + }, + { + id: "s3", + title: "Confiance plate en interne", + description: + "Une fois dans le LAN, l’application parle souvent à la base avec des comptes ou règles larges : surface d’attaque east-west élevée si un poste est compromis.", + highlightNodes: ["lan", "app", "db"], + highlightEdges: ["e-lan-app", "e-app-db"], + pillars: ["application", "data", "network"], + practices: [ + "Segmenter et appliquer des politiques au plus près des charges (micro-segmentation).", + "Principe du moindre privilège sur les comptes de service et les flux base de données.", + ], + }, + ], +}; diff --git a/src/components/demos/zero-trust/scenarios/east-west.ts b/src/components/demos/zero-trust/scenarios/east-west.ts new file mode 100644 index 0000000..1200da7 --- /dev/null +++ b/src/components/demos/zero-trust/scenarios/east-west.ts @@ -0,0 +1,126 @@ +import { MarkerType } from "@xyflow/react"; +import type { ZTNodeData, ZTScenarioDefinition } from "../types"; + +const node = ( + id: string, + x: number, + y: number, + data: ZTNodeData, +): Node => ({ + id, + type: "ztNode", + position: { x, y }, + data, +}); + +const edgeOk = ( + id: string, + source: string, + target: string, + label: string, +): Edge => ({ + id, + source, + target, + label, + animated: true, + markerEnd: { type: MarkerType.ArrowClosed, width: 18, height: 18 }, + style: { stroke: "#0d9488" }, + labelStyle: { fill: "#134e4a", fontWeight: 500, fontSize: 11 }, + labelBgStyle: { fill: "#ecfdf5", fillOpacity: 0.95 }, +}); + +const edgeBlocked = ( + id: string, + source: string, + target: string, + label: string, +): Edge => ({ + id, + source, + target, + label, + animated: false, + markerEnd: { type: MarkerType.ArrowClosed, width: 18, height: 18, color: "#dc2626" }, + style: { stroke: "#dc2626", strokeDasharray: "6 4" }, + labelStyle: { fill: "#991b1b", fontWeight: 600, fontSize: 11 }, + labelBgStyle: { fill: "#fef2f2", fillOpacity: 0.95 }, +}); + +export const eastWestScenario: ZTScenarioDefinition = { + id: "east-west", + title: "Micro-segmentation east-west", + subtitle: "Contrôler les flux latéraux entre charges", + intro: + "Sans segmentation, un compromis sur un service peut se propager. Les politiques réseau et applicatives limitent qui peut parler à qui, avec observabilité centralisée.", + nodes: [ + node("svc-a", 80, 200, { + kind: "workload", + label: "Service A", + role: "Microservice", + }), + node("svc-b", 480, 200, { + kind: "workload", + label: "Service B", + role: "Microservice", + }), + node("seg", 280, 200, { + kind: "gateway", + label: "Segmentation / politique", + role: "Contrôle réseau", + }), + node("siem", 280, 40, { + kind: "monitoring", + label: "SIEM / observabilité", + role: "Journalisation", + }), + ], + edges: [ + edgeOk("e-a-s", "svc-a", "seg", "Flux autorisé et inspecté"), + edgeOk("e-s-b", "seg", "svc-b", "Suite si politique OK"), + edgeBlocked("e-a-b", "svc-a", "svc-b", "Direct — refusé"), + edgeOk("e-a-m", "svc-a", "siem", "Événements"), + edgeOk("e-b-m", "svc-b", "siem", "Événements"), + ], + steps: [ + { + id: "ew1", + title: "Flux latéraux contrôlés", + description: + "Le chemin autorisé passe par la couche de segmentation (pare-feu distribué, service mesh, politiques cloud).", + highlightNodes: ["svc-a", "seg", "svc-b"], + highlightEdges: ["e-a-s", "e-s-b"], + pillars: ["network", "application"], + practices: [ + "Définir des groupes de charges et des règles explicites (allow-list), pas un « tout ouvert » en interne.", + "Réviser régulièrement les règles avec les propriétaires métier.", + ], + }, + { + id: "ew2", + title: "Blocage du raccourci", + description: + "Une tentative de communication directe entre services sans passer par la politique est refusée et visible.", + highlightNodes: ["svc-a", "svc-b"], + highlightEdges: ["e-a-b"], + pillars: ["network", "data"], + practices: [ + "Supposer la compromission : limiter le blast radius par défaut.", + "Tester les règles (red team / tests de fuite) pour valider la segmentation.", + ], + }, + { + id: "ew3", + title: "Visibilité et corrélation", + description: + "Les journaux remontent vers une couche d’observabilité pour corréler identités, flux et alertes.", + highlightNodes: ["svc-a", "svc-b", "siem"], + highlightEdges: ["e-a-m", "e-b-m"], + pillars: ["network", "application"], + practices: [ + "Never trust, always verify : la supervision continue valide que les politiques sont effectivement appliquées.", + "Alertes sur les flux non conformes ou les nouvelles dépendances applicatives.", + ], + }, + ], +}; diff --git a/src/components/demos/zero-trust/scenarios/index.ts b/src/components/demos/zero-trust/scenarios/index.ts new file mode 100644 index 0000000..c8d557b --- /dev/null +++ b/src/components/demos/zero-trust/scenarios/index.ts @@ -0,0 +1,18 @@ +import { classicPerimeterScenario } from "./classic-perimeter"; +import { eastWestScenario } from "./east-west"; +import { zeroTrustAccessScenario } from "./zero-trust-access"; +import type { ZTScenarioDefinition } from "../types"; + +export const ZERO_TRUST_SCENARIOS: ZTScenarioDefinition[] = [ + classicPerimeterScenario, + zeroTrustAccessScenario, + eastWestScenario, +]; + +export function getScenarioById( + id: string, +): ZTScenarioDefinition | undefined { + return ZERO_TRUST_SCENARIOS.find((s) => s.id === id); +} + +export { classicPerimeterScenario, zeroTrustAccessScenario, eastWestScenario }; diff --git a/src/components/demos/zero-trust/scenarios/zero-trust-access.ts b/src/components/demos/zero-trust/scenarios/zero-trust-access.ts new file mode 100644 index 0000000..9dd3a4d --- /dev/null +++ b/src/components/demos/zero-trust/scenarios/zero-trust-access.ts @@ -0,0 +1,143 @@ +import type { Edge, Node } from "@xyflow/react"; +import { MarkerType } from "@xyflow/react"; +import type { ZTNodeData, ZTScenarioDefinition } from "../types"; + +const node = ( + id: string, + x: number, + y: number, + data: ZTNodeData, +): Node => ({ + id, + type: "ztNode", + position: { x, y }, + data, +}); + +const edge = ( + id: string, + source: string, + target: string, + label: string, + color?: string, + handles?: { sourceHandle?: string; targetHandle?: string }, +): Edge => ({ + id, + source, + target, + label, + sourceHandle: handles?.sourceHandle, + targetHandle: handles?.targetHandle, + animated: true, + markerEnd: { type: MarkerType.ArrowClosed, width: 18, height: 18 }, + style: { stroke: color ?? "#0f766e" }, + labelStyle: { fill: "#134e4a", fontWeight: 500, fontSize: 11 }, + labelBgStyle: { fill: "#ecfdf5", fillOpacity: 0.95 }, +}); + +export const zeroTrustAccessScenario: ZTScenarioDefinition = { + id: "zero-trust-access", + title: "Accès avec vérification continue", + subtitle: "Chaque requête est évaluée : identité, contexte, politique", + intro: + "Approche alignée sur NIST SP 800-207 : pas de confiance implicite au réseau. L’identité et le contexte (appareil, risque) alimentent une décision d’accès avant d’atteindre l’application et les données.", + nodes: [ + node("user", 40, 220, { + kind: "user", + label: "Utilisateur", + role: "Identité", + }), + node("device", 220, 220, { + kind: "device", + label: "Poste / mobile", + role: "Appareil", + }), + node("idp", 220, 40, { + kind: "idp", + label: "IdP (OIDC)", + role: "Authentification", + }), + node("pep", 420, 220, { + kind: "gateway", + label: "PEP / API GW", + role: "Application du contrôle", + }), + node("app", 600, 120, { + kind: "workload", + label: "Application", + role: "Charge de travail", + }), + node("data", 600, 300, { + kind: "data", + label: "Données", + role: "Stockage chiffré", + }), + ], + edges: [ + edge("e-ud", "user", "device", "Interaction", undefined, { + sourceHandle: "right-s", + targetHandle: "left-t", + }), + edge("e-di", "device", "idp", "Authorization Code + PKCE", "#0369a1", { + sourceHandle: "top-s", + targetHandle: "bot-t", + }), + edge("e-id", "idp", "device", "Jetons (access, refresh)", "#0369a1", { + sourceHandle: "bot-s", + targetHandle: "top-t", + }), + edge("e-dp", "device", "pep", "Requête + jeton", "#0f766e", { + sourceHandle: "right-s", + targetHandle: "left-t", + }), + edge("e-pa", "pep", "app", "Autorisé si politique OK", "#0f766e", { + sourceHandle: "top-s", + targetHandle: "bot-t", + }), + edge("e-ad", "app", "data", "mTLS / chiffrement", "#7c3aed", { + sourceHandle: "bot-s", + targetHandle: "top-t", + }), + ], + steps: [ + { + id: "z1", + title: "Authentification forte et contexte", + description: + "L’IdP émet des jetons après vérification de l’identité et du contexte (MFA, risque, appareil).", + highlightNodes: ["user", "device", "idp"], + highlightEdges: ["e-ud", "e-di", "e-id"], + pillars: ["identity", "device"], + practices: [ + "MFA adaptatif ; pas de secrets long-terme exposés côté client (PKCE pour les clients publics).", + "Évaluer la posture de l’appareil avant d’accorder la session.", + ], + }, + { + id: "z2", + title: "Décision de politique au point d’application", + description: + "Le PEP (Policy Enforcement Point) applique les règles : qui, quoi, depuis où, avec quel risque — avant d’atteindre la charge de travail.", + highlightNodes: ["device", "pep", "app"], + highlightEdges: ["e-dp", "e-pa"], + pillars: ["application", "network"], + practices: [ + "Séparer clairement PDP (politique) et PEP (application) dans l’architecture cible.", + "Moindre privilège : scopes OAuth/OIDC minimaux, rôles applicatifs justifiés.", + ], + }, + { + id: "z3", + title: "Protection des données jusqu’au stockage", + description: + "Les flux vers les données sont chiffrés et tracés ; l’accès reste soumis au contrôle continu (révocation, session courte, audit).", + highlightNodes: ["app", "data"], + highlightEdges: ["e-ad"], + pillars: ["data", "application"], + practices: [ + "Chiffrement au repos et en transit ; classification des données sensibles.", + "Observabilité : journaux corrélés pour détecter les abus de jetons ou les accès anormaux.", + ], + }, + ], +}; -- cgit v1.2.3