ADR-0104: Security model — secrets, TLS, RBAC, and redaction¶
The consolidated threat model and security posture: how credentials, TLS trust, least-privilege RBAC, and payload redaction are handled across Kollect.
Theme: 01 · Foundations · Status: Current
Context¶
Security decisions were spread across many ADRs — TLS trust (ADR-0201), namespaced
isolation and SAR (ADR-0203), redaction (ADR-0303),
HTTP/API auth (ADR-0404), hub mTLS (ADR-0503) —
but there was no single model a reviewer could read to understand Kollect's posture. This ADR
consolidates it. (SECURITY.md at the repo root remains the disclosure policy; this is the
architecture.)
Threat model (what we defend against)¶
- A compromised or misconfigured tenant reading/exporting resources outside its namespace.
- Secrets (registry creds, DB passwords, tokens, kubeconfigs) leaking into exported inventory, logs, or etcd status.
- Untrusted/MITM'd sink or cluster endpoints.
- Over-broad operator RBAC enabling privilege escalation.
Decision¶
Secret handling¶
- Credentials are referenced by
secretRef, never inlined in CRD specs (ADR-0201). - Reconcilers resolve secrets and pass material via
BuildContext(SecretData,DatabaseSecretData,CAPEM) — backends never read Kubernetes secrets themselves (ADR-0406). - Secret values are never logged and never written to CR
statusor events (ADR-0602).
TLS trust¶
- Sinks and cluster connections trust a configurable CA (
caPEM) resolved from a secret/configmap. insecureSkipVerifyexists only where unavoidable (HTTP-ish backends), is off by default, and is a webhook-warned/loud opt-in. Git/object-store and hub transport require real trust — no skip.
RBAC (least privilege)¶
- The operator's
ClusterRolegrants only the verbs needed (watch/list/get on selected GVKs; CRUD on Kollect CRDs; leader-election on a single lease). - Tenant mode (ADR-0203) narrows watches to configured namespaces;
chart emits
Role/RoleBindinginstead of cluster-wide bindings (ADR-0704). - Cross-namespace reads in namespaced inventories are SubjectAccessReview-gated: missing permission degrades gracefully (skip + condition) rather than escalating.
Redaction (no secrets in payloads)¶
- Profiles redact via
scrubKeys/redaction before items enter the store (ADR-0303), so the export data contract (ADR-0405) is secret-free by construction. - Helm release values, Secret data, and known-sensitive keys are scrubbed at extraction time, not at export time.
API / HTTP exposure¶
- The read API requires auth (token/mTLS) and is namespace-scoped (ADR-0404); it is a feature-gated, off-by-default surface.
Multi-cluster¶
- Spoke→hub uses mTLS / mesh identity (ADR-0503); the hub allowlists clusters and rejects unlisted ones.
Webhook TLS (serving)¶
- Validating webhook serving certs: cert-manager default, self-signed bootstrap fallback (ADR-0105).
Supply chain¶
- Images are signed and SBOM'd; see ADR-0705.
Consequences¶
- A reviewer has one page for the posture; individual ADRs hold the detail.
- Redaction-at-extraction means a sink can never accidentally export a secret the store never held.
- SAR-gated degradation favors availability + safety over hard failure on partial permissions.
Open questions¶
- DECIDED : Encryption-at-rest for sinks (Postgres/object-store) is recommended and documented, not enforced by the operator (it's a backend/infra responsibility).
- DECIDED : Add a formal RBAC audit gate in CI (
kubeaudit-style) as a maturity signal (ADR-0705). - OPEN: A built-in secret-leak scanner over outgoing payloads as defense-in-depth beyond
scrubKeys?