Runbook SOC, AI-SDR, Kill Switch et reprise
English version: en/soc-runbook.md
Section 11 du kit. Code : services/control-plane/src/eai/domains/soc (routes /v1/soc/*, voir le contrat API, section SOC, AI-SDR and Kill Switch), moteur de risque eai/domains/risk/engine.py, moteur de politique de réponse deploy/policies/opa/soc_response.rego, migrations sql/postgres/V120 à V122, contrats security-incident et containment-order, tests services/control-plane/tests/test_soc_ai_sdr.py et test_soc_runtime_rules.py.
Composants
| Composant | Où | Rôle |
|---|---|---|
| Detection Ingestor | soc/detections.py, soc/webhooks.py, soc/jobs.py (job soc_scan) | Normalise chaque source en une détection : internes (guard, DLP, egress, quotas, plugins, runtime, authz, politiques, audit, connaissance, MCP, lus dans la chaîne d'audit après un filigrane par tenant) et externes (Falco, Tetragon, OpenSearch Security Analytics par webhooks signés HMAC) |
| Event Correlator | soc/detections.py | Même utilisateur, sinon même agent, sinon détections de niveau tenant dans EAI_SOC_CORRELATION_WINDOW_SECONDS : un seul incident ouvert (catégorie soc) |
| Risk Engine | domains/risk/engine.py | Sévérité, confiance, score et liste des raisons, conservés sur la détection et dans la chronologie de l'incident |
| Incident Manager | domains/incidents | Cycle de vie open, investigating, contained, resolved, closed, assignation, chronologie, envoi au SIEM |
| Response Policy Engine | deploy/policies/opa/soc_response.rego, soc/response.py | Réaction et cible de la liste autorisée, plafond de rayon d'impact, garde de proportionnalité. OPA quand EAI_OPA_URL est défini, sinon les mêmes règles en processus (un test garde les deux identiques) |
| Kill Switch Registry | eai.kill_switches (PostgreSQL) | Source de vérité : desired_state SUSPENDED ou RELEASED, observed_state PENDING, APPLIED_PENDING_RECEIPT, SUSPENDED_VERIFIED, VERIFICATION_FAILED, RELEASED |
| Enforcement Adapters | soc/adapters.py | Un chemin de code fixe par réaction typée, jamais une commande |
| Containment Verifier | soc/jobs.py (job soc_verify) | Re-sonde chaque kill switch actif, réapplique un confinement qui a dérivé, enregistre un reçu à chaque fois |
| Recovery Controller | soc/recovery.py | RECOVER : deux approbateurs distincts, différents du demandeur, puis ré-autorisation de la cible |
Où une suspension s'applique
- Point de décision de politique (
eai.policy.evaluate, chaque décision des passerelles, du runtime, des connecteurs, des modèles et des outils) : une décision dont le sujet, la ressource, le run, la session, l'agent, l'application, le fournisseur ou le tenant est suspendu est refusée avant tout moteur, avec la raisonkill_switch:<REACTION>:<type>:<ref>:<id>. Une panne d'OPA ou une règle permissive du tenant ne la lève pas. - Authentification (
eai.auth.current_principal) :REVOKE_CREDENTIAL(401credential_revoked, les clés d'API sont aussi révoquées danseai.api_keys),SUSPEND_IDENTITY(403identity_suspended, pour tout type d'identifiant, y compris un JWT encore valide),SUSPEND_TENANT(403tenant_suspended; un administrateur plateforme entre encore pour la reprise). - Runtime des agents : les étapes et les sous-agents appellent d'abord
eai.policy.kill_switch_guard; le refus est un 403policy_deniedavec undecision_idenregistré et leskill_switch_ids.CANCEL_RUNetCANCEL_SUBAGENTSannulent les runs ;SUSPEND_AGENTsuspend l'agent etPOST /v1/agents/{id}/resumerépond 409kill_switch_activejusqu'à la reprise. - Cluster (
ISOLATE_NETWORK,TERMINATE_WORKLOAD) : l'ordre produit une CiliumNetworkPolicy (refus en entrée et en sortie) ou un patch de mise à zéro réplica pour le chemin GitOps ; le kill switch resteAPPLIED_PENDING_RECEIPTjusqu'à ce qu'un rapporteur dans le cluster poste un reçu signé surPOST /v1/soc/receipts.
Tout est relu dans PostgreSQL à chaque décision : le redémarrage d'une réplique ne reprend jamais une cible suspendue (test_kill_switches_persist_across_restart).
Garde de proportionnalité (critères définis à l'avance)
Valeurs dans deploy/policies/opa/data.json (soc_response), reprises par soc/response.py :
- automatique seulement pour
BLOCK_REQUEST,RATE_LIMIT,REQUIRE_APPROVAL,CANCEL_RUN,CANCEL_SUBAGENTS,SUSPEND_AGENT,DISABLE_TOOL,QUARANTINE_CONNECTOR,REVOKE_CREDENTIAL,DISABLE_MODEL_ROUTE, avec une confiance d'au moins 0,9 et un rayon d'impact d'au plus 5 ; - un ordre manuel au-delà d'un rayon d'impact de 25 attend une seconde personne ;
EMERGENCY_LOCKDOWNetSUSPEND_TENANTattendent toujours une seconde personne ;- un ordre au-delà de son
max_blast_radiusest refusé (409containment_refused, raisonblast_radius_exceeded) ; - OPA injoignable : les règles en processus décident et aucun ordre n'est automatique (raison
policy_engine_unavailable).
Le rayon d'impact est compté, jamais deviné : un agent vaut 1 plus ses runs actifs, un run 1 plus ses sous-agents actifs, un tenant 1 plus ses membres et ses agents.
Plans séparés (SECU-021)
- L'alerte (envoi au SIEM, Alertmanager) n'est jamais sur le chemin d'application : un SIEM en panne n'arrête ni un incident ni un confinement.
- L'application ne dépend pas d'OPA : le registre des kill switches est lu avant tout moteur.
- L'audit est écrit dans la même transaction que chaque changement : si l'écriture d'audit échoue, le confinement est annulé avec elle, jamais appliqué sans audit.
Rôles et accès
Les rôles SOC sont fixés par eai.domains.soc.catalog : analyste SOC security_officer ou soc_analyst (rôle ajouté par la migration V161 ; tri, ordres, demandes de reprise, scellement de preuves) ; responsable SOC security_officer ou tenant_admin (approbations, reprise, rapprochement) ; lecteurs des consoles SOC security_officer, tenant_admin, auditor, soc_analyst ; lecteurs de preuves security_officer, tenant_admin, auditor ; rapporteurs de détections security_officer, agent, soc_analyst (portée soc:detections). Quatre yeux signifie toujours deux personnes distinctes. Chaque lecture des consoles SOC est auditée (soc.access, soc.evidence.access), chaque refus aussi (soc.access.deny, que le scanner transforme en détection privilege_escalation).
Preuves et chaîne de possession (AUDI-017)
Chaque détection, ordre, reçu et reprise d'un incident est scellé dans eai.incident_evidence (empreinte du contenu, empreinte précédente, chaîne par incident). Chaque scellement, lecture et export est une entrée de eai.incident_custody (seconde chaîne : qui, quand, quoi). Les deux tables refusent UPDATE et DELETE (déclencheur). GET /v1/soc/incidents/{id}/evidence et /custody recalculent les deux chaînes. POST /v1/soc/incidents/{id}/evidence/export cherche d'abord les secrets dans le lot : 422 secret_detected (type et chemin seulement, jamais la valeur) et une détection secret_leak.
Sûreté des transactions (SECU-023, RELE-007)
Les appels à effet de bord passent par eai.domains.soc.transactions.execute avec une clé d'idempotence et un identifiant de transaction : contrôle des kill switches, intention durable (in_flight) validée avant l'appel, puis le résultat. Une nouvelle tentative avec la même clé renvoie le résultat conservé et n'appelle jamais le fournisseur une seconde fois. Un confinement qui annule un run ou suspend un agent marque ses appels en cours interrupted : le responsable SOC les rapproche (POST /v1/soc/transactions/{id}/reconcile, résultat committed ou not_executed) avec la référence du fournisseur ; une nouvelle tentative à l'aveugle est refusée (409 transaction_needs_reconciliation).
Procédure : contenir
- Lire l'incident :
GET /v1/incidents/{id}(chronologie, raisons du risque),GET /v1/soc/detections?incident_id=. - Ordres automatiques en attente :
GET /v1/soc/orders?status=pending_approval; approuver avec une seconde personne :POST /v1/soc/orders/{id}/approve, ou rejeter. - Confinement manuel :
POST /v1/soc/ordersavec la réaction et la cible les plus étroites, et unmax_blast_radius. - Vérifier la preuve : l'ordre porte son kill switch ;
observed_statedoit valoirSUSPENDED_VERIFIED. PourISOLATE_NETWORKetTERMINATE_WORKLOAD, validermanifestdans le dépôt GitOps ; l'état ne devient vérifié qu'avec le reçu signé du cluster. - Re-sonder à tout moment :
POST /v1/soc/kill-switches/{id}/verify.VERIFICATION_FAILEDsignifie que l'effet n'est pas prouvé : escalader. - Transactions en cours :
GET /v1/soc/transactions?status=interrupted, rapprocher chacune avec le fournisseur.
Procédure : reprise (quatre yeux)
- Demander :
POST /v1/soc/recoveries {kill_switch_id, reason}. - Deux responsables SOC, tous deux différents du demandeur, approuvent :
POST /v1/soc/recoveries/{id}/approve. - L'un d'eux ré-autorise :
POST /v1/soc/recoveries/{id}/reauthorize. Le kill switch est levé, la cible est réévaluée par le point de décision (403reauthorization_deniedconserve le kill switch), puis restaurée (un agent est relancé). Une clé d'API révoquée le reste : émettre une nouvelle clé. - Clore l'incident :
PATCH /v1/incidents/{id} {status: resolved}puisclosed; exporter les preuves si besoin.
Réglages
| Réglage | Défaut | Rôle |
|---|---|---|
EAI_SOC_WEBHOOK_SECRET | vide | Secret HMAC des webhooks SOC et des reçus du cluster (vide : toute livraison refusée). 32 caractères minimum en prod (contrôlé au démarrage) |
EAI_SOC_CORRELATION_WINDOW_SECONDS | 900 | Fenêtre de corrélation (60 à 86400, contrôlée au démarrage en prod) |
EAI_WORKER_SOC_SCAN_SECONDS | 30 | Intervalle du job soc_scan du worker |
EAI_WORKER_SOC_VERIFY_SECONDS | 120 | Intervalle du job soc_verify du worker (Containment Verifier) |
Falco et Tetragon : BLOQUÉ sans cluster
deploy/runtime-security/falco/eai-platform-rules.yaml (shell, lecture de secrets montés, sortie publique des agents, élévation de privilèges, dérive de binaires dans la sandbox) et deploy/runtime-security/tetragon/eai-tracing-policies.yaml (observation dans enterprise-ai, Sigkill dans eai-sandbox) sont validés hors ligne seulement (test_soc_runtime_rules.py : structure, références, namespaces, correspondance avec les types de détection). Aucun schéma kubeconform n'existe hors ligne pour eux. Leur effet à l'exécution (une alerte levée par un vrai appel système, un processus tué) exige un vrai cluster avec Falco et Tetragon : BLOQUÉ (responsable : l'opérateur plateforme du cluster cible ; critère de sortie : une alerte de test de chaque règle reçue par POST /v1/soc/webhooks/falco et un Sigkill mesuré par Tetragon). Falcosidekick et le webhook d'alerte d'OpenSearch ne calculent pas eux-mêmes la signature HMAC X-EAI-Signature : un relais de signature dans le cluster est un prérequis de déploiement, tout comme le rapporteur de reçus des adaptateurs Cilium et workload ; les deux sont BLOQUÉS avec le cluster.
Réactions typées et cibles
Les réactions et les cibles admises sont des listes fermées de eai.domains.soc.catalog ; un ordre hors liste est refusé. RECOVER n'est pas un ordre : c'est le circuit de reprise à quatre yeux.
| Réaction | Effet | Automatique possible |
|---|---|---|
BLOCK_REQUEST | Refuse la requête visée | oui |
RATE_LIMIT | Limite le débit de la cible | oui |
REQUIRE_APPROVAL | Exige une approbation humaine avant l'action | oui |
CANCEL_RUN | Annule le run | oui |
CANCEL_SUBAGENTS | Annule les sous-agents d'un run | oui |
SUSPEND_AGENT | Suspend l'agent jusqu'à la reprise | oui |
SUSPEND_APPLICATION | Suspend une application gouvernée | non |
DISABLE_TOOL | Désactive un outil | oui |
QUARANTINE_CONNECTOR | Met un connecteur en quarantaine | oui |
REVOKE_CREDENTIAL | Révoque un identifiant (401 credential_revoked) | oui |
DISABLE_MODEL_ROUTE | Désactive une route de modèle | oui |
ISOLATE_NETWORK | CiliumNetworkPolicy de refus en entrée et en sortie (GitOps, reçu signé) | non |
TERMINATE_WORKLOAD | Mise à zéro réplica (GitOps, reçu signé) | non |
SUSPEND_IDENTITY | Suspend une identité, quel que soit l'identifiant (403 identity_suspended) | non |
SUSPEND_TENANT | Suspend le tenant (403 tenant_suspended), toujours avec une seconde personne | non |
EMERGENCY_LOCKDOWN | Verrouillage d'urgence, toujours avec une seconde personne | non |
Cibles : request, session, run, agent, subagent, application, tool, plugin, mcp, connector, knowledge_base, model, credential, identity, tenant, workload, service. Le catalogue complet se lit avec GET /v1/soc/catalog.
Lire le registre des kill switches
GET /v1/soc/kill-switches liste chaque kill switch avec desired_state et observed_state. Un kill switch actif dont l'état observé n'est pas SUSPENDED_VERIFIED n'est pas prouvé : relancer la vérification (POST /v1/soc/kill-switches/{switch_id}/verify) et, pour un confinement de cluster, contrôler l'arrivée du reçu signé (POST /v1/soc/receipts).
Pages liées
- Circuits d'approbation et règles des quatre yeux : approvals.md.
- Preuves, sceaux et chaîne de possession : audit-evidence.md, risk-compliance.md.
- Fichiers bloqués transformés en détections : file-security-pipeline.md.
- Vue d'ensemble de la gouvernance : governance.md.