Secrets Management
Relevant source files
- infrastructure/ansible/requirements.yaml
- kubernetes/apps/cert-manager/cert-manager/app/helmrelease.yaml
- kubernetes/apps/default/paperless/app/helmrelease.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/app/certificate.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/app/clustersecretstore.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/app/externalsecret.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/app/helmrelease.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/app/issuer.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/app/kustomization.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/app/ocirepository.yaml
- kubernetes/apps/external-secrets/bitwarden-sdk-server/ks.yaml
- kubernetes/apps/external-secrets/kustomization.yaml
- kubernetes/apps/flux-system/flux-instance/app/externalsecret.yaml
- kubernetes/apps/flux-system/flux-instance/app/helmrelease.yaml
- kubernetes/apps/flux-system/flux-instance/app/kustomization.yaml
- kubernetes/apps/flux-system/flux-instance/app/prometheusrule.yaml
- kubernetes/apps/flux-system/flux-instance/app/receiver.yaml
- kubernetes/apps/flux-system/flux-instance/ks.yaml
- kubernetes/apps/flux-system/flux-operator/app/helmrelease.yaml
- kubernetes/apps/flux-system/flux-operator/app/kustomization.yaml
- kubernetes/apps/flux-system/flux-operator/ks.yaml
- kubernetes/apps/kube-system/metrics-server/app/helmrelease.yaml
- kubernetes/apps/kube-system/reloader/app/helmrelease.yaml
- kubernetes/apps/network/external-dns/app/helmrelease.yaml
- scripts/migrate_to_oci.py
The home-ops repository utilizes a multi-layered secrets management pipeline to ensure sensitive data is encrypted at rest in Git while being dynamically injected into the Kubernetes cluster. This architecture combines SOPS with Age for static encryption and the External Secrets Operator (ESO) with a Bitwarden SDK Server for dynamic secret synchronization.
Secrets Pipeline Overview
The lifecycle of a secret in this cluster follows a distinct path from creation to consumption by an application.
- Encryption at Rest: High-level bootstrap secrets are encrypted using
sopsandagebefore being committed to the repository. - External Storage: Most application secrets (API tokens, database passwords) are stored in Bitwarden.
- Synchronization: The
external-secretsoperator polls the Bitwarden SDK Server. - Injection:
ExternalSecretresources define how to map Bitwarden items to KubernetesSecretobjects. - Consumption: Applications reference these standard Kubernetes
Secretobjects viaenvFromorsecretRef.
Data Flow Diagram: Secret Synchronization
The following diagram illustrates the flow from the Bitwarden vault to a running Pod.
[Flowchart Diagram]
Sources:kubernetes/apps/external-secrets/bitwarden-sdk-server/app/clustersecretstore.yaml1-10kubernetes/apps/flux-system/flux-instance/app/externalsecret.yaml1-21kubernetes/apps/default/paperless/app/helmrelease.yaml59-61
Git-Stored Secrets (SOPS & Age)
For secrets required during the initial bootstrap of the cluster (such as the Bitwarden Access Token itself or Cloudflare API keys used by external-dns), SOPS (Secrets Operations) is used.
- Encryption Provider:
ageis the primary encryption backend. - Automation: The
community.sopsAnsible collection is included in the infrastructure requirements to handle encrypted files during host provisioning infrastructure/ansible/requirements.yaml9-10 - Flux Integration: Flux’s
kustomize-controlleris configured to decrypt SOPS-encrypted files in-cluster using a private Age key stored in a Kubernetes secret.
Sources:infrastructure/ansible/requirements.yaml9-10kubernetes/apps/flux-system/flux-instance/app/helmrelease.yaml27-30
External Secrets Operator (ESO)
The external-secrets operator is the core component for dynamic secret management. It is organized within the external-secrets namespace kubernetes/apps/external-secrets/kustomization.yaml1-12
Bitwarden SDK Server Backend
Instead of the standard Bitwarden CLI, this implementation uses a Bitwarden SDK Server. This server provides a more stable and performant API interface for the operator to communicate with the Bitwarden vault.
- ClusterSecretStore: A cluster-scoped resource named
bitwarden-fieldsacts as the bridge between the cluster and the SDK server kubernetes/apps/external-secrets/bitwarden-sdk-server/app/clustersecretstore.yaml1-10 - Authentication: The SDK server authenticates to Bitwarden using an Access Token (managed via a SOPS-encrypted secret during bootstrap).
Secure Communication (cert-manager)
Communication between the External Secrets Operator and the Bitwarden SDK Server is secured via TLS.
- Issuer: A local
Issueris defined for the SDK server kubernetes/apps/external-secrets/bitwarden-sdk-server/app/issuer.yaml1-10 - Certificate: A
Certificateresource ensures the SDK server has a valid TLS certificate, managed bycert-managerkubernetes/apps/external-secrets/bitwarden-sdk-server/app/certificate.yaml1-10
Sources:kubernetes/apps/external-secrets/kustomization.yaml4-11kubernetes/apps/cert-manager/cert-manager/app/helmrelease.yaml1-24
Mapping Bitwarden to Kubernetes
The ExternalSecret resource maps specific fields from a Bitwarden item (identified by its UUID) to keys within a Kubernetes Secret.
Implementation Example: GitHub Webhook Token
The flux-instance uses an ExternalSecret to retrieve its GitHub webhook token from Bitwarden.
| Code Entity | Value / Reference |
|---|---|
| Resource Name | github-webhook-token |
| Store Reference | bitwarden-fields (ClusterSecretStore) |
| Bitwarden UUID | b67e4583-a622-4cf2-a172-58ee67c703da |
| Property | github-webhook-token |
| Target Secret | github-webhook-token-secret |
# Simplified representation of mapping
spec:
data:
- secretKey: FLUX_GITHUB_WEBHOOK_TOKEN
remoteRef:
key: b67e4583-a622-4cf2-a172-58ee67c703da
property: github-webhook-token
Sources:kubernetes/apps/flux-system/flux-instance/app/externalsecret.yaml1-21
Application Consumption
Once the ExternalSecret is reconciled into a standard Kubernetes Secret, applications consume it using standard envFrom or secretRef patterns. For example, the paperless application loads its environment variables from paperless-secretkubernetes/apps/default/paperless/app/helmrelease.yaml59-61
To ensure pods restart when secrets are updated in Bitwarden, the cluster utilizes reloader. Applications are annotated with reloader.stakater.com/auto: "true" to trigger a rolling update upon secret change kubernetes/apps/default/paperless/app/helmrelease.yaml15-16kubernetes/apps/network/external-dns/app/helmrelease.yaml45-46
Sources:kubernetes/apps/default/paperless/app/helmrelease.yaml59-61kubernetes/apps/kube-system/reloader/app/helmrelease.yaml1-41
Entity Relationship: Secret Management Codebase
This diagram bridges the natural language concepts to the specific code entities and file locations.
[Class Diagram]
Sources:kubernetes/apps/flux-system/flux-instance/app/externalsecret.yaml1-21kubernetes/apps/external-secrets/bitwarden-sdk-server/app/clustersecretstore.yaml1-10kubernetes/apps/kube-system/reloader/app/helmrelease.yaml32-34