Aller au contenu principal

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​

ComposantOùRôle
Detection Ingestorsoc/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 Correlatorsoc/detections.pyMê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 Enginedomains/risk/engine.pySévérité, confiance, score et liste des raisons, conservés sur la détection et dans la chronologie de l'incident
Incident Managerdomains/incidentsCycle de vie open, investigating, contained, resolved, closed, assignation, chronologie, envoi au SIEM
Response Policy Enginedeploy/policies/opa/soc_response.rego, soc/response.pyRé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 Registryeai.kill_switches (PostgreSQL)Source de vérité : desired_state SUSPENDED ou RELEASED, observed_state PENDING, APPLIED_PENDING_RECEIPT, SUSPENDED_VERIFIED, VERIFICATION_FAILED, RELEASED
Enforcement Adapterssoc/adapters.pyUn chemin de code fixe par réaction typée, jamais une commande
Containment Verifiersoc/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 Controllersoc/recovery.pyRECOVER : 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 raison kill_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 (401 credential_revoked, les clés d'API sont aussi révoquées dans eai.api_keys), SUSPEND_IDENTITY (403 identity_suspended, pour tout type d'identifiant, y compris un JWT encore valide), SUSPEND_TENANT (403 tenant_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 403 policy_denied avec un decision_id enregistré et les kill_switch_ids. CANCEL_RUN et CANCEL_SUBAGENTS annulent les runs ; SUSPEND_AGENT suspend l'agent et POST /v1/agents/{id}/resume répond 409 kill_switch_active jusqu'à 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 reste APPLIED_PENDING_RECEIPT jusqu'à ce qu'un rapporteur dans le cluster poste un reçu signé sur POST /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_LOCKDOWN et SUSPEND_TENANT attendent toujours une seconde personne ;
  • un ordre au-delà de son max_blast_radius est refusé (409 containment_refused, raison blast_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​

  1. Lire l'incident : GET /v1/incidents/{id} (chronologie, raisons du risque), GET /v1/soc/detections?incident_id=.
  2. Ordres automatiques en attente : GET /v1/soc/orders?status=pending_approval ; approuver avec une seconde personne : POST /v1/soc/orders/{id}/approve, ou rejeter.
  3. Confinement manuel : POST /v1/soc/orders avec la réaction et la cible les plus étroites, et un max_blast_radius.
  4. Vérifier la preuve : l'ordre porte son kill switch ; observed_state doit valoir SUSPENDED_VERIFIED. Pour ISOLATE_NETWORK et TERMINATE_WORKLOAD, valider manifest dans le dépôt GitOps ; l'état ne devient vérifié qu'avec le reçu signé du cluster.
  5. Re-sonder à tout moment : POST /v1/soc/kill-switches/{id}/verify. VERIFICATION_FAILED signifie que l'effet n'est pas prouvé : escalader.
  6. Transactions en cours : GET /v1/soc/transactions?status=interrupted, rapprocher chacune avec le fournisseur.

Procédure : reprise (quatre yeux)​

  1. Demander : POST /v1/soc/recoveries {kill_switch_id, reason}.
  2. Deux responsables SOC, tous deux différents du demandeur, approuvent : POST /v1/soc/recoveries/{id}/approve.
  3. 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 (403 reauthorization_denied conserve le kill switch), puis restaurée (un agent est relancé). Une clé d'API révoquée le reste : émettre une nouvelle clé.
  4. Clore l'incident : PATCH /v1/incidents/{id} {status: resolved} puis closed ; exporter les preuves si besoin.

Réglages​

RéglageDéfautRôle
EAI_SOC_WEBHOOK_SECRETvideSecret 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_SECONDS900Fenêtre de corrélation (60 à 86400, contrôlée au démarrage en prod)
EAI_WORKER_SOC_SCAN_SECONDS30Intervalle du job soc_scan du worker
EAI_WORKER_SOC_VERIFY_SECONDS120Intervalle 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éactionEffetAutomatique possible
BLOCK_REQUESTRefuse la requête viséeoui
RATE_LIMITLimite le débit de la cibleoui
REQUIRE_APPROVALExige une approbation humaine avant l'actionoui
CANCEL_RUNAnnule le runoui
CANCEL_SUBAGENTSAnnule les sous-agents d'un runoui
SUSPEND_AGENTSuspend l'agent jusqu'à la repriseoui
SUSPEND_APPLICATIONSuspend une application gouvernéenon
DISABLE_TOOLDésactive un outiloui
QUARANTINE_CONNECTORMet un connecteur en quarantaineoui
REVOKE_CREDENTIALRévoque un identifiant (401 credential_revoked)oui
DISABLE_MODEL_ROUTEDésactive une route de modèleoui
ISOLATE_NETWORKCiliumNetworkPolicy de refus en entrée et en sortie (GitOps, reçu signé)non
TERMINATE_WORKLOADMise à zéro réplica (GitOps, reçu signé)non
SUSPEND_IDENTITYSuspend une identité, quel que soit l'identifiant (403 identity_suspended)non
SUSPEND_TENANTSuspend le tenant (403 tenant_suspended), toujours avec une seconde personnenon
EMERGENCY_LOCKDOWNVerrouillage d'urgence, toujours avec une seconde personnenon

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​