This repository contains a simple REST wrapper for the Bitwarden Rust SDK.
The main purpose of this API is to accommodate the needs for External Secrets Operator to talk to Bitwarden Secrets Manager.
The API is slim and follows basic REST principles. The following endpoints are supported with sample requests:
/rest/api/1/secret
Method GET
.
{
"id": "f5847eef-2f89-43bc-885a-b18a01178e3e"
}
Response:
{
"creationDate": "2024-04-04",
"id": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"key": "test",
"note": "note",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"revisionDate": "2024-04-04",
"value": "value"
}
/rest/api/1/secrets-by-ids
Method GET
.
{
"ids": [
"f5847eef-2f89-43bc-885a-b18a01178e3e", "0cab75c4-ba26-4996-a8bf-517095857ce3"
]
}
Response:
{
"data": [
{
"creationDate": "2024-04-04",
"id": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"key": "test",
"note": "note",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"revisionDate": "2024-04-04",
"value": "value"
},
{
"creationDate": "2024-04-05",
"id": "0cab75c4-ba26-4996-a8bf-517095857ce3",
"key": "test2",
"note": "note2",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"revisionDate": "2024-04-05",
"value": "value2"
}
]
}
/rest/api/1/secrets
Method GET
.
{
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e"
}
Response:
{
"data":[
{
"id": "1ba2f0c9-d73d-48bf-84a5-290ce5012258",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"key": "this-is-the-name"
}
]
}
rest/api/1/secret
Method PUT
.
{
"id": "1ba2f0c9-d73d-48bf-84a5-290ce5012258",
"key": "name",
"note": "new-note",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"value": "new-value"
}
Response:
{
"creationDate": "2024-04-04",
"id": "1ba2f0c9-d73d-48bf-84a5-290ce5012258",
"key": "test",
"note": "note",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"revisionDate": "2024-04-04",
"value": "value"
}
rest/api/1/secret
Method POST
.
{
"key": "name",
"note": "note",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"value": "value"
}
Response:
{
"creationDate": "2024-04-04",
"id": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"key": "name",
"note": "note",
"organizationId": "f5847eef-2f89-43bc-885a-b18a01178e3e",
"revisionDate": "2024-04-04",
"value": "value"
}
The router is using a middleware called Warden
that will create an authenticated client for all the requests.
This client is created through the use of Headers. The following headers can be provided for each call:
Warden-Access-Token: <token> // mandatory
Warden-State-Path: <state-path>
Warden-Api-Url: <url>
Warden-Identity-Url: <url>
A sample call could look something like this:
curl --insecure -d '{"key": "test2", "value": "secret","note": "shit", "organizationId": "ac2b00ac-2ef7-4d86-8cbd-b18a011760cb", "projectIds":[
"f5847eef-2f89-43bc-885a-b18a01178e3e"]}' https://chart-bitwarden-sdk-server.default.svc.cluster.local:9998/rest/api/1/secret --header 'Warden-Acce
ss-Token:<token>' -X POST
The server is a dependency to external-secrets' helm chart, therefor it can be installed together with ESO like this:
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace \
--set bitwarden-sdk-server.enabled=true
Or, it can also be installed in a standalone way using helm from this repository.
The server MUST run using HTTPS. A recommended way to generate a certificate is to use cert-manager.
The certificate can be defined in a Kubernetes secret called bitwarden-tls-certs
. This can be overwritten in the helm
chart values file.
The certificate will then be required when using external-secrets' Bitwarden provider.
There are many ways to generate secrets for an HTTP server. One of which could be through cert-manager.
That process can be found under the hack
folder. But using an existing certificate is also possible through helm
values. These are mounted inside the container and used further by the client with keys defined by the following
command line arguments:
flag.StringVar(&rootArgs.server.KeyFile, "key-file", "/certs/key.pem", "--key-file /certs/key.pem")
flag.StringVar(&rootArgs.server.CertFile, "cert-file", "/certs/cert.pem", "--cert-file /certs/cert.pem")
The certificate mount target and values are defined under image
section in the values file as such:
image:
repository: ghcr.io/external-secrets/bitwarden-sdk-server
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
tls:
enabled: true
volumeMounts:
- mountPath: "/certs"
name: "bitwarden-tls-certs"
volumes:
- name: "bitwarden-tls-certs"
secret:
secretName: "bitwarden-tls-certs"
items:
- key: "tls.crt"
path: "cert.pem"
- key: "tls.key"
path: "key.pem"
- key: "ca.crt"
path: "ca.pem"
To use cert-manager the hack
folder sets up the following certificate issuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: bitwarden-bootstrap-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bitwarden-bootstrap-certificate
namespace: cert-manager
spec:
# this is discouraged but required by ios
commonName: cert-manager-bitwarden-tls
isCA: true
secretName: bitwarden-tls-certs
subject:
organizations:
- external-secrets.io
dnsNames:
- external-secrets-bitwarden-sdk-server.default.svc.cluster.local
- bitwarden-sdk-server.default.svc.cluster.local
- localhost
ipAddresses:
- 127.0.0.1
- ::1
privateKey:
algorithm: RSA
encoding: PKCS8
size: 2048
issuerRef:
name: bitwarden-bootstrap-issuer
kind: ClusterIssuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: bitwarden-certificate-issuer
spec:
ca:
secretName: bitwarden-tls-certs
The important bits are the dnsNames
. The first one is with the external-secrets helm release name, and the second one
is a plain install. But also, external-secrets pins the release name of bitwarden, so that should work too. This will
create a self-signed certificate for us to use internally. This certificate will later be provided to external-secrets
so it can talk to the service.
Next, we create a Certificate for bitwarden with the following request:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bitwarden-tls-certs
namespace: default
spec:
secretName: bitwarden-tls-certs
dnsNames:
- bitwarden-sdk-server.default.svc.cluster.local
- external-secrets-bitwarden-sdk-server.default.svc.cluster.local
- localhost
ipAddresses:
- 127.0.0.1
- ::1
privateKey:
algorithm: RSA
encoding: PKCS8
size: 2048
issuerRef:
name: bitwarden-certificate-issuer
kind: ClusterIssuer
group: cert-manager.io
This is provided to bitwarden to initialize an HTTPS server.
On external-secrets side, there are two options to provide the certificate.
One is through caBundle
which accepts the plain root certificate as a base64 encoded value.
Second is through caProvider
that uses either a secret or a configmap and looks for the right key.
WARNING: DO NOT provide the same secret as the server. For more detail read cert-manager Trust Post.
For testing purposes, or if you trust your network that much, an --insecure
flag has been provided that runs this
server as plain HTTP.
Run make prime-test-cluster
to launch a cluster and generate a certificate for the service. One done, simply run tilt
to create the service. Note OSX users must install https://github.com/FiloSottile/homebrew-musl-cross in order to
build the CGO library.
Usage on the external-secrets side is documented under Bitwarden Secrets Manager Provider.