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 (vedicharts/soctalk-system/templates/60-secrets.yamlper 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 documentata — IntegrationConfig.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)
| Secret | Materiale | Posizione | Utilizzato da | Rotazione |
|---|---|---|---|---|
soctalk-system-postgres-admin-creds | user/pw | ns soctalk-system | Solo container db-init del pod API (migrazioni + bootstrap) | Manuale |
soctalk-system-postgres-app-creds | user/pw | ns soctalk-system | Pod API (runtime, soggetto a RLS) | Manuale |
soctalk-system-postgres-mssp-creds | user/pw | ns soctalk-system | Pod API (query cross-tenant system_context()) | Manuale |
soctalk-system-jwt-signing-key | secret HMAC | ns soctalk-system | Pod API | Manuale |
soctalk-system-adapter-signing-key | chiave HMAC | ns soctalk-system | Pod API (emette token adapter per-tenant) | Manuale |
soctalk-system-bootstrap-admin | email + password | ns soctalk-system | Solo container db-init del pod API | Manuale |
soctalk-system-llm-api-key | chiavi API del provider (anthropic-api-key + openai-api-key) | ns soctalk-system | Pod API (default a livello di installazione) | Manuale |
adapter-token | bearer token | ns tenant-<slug> | Pod adapter del Tenant | Emesso al provisioning; rotazione tramite ri-provisioning |
runs-worker-token | bearer token | ns tenant-<slug> | Pod runs-worker del Tenant (chiama /api/internal/worker/runs/*) | Come sopra |
tenant-llm-key | chiave API LLM | ns 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>-llm | chiave API LLM (copia legacy / di audit) | ns soctalk-system | Non montato da alcun pod V1 | Come sopra; questa copia viene scritta per l'audit ma non è la fonte autoritativa letta dal runs-worker |
wazuh-authd-secret | shared secret | ns tenant-<slug> | Wazuh manager (enrollment) | Rigenera per forzare il re-enrollment di tutti gli agent |
wazuh-<slug>-wazuh-creds | user/pw | ns 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_plainin Postgres. - Fonte montata:
Secret/tenant-llm-keyintenant-<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 riavviaDeployment/soctalk-runs-workeraffinché 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:
- Il controller genera valori casuali (es.
openssl rand -hex 32). - Il controller crea un
Secretnel ns di destinazionetenant-<slug>. - Il controller registra il riferimento
(tenant-<slug>, wazuh-bootstrap, v1)nella tabellaTenantSecret. - Il controller rende i values del chart tenant referenziando il Secret per nome.
helm installprocede; 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:
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)
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-keyintenant-<slug>(non nel namespace di sistema). - Il controller riavvia
Deployment/soctalk-runs-workernel 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.
- Store autoritativo aggiornato in Postgres (
Rotazione delle credenziali admin di Wazuh / TheHive / Cortex (manuale, runbook):
kubectl patch secret <name> -n tenant-<slug> ...per riscrivere la credenziale.kubectl rollout restartdel workload interessato affinché la rilegga.- Una CLI wrapper per questo (
soctalk-cli rotate-admin) era documentata in bozze precedenti ma non è implementata in V1.
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).
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-apiinsoctalk-system: può leggere i Secret insoctalk-system(credenziali Postgres, chiavi di firma JWT/adapter). È inoltre autorizzato a scrivere Secret nei namespacetenant-*(necessario per creare/ruotare i secret di bootstrap dei tenant) — il chart V1 consolida i ruoli API + controller in questo SA. ServiceAccountper-tenant intenant-<slug>: può leggere solo i secret nel proprio namespace. Può leggere i propriadapter-token/runs-worker-token/tenant-llm-key, ma mai la chiave di firma di sistema.- Il
soctalk-orchestrator-sadelle 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.secretKeyRefnei 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
ExternalSecretreferenziano 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).
