Skip to main content

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 if SecretsManager.Endpoint is 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, derivePassword algorithm, 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.Endpoint empty) and the convertSecretsToESO guard
  • deployment — fix-secrets-to-eso is 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 infra StoreMasterCredentials step
  • idp — Keycloak admin password is one of the things written into Secrets Manager by the StoreKeycloakCredentials step
  • config — MasterPassword and SecretsManager.* 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.