Security
How openDesk handles secrets end-to-end: a single master password derives most application secrets via
derivePassword, ESO (External Secrets Operator) materializes them from STACKIT Secrets Manager into Kubernetes Secrets, and rendered manifests are scrubbed of plaintext before being pushed to git. Answers: "What does the master password do?", "How does ESO know which secret to pull from where?", "What happens ifSecretsManager.Endpointis empty?", "Why was a plaintext secret committed to git that one time?", "Where does the deployer store the Keycloak admin password?".Audience: anyone working with credentials, secret rotation, encryption-at-rest decisions, or debugging an ESO sync.
What lives here
- The conceptual model of openDesk's secret pipeline: master password → derivation → Secrets Manager → ESO → K8s Secrets.
- Incidents that exposed gaps in this pipeline.
What does NOT live here:
- The deployer-side code that converts Secrets to ExternalSecrets — see deployment (
fix-secrets-to-eso, the InstallESO step). - Provisioning of the Secrets Manager STACKIT resource — see infrastructure.
- Input-side validation of master password / SM credentials (whitespace, required fields) — see config.
- Per-app secret layouts (which keys Nextcloud / OX / Synapse expect) — see apps.
Pages
Concepts
- concept-master-password — 16-char minimum,
derivePasswordalgorithm, three-location storage model (instance.yaml 0600/atomic, SM<environment>/master-password, OX prev-master cache) - concept-eso — External Secrets Operator; per-tenant SecretStore in the deployment namespace; unconditional plaintext guard; module:app secret-skew guard
- concept-stackit-secrets-manager — Vault KV v2 backend; userpass auth flow; WriteKV2 (replace) vs WriteKV2Merge (read-merge-write); 60s timeout; what gets stored where
Incidents
- incident-plaintext-secrets-in-git — when rendered Secrets ended up plaintext in the rendered-manifests repo; root cause (
SecretsManager.Endpointempty) and theconvertSecretsToESOguard
Related topics
- deployment —
fix-secrets-to-esois the rendering-pipeline transform that swaps Secret docs for ExternalSecret CRs; the InstallESO step installs the operator - infrastructure — STACKIT Secrets Manager instance is provisioned by Terraform; connection details land in
runtime-state.json; the master password is stored to SM by the infraStoreMasterCredentialsstep - idp — Keycloak admin password is one of the things written into Secrets Manager by the
StoreKeycloakCredentialsstep - config —
MasterPasswordandSecretsManager.*are input fields; whitespace-handling and validation incidents live there - apps — per-app secret expectations (which keys an app's chart expects, which env vars it reads) live with the app
When to add a page here
- A new secret-related concept — key-rotation policy, KMS integration, customer-managed-keys flow (
concept-*) - A secret-rotation or recovery procedure — rotate master password, rotate SM credentials, recover from a leaked token (
runbook-*) - A security incident — leak, mis-permissioning, encryption-at-rest gap, plaintext-in-git regression (
incident-*) - A decision on secret-storage architecture — switch from Vault KV v2 to KMS, scope ESO across namespaces (
decision-*) - A new threat-model entry (
concept-*— sparingly; prefer linking to a single living threat-model doc when one exists)
Pages on the mechanics of how rendered YAML gets transformed (e.g., fix-secrets-to-eso) belong in deployment under fixes — this topic covers the "why" and the operator-facing model. Per-app secret layout (e.g., what keys Nextcloud expects in its admin-password Secret) belongs with the app in apps. Input-side validation of secret material (master-password whitespace, registry-cred whitespace) belongs in config under incidents.