Bitwarden

Published: May 21, 2024 by Isaac Johnson

Last week we looked at Vaultwarden, the Rust based clone of Bitwarden.

Today, let’s look at Bitwarden itself. While Bitwarden has a commercial offering, their source code and backend are completely Open-source. We’ll try to setup the Kubernetes version (spoiler: some success, but ingress issues persist), then look at the SaaS offering and some of it’s features.

We’ll take a look at export/import between Wardens (Vault and Bit) and explore browser extensions. We’ll check out the Android app and touch on costs (actually quite reasonable).

Let’s dig in!

Installation

There are a lot of install options for Bitwarden but we’ll start with their Helm charts for a native Kubernetes deployment.

We want to get the values.yaml to edit. To do this, let’s add their chart repo and show values to a file

$ helm repo add bitwarden https://charts.bitwarden.com/
"bitwarden" has been added to your repositories


$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitwarden" chart repository
...Unable to get an update from the "freshbrewed" chart repository (https://harbor.freshbrewed.science/chartrepo/library):
        failed to fetch https://harbor.freshbrewed.science/chartrepo/library/index.yaml : 404 Not Found
...Unable to get an update from the "myharbor" chart repository (https://harbor.freshbrewed.science/chartrepo/library):
        failed to fetch https://harbor.freshbrewed.science/chartrepo/library/index.yaml : 404 Not Found
...Successfully got an update from the "azure-samples" chart repository
...Successfully got an update from the "portainer" chart repository
...Successfully got an update from the "ngrok" chart repository
...Successfully got an update from the "nfs" chart repository
...Successfully got an update from the "opencost" chart repository
...Successfully got an update from the "adwerx" chart repository
...Successfully got an update from the "opencost-charts" chart repository
...Successfully got an update from the "actions-runner-controller" chart repository
...Successfully got an update from the "openfunction" chart repository
...Successfully got an update from the "kuma" chart repository
...Successfully got an update from the "rhcharts" chart repository
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "novum-rgi-helm" chart repository
...Successfully got an update from the "zabbix-community" chart repository
...Successfully got an update from the "makeplane" chart repository
...Successfully got an update from the "sonarqube" chart repository
...Successfully got an update from the "lifen-charts" chart repository
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "gitea-charts" chart repository
...Successfully got an update from the "elastic" chart repository
...Successfully got an update from the "harbor" chart repository
...Successfully got an update from the "sumologic" chart repository
...Successfully got an update from the "akomljen-charts" chart repository
...Successfully got an update from the "rook-release" chart repository
...Successfully got an update from the "datadog" chart repository
...Successfully got an update from the "incubator" chart repository
...Successfully got an update from the "argo-cd" chart repository
...Successfully got an update from the "rancher-latest" chart repository
...Successfully got an update from the "crossplane-stable" chart repository
...Successfully got an update from the "uptime-kuma" chart repository
...Successfully got an update from the "newrelic" chart repository
...Successfully got an update from the "gitlab" chart repository
...Successfully got an update from the "prometheus-community" chart repository
...Successfully got an update from the "jfelten" chart repository
...Successfully got an update from the "btungut" chart repository
...Successfully got an update from the "confluentinc" chart repository
...Successfully got an update from the "kube-state-metrics" chart repository
...Successfully got an update from the "dapr" chart repository
...Successfully got an update from the "ingress-nginx" chart repository
...Successfully got an update from the "longhorn" chart repository
...Successfully got an update from the "kiwigrid" chart repository
...Successfully got an update from the "jetstack" chart repository
...Successfully got an update from the "spacelift" chart repository
...Successfully got an update from the "castai-helm" chart repository
...Successfully got an update from the "openproject" chart repository
...Successfully got an update from the "kubecost" chart repository
...Successfully got an update from the "signoz" chart repository
...Successfully got an update from the "openzipkin" chart repository
...Successfully got an update from the "open-telemetry" chart repository
...Successfully got an update from the "ananace-charts" chart repository
...Successfully got an update from the "grafana" chart repository
...Unable to get an update from the "epsagon" chart repository (https://helm.epsagon.com):
        Get "https://helm.epsagon.com/index.yaml": dial tcp: lookup helm.epsagon.com on 172.22.64.1:53: server misbehaving
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈

I’ll create a namespace in anticipation

$ kubectl create ns bitwarden
namespace/bitwarden created

Then fetch the values to a file

$ helm show values bitwarden/self-host --devel > my-bw-values.yaml

I’ll edit some of the values - such as Ingress and Storageclass name

# Optional - Override the chart name if desired
fullnameOverride: ""
nameOverride: ""

general:
  # Domain name for the service
  domain: "bitwarden.tpk.pw"
  ingress:
    # Set to false if using a custom ingress
    enabled: true
    # Current supported values for ingress type include: nginx
    className: "nginx"
     ## - Annotations to add to the Ingress resource.
    annotations:
      nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
      # nginx.ingress.kubernetes.io/use-regex: "true"
      # nginx.ingress.kubernetes.io/rewrite-target: /$1
    ## - Labels to add to the Ingress resource
    labels: {}
    # Certificate options
    tls:
      # TLS certificate secret name
      name: bitwarden-tls
      # Cluster cert issuer (ex. Let's Encrypt) name if one exists
      clusterIssuer: azuredns-tpkpw
    # Ingress path configuration - The samples provided are for Nginx. Examples with other Ingress providers are in the chart Readme on GitHub

I can create the A Record

$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 75.73.224.240 -n bitwarden
{
  "ARecords": [
    {
      "ipv4Address": "75.73.224.240"
    }
  ],
  "TTL": 3600,
  "etag": "fa9b2914-8414-4c0c-8627-215e1b2d4fb9",
  "fqdn": "bitwarden.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/bitwarden",
  "name": "bitwarden",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "type": "Microsoft.Network/dnszones/A"
}

I needed to get the Host ID and Key from https://bitwarden.com/host/.

It doesn’t cost anything and used by sales and security to send updates. Small price to pay, I suppose

/content/images/2024/05/bitwarden-01.png

Before I can move forward I need to create a secret

$ kubectl create secret generic custom-secret -n bitwarden \
-from-literal=globalSet>     --from-literal=globalSettings__installation__id="b************************************2" \
>     --from-literal=globalSettings__installation__key="7****************************h" \
>     --from-literal=globalSettings__mail__smtp__username="apikey" \
>     --from-literal=globalSettings__mail__smtp__password="SG.U**********************************************************************4" \
   --from-litera>     --from-literal=globalSettings__yubico__clientId="REPLACE" \
  --from>     --from-literal=globalSettings__yubico__key="REPLACE" \
>     --from-literal=globalSettings__hibpApiKey="REPLACE" \
   --fro>     --from-literal=SA_PASSWORD="REPLACE"
secret/custom-secret created

Now I can install

$ helm upgrade bitwarden bitwarden/self-host --install --namespace bitwarden --values my-bw-values.yaml
Release "bitwarden" does not exist. Installing it now.
Error: failed post-install: 1 error occurred:
        * timed out waiting for the condition

I think it was due to the PVCs

$ kubectl get pvc -n bitwarden
NAME                                 STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
bitwarden-self-host-mssqlbackups     Pending                                      local-path     96m
bitwarden-self-host-mssqldata        Pending                                      local-path     96m
bitwarden-self-host-dataprotection   Pending                                      local-path     96m
bitwarden-self-host-attachments      Pending                                      local-path     96m
bitwarden-self-host-mssqllog         Pending                                      local-path     96m
bitwarden-self-host-licenses         Pending                                      local-path     96m

I tried a second time round with ‘managed-nfs-storage’

$ helm upgrade bitwarden bitwarden/self-host --install --namespace bitwarden --values my-bw-values.yaml
Release "bitwarden" does not exist. Installing it now.
Error: failed post-install: 1 error occurred:
        * timed out waiting for the condition
        
        
$ kubectl get pvc -n bitwarden
NAME                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
bitwarden-self-host-mssqllog         Bound    pvc-055d61c6-c020-4f9e-ba18-2197077acc92   10Gi       RWX            managed-nfs-storage   3m32s
bitwarden-self-host-dataprotection   Bound    pvc-7448e6da-c878-45d3-adcd-97884f3cb268   1Gi        RWX            managed-nfs-storage   3m32s
bitwarden-self-host-licenses         Bound    pvc-c42740d2-e9e5-4455-aefa-9cc1c823169d   1Gi        RWX            managed-nfs-storage   3m32s
bitwarden-self-host-mssqlbackups     Bound    pvc-7e1689f8-0693-4928-a1cf-fe8b8b094fa7   1Gi        RWX            managed-nfs-storage   3m32s
bitwarden-self-host-attachments      Bound    pvc-7fdf610e-9a5e-41b7-a524-9f704f56e5af   1Gi        RWX            managed-nfs-storage   3m32s
bitwarden-self-host-mssqldata        Bound    pvc-03894d3d-626d-4b1e-90e4-a2b8b3976a3f   10Gi       RWX            managed-nfs-storage   3m32s

Seems it is the MSQL this time causing troubles

$ kubectl get pods -n bitwarden
NAME                                                 READY   STATUS             RESTARTS        AGE
bitwarden-self-host-notifications-54466bbfff-jtmtq   1/1     Running            0               7m26s
bitwarden-self-host-icons-5d568bd579-gkn88           1/1     Running            0               7m26s
bitwarden-self-host-identity-7dcd9f5dc5-mdjjm        1/1     Running            0               7m26s
bitwarden-self-host-events-575499c877-tvsmm          1/1     Running            0               7m26s
bitwarden-self-host-web-76d589c6b4-29sw7             1/1     Running            0               7m26s
bitwarden-self-host-attachments-55c87f9748-dhxkc     1/1     Running            0               7m26s
bitwarden-self-host-api-685dbdcbb5-snpzx             1/1     Running            0               7m26s
bitwarden-self-host-sso-5756c5d654-hdjjl             1/1     Running            1 (3m37s ago)   7m26s
bitwarden-self-host-admin-5d7f7dfb8c-2qs9z           1/1     Running            1 (3m5s ago)    7m26s
bitwarden-self-host-mssql-0                          0/1     CrashLoopBackOff   5 (96s ago)     7m26s

Let’s take a look

builder@DESKTOP-QADGF36:~/Workspaces/vaultwarden$ kubectl get pods -n bitwarden
NAME                                                 READY   STATUS    RESTARTS        AGE
bitwarden-self-host-notifications-54466bbfff-jtmtq   1/1     Running   0               8m48s
bitwarden-self-host-icons-5d568bd579-gkn88           1/1     Running   0               8m48s
bitwarden-self-host-identity-7dcd9f5dc5-mdjjm        1/1     Running   0               8m48s
bitwarden-self-host-events-575499c877-tvsmm          1/1     Running   0               8m48s
bitwarden-self-host-web-76d589c6b4-29sw7             1/1     Running   0               8m48s
bitwarden-self-host-attachments-55c87f9748-dhxkc     1/1     Running   0               8m48s
bitwarden-self-host-api-685dbdcbb5-snpzx             1/1     Running   0               8m48s
bitwarden-self-host-sso-5756c5d654-hdjjl             1/1     Running   2 (79s ago)     8m48s
bitwarden-self-host-admin-5d7f7dfb8c-2qs9z           1/1     Running   2 (37s ago)     8m48s
bitwarden-self-host-mssql-0                          0/1     Running   6 (2m58s ago)   8m48s
builder@DESKTOP-QADGF36:~/Workspaces/vaultwarden$ kubectl describe pod bitwarden-self-host-mssql-0 -n bitwarden | tail -n 13
Events:
  Type     Reason            Age                     From               Message
  ----     ------            ----                    ----               -------
  Warning  FailedScheduling  8m49s                   default-scheduler  0/3 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling..
  Normal   Scheduled         8m48s                   default-scheduler  Successfully assigned bitwarden/bitwarden-self-host-mssql-0 to hp-hp-elitebook-850-g2
  Normal   Pulled            7m59s                   kubelet            Successfully pulled image "mcr.microsoft.com/mssql/server:2022-CU11-ubuntu-22.04" in 40.138857515s (40.138882568s including waiting)
  Normal   Pulled            7m42s                   kubelet            Successfully pulled image "mcr.microsoft.com/mssql/server:2022-CU11-ubuntu-22.04" in 320.383449ms (320.390237ms including waiting)
  Normal   Pulled            6m55s                   kubelet            Successfully pulled image "mcr.microsoft.com/mssql/server:2022-CU11-ubuntu-22.04" in 288.149992ms (288.169048ms including waiting)
  Normal   Pulling           6m1s (x4 over 8m39s)    kubelet            Pulling image "mcr.microsoft.com/mssql/server:2022-CU11-ubuntu-22.04"
  Normal   Pulled            6m1s                    kubelet            Successfully pulled image "mcr.microsoft.com/mssql/server:2022-CU11-ubuntu-22.04" in 284.072665ms (284.08735ms including waiting)
  Normal   Created           6m1s (x4 over 7m58s)    kubelet            Created container bitwarden-self-host-mssql
  Normal   Started           6m1s (x4 over 7m58s)    kubelet            Started container bitwarden-self-host-mssql
  Warning  BackOff           3m39s (x20 over 7m19s)  kubelet            Back-off restarting failed container bitwarden-self-host-mssql in pod bitwarden-self-host-mssql-0_bitwarden(2989c2ed-f395-4dac-a49f-692f1f24dbc0)

Looks like its the DB password at fault


2024-05-12 19:47:34.81 Server      External governance manager initialized
2024-05-12 19:47:34.89 spid49s     ERROR: Unable to set system administrator password: Password validation failed. The password does not meet SQL Server password policy requirements because it is too short. The password must be at least 8 characters..
2024-05-12 19:47:34.89 spid49s     An error occurred during server setup. See previous errors for more information.
2024-05-12 19:47:34.90 spid49s     SQL Trace was stopped due to server shutdown. Trace ID = '1'. This is an informational message only; no user action is required.

Which I didn’t set (left the default)

    --from-literal=SA_PASSWORD="REPLACE"

Installing after fixing the SA password:

$ helm upgrade bitwarden bitwarden/self-host --install --namespace bitwarden --values my-bw-values.yaml
Release "bitwarden" does not exist. Installing it now.
NAME: bitwarden
LAST DEPLOYED: Sun May 12 15:26:50 2024
NAMESPACE: bitwarden
STATUS: deployed
REVISION: 1
TEST SUITE: None

Which showed the pods are now running

$ kubectl get pods -n bitwarden
NAME                                                 READY   STATUS    RESTARTS   AGE
bitwarden-self-host-identity-7dcd9f5dc5-bt8qt        1/1     Running   0          3m43s
bitwarden-self-host-web-76d589c6b4-dgxkc             1/1     Running   0          3m43s
bitwarden-self-host-attachments-55c87f9748-rq26t     1/1     Running   0          3m43s
bitwarden-self-host-icons-5d568bd579-lw4mm           1/1     Running   0          3m42s
bitwarden-self-host-notifications-54466bbfff-fjddn   1/1     Running   0          3m43s
bitwarden-self-host-sso-5756c5d654-btsxz             1/1     Running   0          3m43s
bitwarden-self-host-api-685dbdcbb5-qj56p             1/1     Running   0          3m43s
bitwarden-self-host-events-575499c877-8lxq5          1/1     Running   0          3m43s
bitwarden-self-host-admin-5d7f7dfb8c-x87pk           1/1     Running   0          3m43s
bitwarden-self-host-mssql-0                          1/1     Running   0          3m43s

$ kubectl get ingress -n bitwarden
NAME                          CLASS   HOSTS              ADDRESS   PORTS     AGE
bitwarden-self-host-ingress   nginx   bitwarden.tpk.pw             80, 443   4m16s

I’m starting to get stumped.

The deployment is fine, the pods are fine, yet the service won’t route

builder@DESKTOP-QADGF36:~/Workspaces/vaultwarden$ helm upgrade bitwarden bitwarden/self-host --install --namespace bitwarden --values my-bw-values.yaml
Release "bitwarden" does not exist. Installing it now.
NAME: bitwarden
LAST DEPLOYED: Sun May 12 15:44:36 2024
NAMESPACE: bitwarden
STATUS: deployed
REVISION: 1
TEST SUITE: None
builder@DESKTOP-QADGF36:~/Workspaces/vaultwarden$ kubectl get pods -n bitwarden
NAME                                                 READY   STATUS    RESTARTS   AGE
bitwarden-self-host-web-76d589c6b4-4nbp5             1/1     Running   0          13h
bitwarden-self-host-events-575499c877-nzj6v          1/1     Running   0          13h
bitwarden-self-host-icons-5d568bd579-7tzwj           1/1     Running   0          13h
bitwarden-self-host-notifications-54466bbfff-5pbpj   1/1     Running   0          13h
bitwarden-self-host-attachments-55c87f9748-m6m45     1/1     Running   0          13h
bitwarden-self-host-admin-5d7f7dfb8c-dp8j4           1/1     Running   0          13h
bitwarden-self-host-identity-7dcd9f5dc5-xmhbj        1/1     Running   0          13h
bitwarden-self-host-sso-5756c5d654-97qpx             1/1     Running   0          13h
bitwarden-self-host-api-685dbdcbb5-5l9ss             1/1     Running   0          13h
bitwarden-self-host-mssql-0                          1/1     Running   0          13h

/content/images/2024/05/bitwarden-02.png

I can port-forward to the service without issue:

$ kubectl port-forward svc/bitwarden-self-host-web -n bitwarden 5000:5000
Forwarding from 127.0.0.1:5000 -> 5000
Forwarding from [::1]:5000 -> 5000
Handling connection for 5000
Handling connection for 5000
Handling connection for 5000
Handling connection for 5000

/content/images/2024/05/bitwarden-03.png

I’m going to try and build out a new Ingress myself to see if I can debug this.

To do that, I’ll get another URL, this time using AWS Route53

$ aws route53 change-resource-record-sets --hosted-zone-id Z39E8QFU0F9PZP --change-batch file://r53-bitwarden.json
{
    "ChangeInfo": {
        "Id": "/change/C08820402CFVLOYKVGC1N",
        "Status": "PENDING",
        "SubmittedAt": "2024-05-13T10:44:38.605Z",
        "Comment": "CREATE bitwarden fb.s A record "
    }
}

I replicated the blocks into the framework for a working Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: bitwarden
spec:
  rules:
  - host: bitwarden.freshbrewed.science
    http:
      paths:
      - backend:
          service:
            name: bitwarden-self-host-web
            port:
              number: 5000
        path: /(.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-attachments
            port:
              number: 5000
        path: /attachments/(.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-api
            port:
              number: 5000
        path: /api/(.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-icons
            port:
              number: 5000
        path: /icons/(.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-notifications
            port:
              number: 5000
        path: /notifications/(.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-events
            port:
              number: 5000
        path: /events/(.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-sso
            port:
              number: 5000
        path: /(sso/.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-identity
            port:
              number: 5000
        path: /(identity/.*)
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-admin
            port:
              number: 5000
        path: /(admin/?.*)
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - bitwarden.freshbrewed.science
    secretName: bitwarden-fbs-tls

Then applied

$ kubectl apply -f ./bitwarden.ingress.fbs.yaml -n bitwarden
ingress.networking.k8s.io/bitwarden created

Once the cert is satisified

$ kubectl get cert -n bitwarden
NAME                READY   SECRET              AGE
bitwarden-tls       True    bitwarden-tls       14h
bitwarden-fbs-tls   True    bitwarden-fbs-tls   2m8s

I can give it a try.

/content/images/2024/05/bitwarden-04.png

still no go.

I think there is an app misconfiguration. I can see the app loads /app/index.html

But my Nginx logs display

2024/05/13 10:55:09 [error] 2722#2722: *11321608 open() "/etc/nginx/html/favicon.ico" failed (2: No such file or directory), client: 180.149.25.35, server: bitwarden.freshbrewed.science, request: "GET /favicon.ico HTTP/1.1", host: "bitwarden.freshbrewed.science", referrer: "https://bitwarden.freshbrewed.science/"
180.149.25.35 - - [13/May/2024:10:55:09 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "https://bitwarden.freshbrewed.science/" "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.47 Mobile/15E148 Safari/604.1" "-"

If I reduce my ingress to just

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: bitwarden
spec:
  rules:
  - host: bitwarden.freshbrewed.science
    http:
      paths:
      - backend:
          service:
            name: bitwarden-self-host-web
            port:
              number: 5000
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - bitwarden.freshbrewed.science
    secretName: bitwarden-fbs-tls

(which cuts out a lot of endpoints, but does expose the front door)

That works

/content/images/2024/05/bitwarden-05.png

I pivoted from the regexp and ImplementationSpecific routes to Prefix types.

Seems to start up

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: bitwarden
spec:
  rules:
  - host: bitwarden.freshbrewed.science
    http:
      paths:
      - backend:
          service:
            name: bitwarden-self-host-web
            port:
              number: 5000
        path: /
        pathType: Prefix
      - backend:
          service:
            name: bitwarden-self-host-attachments
            port:
              number: 5000
        path: /attachments/
        pathType: Prefix
      - backend:
          service:
            name: bitwarden-self-host-api
            port:
              number: 5000
        path: /api/
        pathType: ImplementationSpecific
      - backend:
          service:
            name: bitwarden-self-host-icons
            port:
              number: 5000
        path: /icons/
        pathType: Prefix
      - backend:
          service:
            name: bitwarden-self-host-notifications
            port:
              number: 5000
        path: /notifications/
        pathType: Prefix
      - backend:
          service:
            name: bitwarden-self-host-events
            port:
              number: 5000
        path: /events/
        pathType: Prefix
      - backend:
          service:
            name: bitwarden-self-host-sso
            port:
              number: 5000
        path: /sso/
        pathType: Prefix
      - backend:
          service:
            name: bitwarden-self-host-identity
            port:
              number: 5000
        path: /identity/
        pathType: Prefix
      - backend:
          service:
            name: bitwarden-self-host-admin
            port:
              number: 5000
        path: /admin/
        pathType: Prefix
  tls:
  - hosts:
    - bitwarden.freshbrewed.science
    secretName: bitwarden-fbs-tls

/content/images/2024/05/bitwarden-06.png

I saw an error on signup

/content/images/2024/05/bitwarden-07.png

But it did create the account

/content/images/2024/05/bitwarden-08.png

However, ‘Send email’ seems to have an issue

/content/images/2024/05/bitwarden-09.png

I realized I used the wrong SMTP url

    smtpHost: "smtp.sendgrid.com"

should be

    smtpHost: "smtp.sendgrid.net"

I tried a few tricks, including coming up with a set of commands to rotate all but the database pod

kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=api
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=events
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=notifications
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=web
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=admin
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=icons
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=identity
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=sso
kubectl delete pods -n bitwarden -l app.kubernetes.io/instance=bitwarden,app.kubernetes.io/component=attachments

But it didn’t seem to take.

I decided to uninstall and reinstall

builder@DESKTOP-QADGF36:~/Workspaces/vaultwarden$ helm delete bitwarden -n bitwarden
release "bitwarden" uninstalled

builder@DESKTOP-QADGF36:~/Workspaces/vaultwarden$ kubectl get pvcs -n bitwarden
error: the server doesn't have a resource type "pvcs"
builder@DESKTOP-QADGF36:~/Workspaces/vaultwarden$ helm upgrade bitwarden bitwarden/self-host --install --namespace bitwarden --values my-bw-values.yaml
Release "bitwarden" does not exist. Installing it now.
NAME: bitwarden
LAST DEPLOYED: Mon May 13 06:29:58 2024
NAMESPACE: bitwarden
STATUS: deployed
REVISION: 1
TEST SUITE: None

The re-install seemed to work

/content/images/2024/05/bitwarden-10.png

I can also see a positive confirmation on my Twilio/Sendgrid test page

/content/images/2024/05/bitwarden-11.png

I used, for reference

  admins: "isaac@freshbrewed.science"
  email:
    # Email address used for invitations, typically no-reply@smtp-host
    replyToEmail: "isaac@freshbrewed.science"
    # Your SMTP server hostname (recommended) or IP address
    smtpHost: "smtp.sendgrid.net"
    # The SMTP port used by the SMTP server
    smtpPort: "587"
    # Whether your SMTP server uses an encryption protocol, "true" for SSL, "false" for TLS
    smtpSsl: "false"
  # Custom labels to add throughout the installation

Where the email matches my Sendgrid validated user

I also got a validation email to use

/content/images/2024/05/bitwarden-12.png

Even though I verified it, I still see the decoration on the right. Perhaps there is just a delay?

/content/images/2024/05/bitwarden-13.png

I tried to create a folder, didn’t work. I tried to create an item - also didn’t work

When we add that to the “Premium” feature of copying a totp token to the clipboard, I’m starting to reconsider keeping this app

/content/images/2024/05/bitwarden-15.png

SaaS

Okay, I’m not going to waste more time self-hosting.

Let’s try the SaaS offering by signing up at https://vault.bitwarden.com/#/register?layout=default

/content/images/2024/05/bitwarden-16.png

The sign-up is similar to the one I saw in self-hosted, albeit there is a captcha check

Once created, I am greeted with a similar dashboard. However here, it would appear, I can also import data and add a browser extension.

/content/images/2024/05/bitwarden-17.png

I verified my email and it seems that took

/content/images/2024/05/bitwarden-18.png

I could create a secret just fine

/content/images/2024/05/bitwarden-19.png

Import from VaultWarden

I wanted to see if I could transfer the data out of VaultWarden to a SaaS Bitwarden.

To me, that might be the larger value of a SaaS Bitwarden - getting support and moving to a managed instance for HA.

I exported as JSON from VaultWarden (bottom) and imported as JSON Bitwarden data to the SaaS instance (above)

/content/images/2024/05/bitwarden-20.png

There was no issue in importing the two test fields I had in VaultWarden up to now

/content/images/2024/05/bitwarden-21.png

I checked one password just to be certain

/content/images/2024/05/bitwarden-22.png

Export

Can we go the other way?

Let’s create a new key we can be certain came from SaaS then export, and import to our on-prem instance

Premium

I take back what I said about Premium.

All they want is $10/year for up to 6 users and family sharing. That barely covers hosting - I think that is more than a reasonable ask.

/content/images/2024/05/bitwarden-23.png

App

It’s fine for a password manager to work on the web, but I need to see it work on my phone as well.

I installed from Google Play here and it worked.

/content/images/2024/05/bitwarden-25.jpg

The app blocks screenshots by default, but you can disable that in the “other” settings

/content/images/2024/05/bitwarden-26.jpg

Now I can show you that I have all the same accounts listed

/content/images/2024/05/bitwarden-27.jpg

Browser Extension

They also have a Chrome Browser Extension

/content/images/2024/05/bitwarden-28.png

I’ll try my self-hosted VaultWarden to start

In configuring my browser extension, I need to give details about my ‘self-hosted’ option

/content/images/2024/05/bitwarden-29.png

I can then use those for the login

/content/images/2024/05/bitwarden-30.png

/content/images/2024/05/bitwarden-31.png

To test, I’ll log out of the SaaS option and when I go to login again, I have a “new item” drop down that let’s me add credentials

/content/images/2024/05/bitwarden-32.png

I’ll now try using those to login from the web to the SaaS instance

/content/images/2024/05/bitwarden-33.png

Which completely worked

/content/images/2024/05/bitwarden-34.png

Summary

We took a bit longer than I would have liked to try and get the helm install to work. I could have moved to the Docker compose instance as detailed here, but I already had Vaultwarden up and didn’t need to add another instance.

We then tested the SaaS offering and looked at export and import to Vaultwarden. Knowing that our secrets are portable is a big win for Bitwarden. When looking at the pricing of the commercial offering, I was really impressed by the low costs and I might actually buy this as a new secret store for the family accounts.

Lastly, we looked briefly at the Android App app (there is an iOS app too) and Chrome Browser exentsion. I need to sit with the chrome extension a bit. I might try the Firefox one next.

Bitwarden Opensource Containers Kubernetes

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes