This page documents a minimal, working setup for integrating External Secrets Operator (ESO) with Bitwarden Secrets Manager in Kubernetes.
The focus is deliberately pragmatic:
This setup is based on real-world usage and is the configuration referenced in the accompanying blog post.
The following components are required:
cert-manager is a hard dependency for this setup. Both the External Secrets webhook and the Bitwarden SDK server rely on TLS certificates issued inside the cluster.
At a high level, the setup looks like this:
ClusterSecretStore exposes Bitwarden Secrets Manager cluster-wideAll components in this setup are scoped to a dedicated namespace.
kubectl create namespace external-secrets
First, define the required issuers. These issuers are used to bootstrap CAs and to sign certificates for the Bitwarden SDK server and the ESO webhook.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: bitwarden-bootstrap-issuer
namespace: external-secrets
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: bitwarden-ca-issuer
namespace: external-secrets
spec:
ca:
secretName: bitwarden-root-ca
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: eso-webhook-bootstrap-issuer
namespace: external-secrets
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: eso-webhook-ca-issuer
namespace: external-secrets
spec:
ca:
secretName: eso-webhook-root-ca
Next, define the certificates required for the Bitwarden SDK server and the External Secrets webhook.
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bitwarden-bootstrap-ca
namespace: external-secrets
spec:
isCA: true
commonName: bitwarden-sdk-root-ca
secretName: bitwarden-root-ca
subject:
organizations:
- external-secrets.io
privateKey:
algorithm: RSA
encoding: PKCS8
size: 2048
issuerRef:
name: bitwarden-bootstrap-issuer
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bitwarden-sdk-server-tls
namespace: external-secrets
spec:
secretName: bitwarden-tls-certs
dnsNames:
- bitwarden-sdk-server.external-secrets.svc.cluster.local
- bitwarden-sdk-server.external-secrets.svc
- bitwarden-sdk-server
- localhost
ipAddresses:
- 127.0.0.1
- ::1
privateKey:
algorithm: RSA
encoding: PKCS8
size: 2048
issuerRef:
name: bitwarden-ca-issuer
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: eso-webhook-bootstrap-ca
namespace: external-secrets
spec:
isCA: true
commonName: eso-webhook-root-ca
secretName: eso-webhook-root-ca
subject:
organizations:
- external-secrets.io
privateKey:
algorithm: RSA
encoding: PKCS8
size: 2048
issuerRef:
name: eso-webhook-bootstrap-issuer
kind: Issuer
group: cert-manager.io
Minimal Helm values enabling the Bitwarden SDK server and cert-manager integration:
bitwarden-sdk-server:
enabled: true
webhook:
certManager:
enabled: true
addInjectorAnnotations: true
cert:
create: true
issuerRef:
group: cert-manager.io
kind: Issuer
name: eso-webhook-ca-issuer
Create a single Bitwarden access token Secret in the external-secrets namespace.
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: bitwarden-access-token
namespace: external-secrets
data:
token: REDACTED
This token is reused centrally and not duplicated per namespace.
Expose Bitwarden Secrets Manager cluster-wide using a ClusterSecretStore:
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: bitwarden-secretsmanager
spec:
provider:
bitwardensecretsmanager:
apiURL: https://api.bitwarden.com
identityURL: https://identity.bitwarden.com
organizationID: REDACTED
projectID: REDACTED
auth:
secretRef:
credentials:
key: token
name: bitwarden-access-token
namespace: external-secrets
bitwardenServerSDKURL: https://bitwarden-sdk-server.external-secrets.svc.cluster.local:9998
caProvider:
type: Secret
name: bitwarden-root-ca
namespace: external-secrets
key: ca.crt
Finally, consume a secret from Bitwarden in any namespace using an ExternalSecret:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: bitwarden
namespace: default
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: bitwarden-secretsmanager
target:
creationPolicy: Owner
deletionPolicy: Retain
data:
- secretKey: test
remoteRef:
key: REDACTED
ClusterSecretStore avoids namespace-level token duplicationThis approach favors:
It intentionally avoids introducing Vault or additional control-plane complexity while still providing strong secret isolation and refresh semantics.