Skip to content

Policy di collocazione dei secret

Nota sul deployment V1. Diverse voci qui sotto fanno riferimento agli "orchestrator pod" come workload distinto — nel chart V1 l'orchestrator è co-locato nel Deployment soctalk-system-api, quindi i riferimenti a "orchestrator pod" indicano il "pod API" in questa release. Anche gli specifici nomi dei Secret K8s possono variare leggermente rispetto ai nomi resi dal chart (vedi charts/soctalk-system/templates/60-secrets.yaml per la fonte di verità).

Invariante (obiettivo)

Obiettivo: nessun materiale segreto in chiaro nel database di SocTalk. Le tabelle Postgres che tracciano i secret memorizzano solo riferimenti: (namespace, name, version_label). Il materiale stesso risiede in un oggetto Secret di Kubernetes, montato nel pod che ne ha bisogno.

Oggi (V1): esiste un'eccezione documentataIntegrationConfig.llm_api_key_plain nel database memorizza le chiavi API LLM per-tenant in chiaro. Ciò è necessario perché il runs-worker legge la chiave dal proprio contesto tenant al momento della presa in carico dell'indagine, e il chart V1 non collega ancora i Secret LLM per-tenant attraverso la pod spec. Considera le credenziali Postgres come protezione di queste chiavi e ruota le chiavi del provider LLM come se fossero esposte se la credenziale del DB viene ruotata.

Le altre categorie di secret — firma JWT, ruoli Postgres, credenziali di integrazione, Wazuh authd — risiedono tutte in Secret K8s e sono referenziate per nome dal DB, non memorizzate inline. Gli obiettivi architetturali (di seguito) descrivono lo stato di destinazione per tutte le classi di secret:

  • Limita il raggio d'azione di una compromissione del DB di SocTalk (nessuna fuga di materiale).
  • Consente il funzionamento dei meccanismi di rotazione nativi di K8s (aggiornamento del Secret → il pod acquisisce il nuovo valore al rimontaggio o alla lettura successiva del Secret).
  • Si allinea con il percorso di integrazione di External Secrets Operator previsto in una release futura.

Inventario dei secret V1 (ciò che il chart rende effettivamente oggi)

SecretMaterialePosizioneUtilizzato daRotazione
soctalk-system-postgres-admin-credsuser/pwns soctalk-systemSolo container db-init del pod API (migrazioni + bootstrap)Manuale
soctalk-system-postgres-app-credsuser/pwns soctalk-systemPod API (runtime, soggetto a RLS)Manuale
soctalk-system-postgres-mssp-credsuser/pwns soctalk-systemPod API (query cross-tenant system_context())Manuale
soctalk-system-jwt-signing-keysecret HMACns soctalk-systemPod APIManuale
soctalk-system-adapter-signing-keychiave HMACns soctalk-systemPod API (emette token adapter per-tenant)Manuale
soctalk-system-bootstrap-adminemail + passwordns soctalk-systemSolo container db-init del pod APIManuale
soctalk-system-llm-api-keychiavi API del provider (anthropic-api-key + openai-api-key)ns soctalk-systemPod API (default a livello di installazione)Manuale
adapter-tokenbearer tokenns tenant-<slug>Pod adapter del TenantEmesso al provisioning; rotazione tramite ri-provisioning
runs-worker-tokenbearer tokenns tenant-<slug>Pod runs-worker del Tenant (chiama /api/internal/worker/runs/*)Come sopra
tenant-llm-keychiave API LLMns tenant-<slug>Pod runs-worker del Tenant (montato tramite secretKeyRef)Avviata dall'MSSP tramite PATCH /api/mssp/tenants/{id}/llm; il controller la materializza da IntegrationConfig.llm_api_key_plain + riavvia il runs-worker
tenant-<id>-llmchiave API LLM (copia legacy / di audit)ns soctalk-systemNon montato da alcun pod V1Come sopra; questa copia viene scritta per l'audit ma non è la fonte autoritativa letta dal runs-worker
wazuh-authd-secretshared secretns tenant-<slug>Wazuh manager (enrollment)Rigenera per forzare il re-enrollment di tutti gli agent
wazuh-<slug>-wazuh-credsuser/pwns tenant-<slug>Wazuh manager + pod linux-ep (enrollment degli agent)Generato al provisioning

Il Triage viene eseguito in soctalk-runs-worker in ciascun namespace tenant-<slug> (non nel pod API centrale). Per questo motivo i secret per-tenant vengono montati nel namespace del tenant, non in soctalk-system.

La chiave API LLM è memorizzata anche in chiaro in IntegrationConfig.llm_api_key_plain in Postgres — vedi la clausola sull'invariante sopra. Il Secret K8s viene materializzato dal valore del DB al momento del provisioning / della rotazione.

Voci obsolete da bozze precedenti (ora rimosse): tenant-<id>-wazuh, tenant-<id>-thehive, tenant-<id>-cortex, wazuh-bootstrap, thehive-bootstrap, cortex-bootstrap, cassandra-creds, soctalk-license. tenant-<id>-llm in soctalk-system esiste ancora in V1 come copia legacy/di audit, ma non è ciò che legge il runs-worker. La sezione sull'architettura sotto descrive la motivazione progettuale; solo l'inventario sopra è aggiornato.

Collocazione della chiave LLM per-tenant

Il Triage viene eseguito nel pod soctalk-runs-worker per-tenant (nel namespace tenant-<slug>), non nel pod API centrale. Per questo motivo le chiavi LLM per-tenant risiedono nel namespace del tenant:

  • Store autoritativo: IntegrationConfig.llm_api_key_plain in Postgres.
  • Fonte montata: Secret/tenant-llm-key in tenant-<slug>, materializzato dal controller a partire dal valore del DB.
  • In caso di rotazione (PATCH /api/mssp/tenants/{id}/llm): il controller riscrive il Secret nel namespace del tenant e riavvia Deployment/soctalk-runs-worker affinché la nuova chiave abbia effetto alla successiva presa in carico di un'indagine.

Secret/tenant-<id>-llm nel namespace soctalk-system esiste anch'esso come copia legacy / di audit derivante da iterazioni di design precedenti, ma non è montato da alcun pod V1. In V1 non esiste alcun mount di Secret cross-namespace.

L'alternativa (ns per-tenant per la chiave LLM di ciascun tenant) viene rivalutata in una release futura con External Secrets Operator, dove ESO può sincronizzare i secret memorizzati in un vault esterno in qualunque namespace ne abbia bisogno.

Secret di bootstrap del data plane

Le credenziali admin di Wazuh/TheHive/Cortex risiedono nei rispettivi namespace tenant perché:

  • Questi pod ne hanno bisogno all'avvio (init container, setup al primo avvio).
  • Le complicazioni del mounting cross-ns descritte sopra.
  • Il raggio d'azione di una compromissione del namespace espone già i pod stessi; collocare il secret di bootstrap nello stesso namespace non aggiunge rischio.

I secret di bootstrap vengono generati dal controller di SocTalk al momento del provisioning del tenant:

  1. Il controller genera valori casuali (es. openssl rand -hex 32).
  2. Il controller crea un Secret nel ns di destinazione tenant-<slug>.
  3. Il controller registra il riferimento (tenant-<slug>, wazuh-bootstrap, v1) nella tabella TenantSecret.
  4. Il controller rende i values del chart tenant referenziando il Secret per nome.
  5. helm install procede; i pod del data plane leggono le credenziali all'avvio.

Se il materiale viene perso (es. Secret eliminato), il ri-provisioning rigenera nuove credenziali. I pod del data plane si riavviano; gli eventuali servizi dipendenti si reinizializzano. Gli agent sugli endpoint del cliente (che dipendono dal secret di enrollment di Wazuh) necessitano di re-enrollment se quello specifico secret viene ruotato: documentato nel runbook operativo.

Convenzioni per la generazione dei secret

Al momento del provisioning del tenant, il controller di SocTalk genera:

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 memorizza riferimenti ed etichette di versione; non conserva il materiale in memoria oltre la chiamata di provisioning.

Rotazione (realtà V1)

  1. Rotazione della chiave LLM per-tenant (avviata dall'MSSP tramite PATCH /api/mssp/tenants/{id}/llm):

    • Store autoritativo aggiornato in Postgres (IntegrationConfig.llm_api_key_plain).
    • Il controller riscrive Secret/tenant-llm-key in tenant-<slug> (non nel namespace di sistema).
    • Il controller riavvia Deployment/soctalk-runs-worker nel namespace del tenant affinché la nuova chiave abbia effetto alla successiva presa in carico. Il riavvio del pod è necessario — la V1 non ricarica i secret a runtime.
  2. Rotazione delle credenziali admin di Wazuh / TheHive / Cortex (manuale, runbook):

    • kubectl patch secret <name> -n tenant-<slug> ... per riscrivere la credenziale.
    • kubectl rollout restart del workload interessato affinché la rilegga.
    • Una CLI wrapper per questo (soctalk-cli rotate-admin) era documentata in bozze precedenti ma non è implementata in V1.
  3. Rotazione delle credenziali Postgres (manuale, runbook):

    • ALTER ROLE soctalk_app WITH PASSWORD ... in Postgres.
    • kubectl patch secret soctalk-system-postgres-app-creds ... (attenzione al nome reso dal chart).
    • kubectl rollout restart deploy soctalk-system-api — in V1 non esiste un orchestrator pod separato (l'orchestrator è co-locato nel pod API).
  4. Rotazione della chiave di firma JWT (una release futura): la rotazione senza downtime richiede il supporto di due chiavi valide durante la transizione. Questa release la rimanda; la rotazione manuale impone una finestra in cui tutti gli utenti devono ri-autenticarsi.

Controllo degli accessi

L'RBAC di Kubernetes limita quali ServiceAccount possono leggere quali Secret:

  • SA soctalk-system-api in soctalk-system: può leggere i Secret in soctalk-system (credenziali Postgres, chiavi di firma JWT/adapter). È inoltre autorizzato a scrivere Secret nei namespace tenant-* (necessario per creare/ruotare i secret di bootstrap dei tenant) — il chart V1 consolida i ruoli API + controller in questo SA.
  • ServiceAccount per-tenant in tenant-<slug>: può leggere solo i secret nel proprio namespace. Può leggere i propri adapter-token / runs-worker-token / tenant-llm-key, ma mai la chiave di firma di sistema.
  • Il soctalk-orchestrator-sa delle bozze precedenti non esiste in V1 — l'orchestrator gira all'interno del pod API sotto il SA dell'API.

I template Role/RoleBinding fanno parte del chart soctalk-system (per i SA di SocTalk) e del chart soctalk-tenant (per i SA per-tenant).

Anti-pattern esplicitamente respinti

  • Iniezione di secret via variabili d'ambiente da file .env (attuale pattern V0): va bene per una singola organizzazione, non per il multi-tenant. Tutti i secret si spostano nei Secret K8s.
  • Secret nel values.yaml di Helm: mai: i file dei values finiscono in Git, nei log CI, nella history di Helm. Il controller di SocTalk rende gli oggetti Secret separatamente e usa valueFrom.secretKeyRef nei template.
  • Chiave LLM unica condivisa per tutti i tenant: esplicitamente fuori scope per il BYO LLM. Sempre chiavi per-tenant.
  • Secret nei ConfigMap: vietato. I ConfigMap sono per configurazioni non sensibili; i Secret per quelle sensibili.

External Secrets Operator (percorso per una release futura)

Una release futura introduce l'integrazione con External Secrets Operator:

  • L'MSSP fornisce un backend di secret (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager).
  • Le risorse ExternalSecret referenziano i path del backend; ESO sincronizza verso i Secret K8s.
  • Le chiavi LLM per-tenant vengono memorizzate nel backend con path come secret/mssp-abc/tenants/acme/llm.
  • La rotazione avviene nel backend; ESO propaga entro l'intervallo di refresh.

La struttura (riferimenti in Postgres → Secret K8s → mount) è compatibile: cambia solo la fonte del Secret (gestita da ESO vs scritta dal controller di SocTalk).

Rilasciato sotto la Licenza Apache 2.0.