Skip to content

Secret Placement Policy

Invariant

No raw secret material in the SocTalk database. Postgres tables that track secrets store references only: (namespace, name, version_label). The material itself is always in a Kubernetes Secret object, mounted into the pod that needs it. This:

  • Limits blast radius of a SocTalk DB compromise (no material leaks).
  • Lets K8s-native rotation mechanisms work (Secret update → pod picks up new value on remount or on next Secret read).
  • Aligns with External Secrets Operator integration path in a future release.

Secret inventory

SecretMaterialLocationAccessed byRotationNotes
Postgres credentials (admin)user/pwSecret/soctalk-postgres-admin-creds in soctalk-systemAlembic Job onlyManual; runbook in a future releaseNot mounted to long-running pods
Postgres credentials (app)user/pwSecret/soctalk-postgres-app-creds in soctalk-systemSocTalk API + orchestrator podsManual
Postgres credentials (mssp)user/pwSecret/soctalk-postgres-mssp-creds in soctalk-systemSocTalk API pod onlyManualUsed only inside system_context()
User JWT signing keyHMAC secret or RSA/Ed25519 private keySecret/soctalk-jwt-signing-key in soctalk-systemSocTalk API podManual; automated in a future releaseHMAC for MVP simplicity; asymmetric when multi-pod
Adapter token signing keyHMAC keySecret/soctalk-adapter-signing-key in soctalk-systemSocTalk API + controller pod onlyManual; automated in a future releaseNever mounted into tenant namespaces
Tenant adapter bearer tokenbearer tokenSecret/adapter-token in tenant-<slug>Tenant adapter podMinted on tenant provisioning; rotated by controllerTenant-bound; cannot mint other tokens
Per-tenant LLM API keybearer tokenSecret/tenant-<id>-llm in soctalk-systemOrchestrator pod (projected by tenant context)MSSP-initiated via tenant config UI → SocTalk rewrites SecretNever appears in any other tenant's context
Per-tenant Wazuh API credentialsuser/pw or tokenSecret/tenant-<id>-wazuh in soctalk-systemOrchestrator MCP subprocess (env at spawn time, tenant context)MSSP-initiated
Per-tenant TheHive API tokentokenSecret/tenant-<id>-thehive in soctalk-systemOrchestrator MCP subprocessMSSP-initiated
Per-tenant Cortex API keytokenSecret/tenant-<id>-cortex in soctalk-systemOrchestrator MCP subprocessMSSP-initiated
Wazuh manager admin password (per tenant)passwordSecret/wazuh-bootstrap in tenant-<slug>Wazuh Deployment pods in that namespaceRunbook; automated in a future releaseGenerated at tenant provisioning time
TheHive admin credentials (per tenant)user/pwSecret/thehive-bootstrap in tenant-<slug>TheHive Deployment podsRunbook
Cortex admin credentials (per tenant)user/pwSecret/cortex-bootstrap in tenant-<slug>Cortex Deployment podsRunbook
Wazuh inter-service certs (manager↔indexer↔dashboard mTLS)X.509Secret/wazuh-certs in tenant-<slug>Wazuh pods in that namespaceGenerated once at tenant provisioning; rotation a future releaseOften via cert-manager per-tenant Issuer
Wazuh agent enrollment secretshared secretSecret/wazuh-authd-secret in tenant-<slug>Wazuh manager (for enrollment)Regenerate to force re-enrollment of all agentsDistributed to customer-endpoint admins out-of-band during onboarding
Cassandra credentials (TheHive-embedded)user/pwSecret/cassandra-creds in tenant-<slug>Cassandra StatefulSet + TheHiveRunbook
License material (a future release)signed JWTSecret/soctalk-license in soctalk-systemSocTalk API podIssued by Cloud; drop into Secret manuallySingle file; kubectl patch secret for refresh

Cross-namespace mounting: not done

SocTalk orchestrator runs in soctalk-system and needs per-tenant LLM keys. Options considered:

  1. Store per-tenant keys in tenant-<slug> ns and mount cross-namespace. K8s doesn't support cross-namespace Secret references in volume mounts. Workarounds exist (CSI driver, external-secrets) but add complexity.
  2. Store per-tenant keys in soctalk-system ns: straightforward; orchestrator lives here. Naming convention (tenant-<id>-llm) keeps them separated. Chosen.

The alternative (per-tenant ns for each tenant's LLM key) is re-evaluated in a future release with External Secrets Operator, where ESO can sync external-vault-stored secrets into whichever namespace needs them.

Data plane bootstrap secrets

Wazuh/TheHive/Cortex admin credentials live in their respective tenant namespaces because:

  • These pods need them at startup (init containers, first-run setup).
  • Cross-ns mounting complications as above.
  • Blast radius of namespace compromise already exposes the pods themselves; putting the bootstrap secret in the same namespace doesn't add risk.

Bootstrap secrets are generated by the SocTalk controller at tenant-provisioning time:

  1. Controller generates random values (e.g., openssl rand -hex 32).
  2. Controller creates Secret in target tenant-<slug> ns.
  3. Controller records the reference (tenant-<slug>, wazuh-bootstrap, v1) in TenantSecret table.
  4. Controller renders tenant chart values referencing the Secret by name.
  5. helm install proceeds; data plane pods read creds at startup.

If the material is lost (e.g., Secret deleted), re-provisioning regenerates new credentials. Data plane pods restart; any dependent services reinitialize. Customer-endpoint agents (which rely on the Wazuh enrollment secret) need re-enrollment if that specific secret rotates: documented in the ops runbook.

Secret generation conventions

At tenant-provisioning time, SocTalk controller generates:

python
import secrets

# Administrative passwords: 32-char high-entropy
wazuh_admin_pw = secrets.token_urlsafe(32)
thehive_admin_pw = secrets.token_urlsafe(32)
cortex_admin_pw = secrets.token_urlsafe(32)

# Enrollment shared secret: 48-char
wazuh_authd = secrets.token_urlsafe(48)

# API tokens (for SocTalk → data plane): 48-char
thehive_api_token = secrets.token_urlsafe(48)
cortex_api_key = secrets.token_urlsafe(48)

# Cassandra: 32-char
cassandra_pw = secrets.token_urlsafe(32)

SocTalk stores references and version labels; it does not keep the material in memory beyond the provisioning call.

Rotation (runbook; a future release automation)

For, rotation is a runbook operation:

  1. Per-tenant LLM key rotation (MSSP initiates via tenant config UI):

    • MSSP admin updates tenant's LLM config with new key.
    • SocTalk controller kubectl patch secret tenant-<id>-llm ... in soctalk-system.
    • Orchestrator pod picks up change on next worker context build (reads Secret freshly per job).
    • No pod restart.
  2. Wazuh / TheHive / Cortex admin credential rotation (runbook):

    • Operator runs SocTalk CLI (deliverable): soctalk-cli rotate-admin --tenant=acme --service=wazuh.
    • CLI generates new credential, kubectl patch secret wazuh-bootstrap ..., restarts Wazuh pods.
    • Brief service interruption per tenant.
  3. Postgres credentials rotation (runbook, a future release automation):

    • Generate new pw; ALTER ROLE soctalk_app WITH PASSWORD ... in Postgres.
    • kubectl patch secret soctalk-postgres-app-creds ....
    • Rolling restart of SocTalk API + orchestrator pods.
  4. JWT signing key rotation (a future release): zero-downtime rotation requires supporting two valid keys during transition. This release defers this; manual rotation forces a window where all users re-auth.

Access control

Kubernetes RBAC restricts which ServiceAccounts can read which Secrets:

  • soctalk-api-sa in soctalk-system: can read all Secrets in soctalk-system (Postgres creds, JWT keys, all per-tenant LLM and integration secrets, license when a future release).
  • soctalk-controller-sa in soctalk-system: can read/write Secrets in soctalk-system and in tenant-* namespaces (needed to create/rotate tenant bootstrap secrets).
  • soctalk-orchestrator-sa in soctalk-system: can read per-tenant LLM and integration Secrets in soctalk-system only.
  • Per-tenant ServiceAccount in tenant-<slug>: can read only secrets in its own namespace. It can read its own adapter-token but never the system signing key.

Role/RoleBinding templates are part of soctalk-system chart (for SocTalk SAs) and soctalk-tenant chart (for per-tenant SAs).

Anti-patterns explicitly rejected

  • Env-var secret injection from .env file (current V0 pattern): fine for single-org, not for multi-tenant. All secrets move to K8s Secrets.
  • Secrets in Helm values.yaml: never: values files end up in Git, CI logs, Helm history. SocTalk controller renders Secret objects separately and uses valueFrom.secretKeyRef in templates.
  • Single shared LLM key for all tenants: explicitly out-of-scope for BYO LLM. Per-tenant keys always.
  • Secrets in ConfigMaps: prohibited. ConfigMaps are for non-sensitive config; Secrets for sensitive.

External Secrets Operator (a future release path)

a future release introduces External Secrets Operator integration:

  • MSSP provides a secret backend (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager).
  • ExternalSecret resources reference backend paths; ESO syncs to K8s Secrets.
  • Per-tenant LLM keys stored in backend with paths like secret/mssp-abc/tenants/acme/llm.
  • Rotation done in backend; ESO propagates within refresh interval.

The structure (refs in Postgres → K8s Secret → mount) is compatible: only the Secret source changes (ESO-managed vs SocTalk-controller-written).

Released under the Apache 2.0 License.