Bitwarden support using webhook provider
Bitwarden is an integrated open source password management solution for individuals, teams, and business organizations.
How is it working ?
To make external-secret compatible with BitWarden, we need:
- External-Secret >= 0.8.0
- To use the Webhook Provider
- 2 (Cluster)SecretStores
- BitWarden CLI image running
bw serve
When you create a new external-secret object, External-Secret Webhook provider will do a query to the Bitwarden CLI pod, which is synced with the BitWarden server.
Requirements
- Bitwarden account (it works also with VaultWarden)
- A Kubernetes secret which contains your BitWarden Credentials
- You need a Docker image with BitWarden CLI installed.
You could use
registry.gitlab.com/ttblt-oss/docker-bw:2023.1.0
or build your own.
Here an example of Dockerfile use to build this image:
FROM debian:sid
ENV BW_CLI_VERSION=2023.1.0
RUN apt update && \
apt install -y wget unzip && \
wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_CLI_VERSION}/bw-linux-${BW_CLI_VERSION}.zip && \
unzip bw-linux-${BW_CLI_VERSION}.zip && \
chmod +x bw && \
mv bw /usr/local/bin/bw && \
rm -rfv *.zip
COPY entrypoint.sh /
CMD ["/entrypoint.sh"]
And the content of entrypoint.sh
#!/bin/bash
set -e
bw config server ${BW_HOST}
export BW_SESSION=$(bw login ${BW_USER} --passwordenv BW_PASSWORD --raw)
bw unlock --check
echo 'Running `bw server` on port 8087'
bw serve --hostname 0.0.0.0 #--disable-origin-protection
Deploy Bitwarden Credentials
apiVersion: v1
data:
BW_HOST: ...
BW_USERNAME: ...
BW_PASSWORD: ....
kind: Secret
metadata:
name: bitwarden-cli
namespace: bitwarden
type: Opaque
Deploy Bitwarden CLI container
apiVersion: apps/v1
kind: Deployment
metadata:
name: bitwarden-cli
namespace: bitwarden
labels:
app.kubernetes.io/instance: bitwarden-cli
app.kubernetes.io/name: bitwarden-cli
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: bitwarden-cli
app.kubernetes.io/instance: bitwarden-cli
template:
metadata:
labels:
app.kubernetes.io/name: bitwarden-cli
app.kubernetes.io/instance: bitwarden-cli
spec:
containers:
- name: bitwarden-cli
image: YOUR_BITWARDEN_CLI_IMAGE
imagePullPolicy: IfNotPresent
env:
- name: BW_HOST
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_HOST
- name: BW_USER
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_USERNAME
- name: BW_PASSWORD
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_PASSWORD
ports:
- name: http
containerPort: 8087
protocol: TCP
livenessProbe:
exec:
command:
- wget
- -q
- http://127.0.0.1:8087/sync?force=true
- --post-data=''
initialDelaySeconds: 20
failureThreshold: 3
timeoutSeconds: 1
periodSeconds: 120
readinessProbe:
tcpSocket:
port: 8087
initialDelaySeconds: 20
failureThreshold: 3
timeoutSeconds: 1
periodSeconds: 10
startupProbe:
tcpSocket:
port: 8087
initialDelaySeconds: 10
failureThreshold: 30
timeoutSeconds: 1
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: bitwarden-cli
namespace: bitwarden
labels:
app.kubernetes.io/instance: bitwarden-cli
app.kubernetes.io/name: bitwarden-cli
annotations:
spec:
type: ClusterIP
ports:
- port: 8087
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: bitwarden-cli
app.kubernetes.io/instance: bitwarden-cli
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
namespace: bitwarden
name: external-secret-2-bw-cli
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: bitwarden-cli
app.kubernetes.io/name: bitwarden-cli
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/instance: external-secrets
app.kubernetes.io/name: external-secrets
NOTE: Deploying a network policy is recommended since, there is no authentication to query the BitWarden CLI, which means that your secrets are exposed.
NOTE: In this example the Liveness probe is quering /sync to ensure that the BitWarden CLI is able to connect to the server and also to sync secrets. (The secret sync is only every 2 minutes in this example)
Deploy ClusterSecretStore (Or SecretStore)
Here the two ClusterSecretStore to deploy
---
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: bitwarden-login
spec:
provider:
webhook:
url: "http://bitwarden-cli:8087/object/item/{{ .remoteRef.key }}"
headers:
Content-Type: application/json
result:
jsonPath: "$.data.login.{{ .remoteRef.property }}"
---
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: bitwarden-fields
spec:
provider:
webhook:
url: "http://bitwarden-cli:8087/object/item/{{ .remoteRef.key }}"
result:
jsonPath: "$.data.fields[?@.name==\"{{ .remoteRef.property }}\"].value"
---
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: bitwarden-notes
spec:
provider:
webhook:
url: "http://bitwarden-cli:8087/object/item/{{ .remoteRef.key }}"
result:
jsonPath: "$.data.notes"
How to use it ?
- If you need the
username
or thepassword
of a secret, you have to usebitwarden-login
- If you need a custom field of a secret, you have to use
bitwarden-fields
- If you need to use a Bitwarden Note for multiline strings (SSH keys, service account json files), you have to use
bitwarden-notes
- The
key
is the ID of a secret, which can be find in the URL with theitemId
value:https://myvault.com/#/vault?itemId=........-....-....-....-............
- The
property
is the name of the field: username
for the username of a secret (bitwarden-login
SecretStore)password
for the password of a secret (bitwarden-login
SecretStore)name_of_the_custom_field
for any custom field (bitwarden-fields
SecretStore)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: my-db-secrets
namespace: default
spec:
target:
name: my-db-secrets
deletionPolicy: Delete
template:
type: Opaque
data:
username: |-
{{ .username }}
password: |-
{{ .password }}
postgres-password: |-
{{ .postgres_password }}
postgres-replication-password: |-
{{ .postgres_replication_password }}
db_url: |-
postgresql://{{ .username }}:{{ .password }}@my-postgresql:5432/mydb
service_account_key: |-
{{ .service_account_key }}
data:
- secretKey: username
sourceRef:
storeRef:
name: bitwarden-login
kind: ClusterSecretStore # or SecretStore
remoteRef:
key: aaaabbbb-cccc-dddd-eeee-000011112222
property: username
- secretKey: password
sourceRef:
storeRef:
name: bitwarden-login
kind: ClusterSecretStore # or SecretStore
remoteRef:
key: aaaabbbb-cccc-dddd-eeee-000011112222
property: password
- secretKey: postgres_password
sourceRef:
storeRef:
name: bitwarden-fields
kind: ClusterSecretStore # or SecretStore
remoteRef:
key: aaaabbbb-cccc-dddd-eeee-000011112222
property: admin-password
- secretKey: postgres_replication_password
sourceRef:
storeRef:
name: bitwarden-fields
kind: ClusterSecretStore # or SecretStore
remoteRef:
key: aaaabbbb-cccc-dddd-eeee-000011112222
property: postgres-replication-password
- secretKey: service_account_key
sourceRef:
storeRef:
name: bitwarden-notes
kind: ClusterSecretStore # or SecretStore
remoteRef:
key: service_account_key