Security incident runbooks
Français : ../runbooks.md
The events below are emitted by eai.security.security_event and written to the tenant audit chain. General operations runbooks are in operations/en/runbooks.md.
SSRF attempts (security.egress.denied)
- Search
security.egress.deniedevents (emitted byeai.integrations.outbound.deny_event): tenant, principal, redacted URL, reason. - Identify the tool, MCP server or connector; a series of denials to
169.254.169.254, private addresses or unusual ports means an attack. - Disable the resource, revoke the principal's API key if needed (see below).
- Restrict the tenant with
egress_allowed_hosts(PUT /v1/security/settings). - Never widen
EAI_EGRESS_PRIVATE_ALLOWLISTto "unblock" a tenant: this list is for platform calls only.
Prompt injection campaign
- Follow
security.prompt_injection.detectedandsecurity.prompt_injection.blocked(LLM gateway and knowledge ingestion) andsecurity.output.link_stripped. - If the tenant is in
detect, setprompt_injection_modetoblock. - For indirect injection, find the document or source (knowledge base, tool result) and remove or reindex it.
- Keep
untrusted_content_wrapping=trueandoutput_links=strip_untrusted. - Add the observed payloads to the garak and promptfoo corpora (
deploy/ci/promptfoo.yaml).
Brute force and credential stuffing
- Follow
security.auth.failed(per IP) andsecurity.rate_limited. - Above
EAI_RATE_LIMIT_AUTH_FAILURES_PER_MINUTEfailures per minute the IP gets 429 (check_auth_lockout). - Check that
EAI_TRUSTED_PROXIESis right: otherwise every request seems to come from the gateway. - Block hostile ranges at the WAF (waf-gateway), enable MFA at the IdP.
Leaked API key
- Rotate the key:
POST /v1/api-keys/{key_id}/rotate(the new value is shown once), or revoke it:DELETE /v1/api-keys/{key_id}. - Search the audit for every action of the key since the leak.
- Run gitleaks on the repository or the leak location, purge history if needed.
- If the key gave access to tool or connector secrets, rotate them too.
WAF false positive
Follow waf-gateway: find the CRS rule in the Coraza log, add the narrowest exclusion (route and parameter) in deploy/apisix/apisix.yaml, never a global disable.
Rate limit tuning
- Measure 429 per bucket in
security.rate_limited. - Tune
EAI_RATE_LIMIT_PER_MINUTEorEAI_RATE_LIMIT_GATEWAY_PER_MINUTEin HelmcontrolPlane.config. - With several replicas, set
EAI_REDIS_URL(rediss://) for shared counters; without Redis every replica counts on its own.
Compromised tenant tool or MCP server
- Disable the tool or MCP server, revoke its OAuth tokens and rotate its secrets in OpenBao.
- Review
security.egress.deniedand the audit of the tool calls. - Restrict
egress_allowed_hosts, check that results were treated as untrusted (wrapping on).
CSP violation
- Read the browser violation report: directive, blocked source.
- An inline script without nonce or an unknown domain means XSS or an extension: do not widen the CSP without analysis.
- A legitimate resource is added in
buildCsp(portal/src/lib/security.ts), never withunsafe-inline.
Vulnerable dependency found by CI
Jobs pip-audit, npm-audit, trivy-fs, trivy-image (GitLab) or security-deps-python, security-deps-node (Forgejo) fail. Upgrade the dependency, otherwise document a dated exception. Details: ci-security-gates.
Key rotation in OpenBao
- Write the new version (
bao kv putunderkv/eai/prod/control-plane), followingcontrolPlane.secretFormats. - Let External Secrets sync, then restart the Control Plane.
- For data encryption keys:
POST /v1/encryption/keys/{key_id}/rotate. - Revoke the old version once the service is checked. Details: data-services-hardening.
Audit chain verification failure
GET /v1/audit/verify(eai.domains.observability.audit_api.verify) reports a break.- Freeze privileged write access, keep a copy of the database.
- Compare with the last signed export (
verify_export) to date the tampering. - Handle it as an insider or database compromise incident.
File blocked by the scan (security.file.blocked)
- Read the scan:
GET /v1/file-security/scans?status=blocked, thenGET /v1/file-security/scans/{scan_id}(verdict, reasons, engine, source, author). malwareorsuspicious: the SOC received a detection; check the open incident and the origin workstation or source.- Suspected false positive: scan the retained sample again (
POST /v1/file-security/scans/{scan_id}/rescan) after the signature update; the original upload is never released, the user uploads the file again. - A family of legitimate files blocked by a YARA-X rule: publish a rules version where that rule carries
action = "report", never disable an engine. - Purge the sample once the analysis is closed:
DELETE /v1/file-security/scans/{scan_id}/sample. Details: file-security-pipeline.md.
Scanning engines unavailable (security.file.scan_unavailable)
GET /v1/file-security/status: engine, endpoint, signature age.- Uploads are refused (
scan_errorverdict, fail closed): this is intended. Restore the ClamAV gateway or the YARA-X worker, check the Bearer key and the CA (EAI_FILE_SCAN_CA_FILE). - Never set
EAI_FILE_SCAN_MODEback todevin production: the startup guard refuses it.
Suspicious reveal in the IT vault (it_vault.reveal)
- Search the
it_vault.revealevents inGET /v1/audit/events: who, when, which service. - An unexplained reveal counts as a leak: rotate the service secret at its source (OpenBao, External Secrets), then update the entry (
PATCH /v1/platform-manager/vault/entries/{entry_id}). - Check the
platform_adminmembers (GET /v1/members) and the groups ofEAI_OIDC_PLATFORM_ADMIN_GROUPSin the directory. Details: secrets-vault.md.
Break glass grant open
- List the grants:
GET /v1/break-glass, thenGET /v1/break-glass/{grant_id}(requester, beneficiary, roles, justification, approvers, expiry). - Check that the justification matches a real incident; otherwise revoke:
POST /v1/break-glass/{grant_id}/revoke. Delegations created under the grant never outlive it. - Review in the audit trail every action taken during the grant (events whose
delegation.break_glass_idis the grant id). Details: identity-context-delegation.md.
Repeated separation of duties denials
- Search
approval.decidewith decisiondenyand reasonseparation_of_duties: a requester trying to approve its own request. - The same principal insisting, or an agent API key trying to approve, points to a bypass attempt: suspend the identity with a
SUSPEND_IDENTITYSOC order (soc-runbook.md). Details: approvals.md.