Bitwarden CRD Operator
Bitwarden CRD Operator is a kubernetes Operator based on kopf. The goal is to create kubernetes native secret objects from bitwarden.

DISCLAIMER: This project is still very work in progress :)
Getting started
You will need a ClientID and ClientSecret (where to get these) as well as your password. Expose these to the operator as described in this example:
env:
- name: BW_HOST
value: "https://bitwarden.your.tld.org"
- name: BW_CLIENTID
value: "user.your-client-id"
- name: BW_CLIENTSECRET
value: "YoUrCliEntSecRet"
- name: BW_PASSWORD
value: "YourSuperSecurePassword"You can also create a secret manually with these information and reference the existing secret like this in the values.yaml:
externalConfigSecret:
enabled: true
name: "my-existing-secret"BW_HOST can be omitted if you are using the Bitwarden SaaS offering. After that it is a basic helm deployment:
helm repo add bitwarden-operator https://lerentis.github.io/bitwarden-crd-operator
helm repo update
kubectl create namespace bw-operator
helm upgrade --install --namespace bw-operator -f values.yaml bw-operator bitwarden-operator/bitwarden-crd-operatorWording
The Bitwarden API distinguishes between simple login entries (username/password), custom fields, and attachments; the operator exposes these via the secretScope setting to control how items are mapped into Kubernetes Secrets.
- login — Use when you need the entry's username or password; specify
secretScope: login. - fields — Use for custom key/value fields stored on an item; specify
secretScope: fields. - attachment — Use for files attached to a Bitwarden item; specify
secretScope: attachment.
Examples:
Login entry:
content:
- element:
secretName: username
secretRef: my-username-key
secretScope: login
Custom field:
content:
- element:
secretName: api_key
secretRef: my-api-key
secretScope: fields
Attachment (file):
content:
- element:
secretName: some-file.txt
secretRef: file-key
secretScope: attachment
BitwardenSecret
Create a CRD object like this:
---
apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenSecret
metadata:
name: name-of-your-management-object
spec:
content:
- element:
secretName: nameOfTheFieldInBitwarden
secretRef: nameOfTheKeyInTheSecretToBeCreated
secretScope: login
- element:
secretName: nameOfAnotherFieldInBitwarden
secretRef: nameOfAnotherKeyInTheSecretToBeCreated
secretScope: login
id: "A Secret ID from bitwarden"
name: "Name of the secret to be created"
secretType:
namespace: "Namespace of the secret to be created"
labels:
key: value
annotations:
key: valueThe ID can be extracted from the browser URL. The resulting secret looks like:
apiVersion: v1
data:
nameOfTheKeyInTheSecretToBeCreated: "base64 encoded value of TheFieldInBitwarden"
nameOfAnotherKeyInTheSecretToBeCreated: "base64 encoded value of AnotherFieldInBitwarden"
kind: Secret
metadata:
annotations:
managed: bitwarden-secrets.lerentis.uploadfilter24.eu
managedObject: bw-operator/test
labels:
key: value
name: name-of-your-management-object
namespace: default
type: OpaqueRegistryCredential
For managing registry credentials, create:
---
apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: RegistryCredential
metadata:
name: name-of-your-management-object
spec:
usernameRef: nameOfTheFieldInBitwarden
passwordRef: nameOfTheFieldInBitwarden
registry: "docker.io"
id: "A Secret ID from bitwarden"
name: "Name of the secret to be created"
namespace: "Namespace of the secret to be created"
labels:
key: value
annotations:
key: valueapiVersion: v1
data:
.dockerconfigjson: "base64 encoded json auth string for your registry"
kind: Secret
metadata:
annotations:
managed: bitwarden-secrets.lerentis.uploadfilter24.eu
managedObject: bw-operator/test
labels:
key: value
name: name-of-your-management-object
namespace: default
type: dockerconfigjsonBitwardenTemplate
A template-driven type that allows jinja2 templates with a helper bitwarden_lookup:
---
apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenTemplate
metadata:
name: name-of-your-management-object
spec:
name: "Name of the secret to be created"
secretType:
namespace: "Namespace of the secret to be created"
labels:
key: value
annotations:
key: value
content:
- element:
filename: config.yaml
template: |
---
api:
enabled: True
key: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }}
allowCrossOrigin: false
apps:
"some.app.identifier:some_version":
pubkey: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }}
enabled: true
Resulting secret example:
apiVersion: v1
data:
Key of the secret to be created: "base64 encoded and rendered template with secrets injected directly from bitwarden"
kind: Secret
metadata:
annotations:
managed: bitwarden-template.lerentis.uploadfilter24.eu
managedObject: namespace/name-of-your-management-object
labels:
key: value
name: Name of the secret to be created
namespace: Namespace of the secret to be created
type: OpaqueThe signature of bitwarden_lookup is (item_id, scope, field) and details about parameters are described in the original docs.
Configuration parameters
The operator uses the bitwarden CLI and sync behaviour can be configured with env vars such as BW_SYNC_INTERVAL, BW_FORCE_SYNC and BW_RELOGIN_INTERVAL (defaults to 3600 seconds).