Free & Secure Local Development: Bitwarden Secrets Manager with K3d + Walkthrough

Free & Secure Local Development: Bitwarden Secrets Manager with K3d + Walkthrough

Table of Contents

Originally posted on the Israeli Tech Radar on medium.

Free & Secure Local Development: Bitwarden Secrets Manager with K3d + Walkthrough

We all deal with secrets, and managing them effectively is crucial for security, especially when working with local development environments. I’ve personally relied on Bitwarden’s free tier for secure password management for years. I moved from a bunch of tools which I needed to be synchronized and kept in sync with my local machine, not because others are bad, but because Bitwarden has a CLI, Desktop, Chrome extension & web which seemly integrated with my workflows.

Now, I’m taking my security practices a step further by leveraging its CLI and Kubernetes controller to retrieve secrets in a secure and efficient manner, directly into my local K3d cluster.

As a consultant, I juggle multiple clients, each with their own set of services, APIs, and cloud environments. Bitwarden’s CLI, desktop app, and web interface allow me to seamlessly switch between these environments without compromising security or constantly looking up credentials. The ability to organize secrets by project/client within Bitwarden is a lifesaver. I regularly need to connect to databases, cloud storage, and various APIs, and Bitwarden ensures I can do so securely and efficiently.

It is very similar to using AWS / Google / Azure or Vault secret managers, but without the cost, in the past I wrote a short how to use Vault with K3d, but this time I’m using Bitwarden.

Before we start, I’m not affiliated with Bitwarden, just a happy user who appreciates a free and powerful solution which has all the required interfaces one needs to manage their secrets.

Let’s delve into why secret management is a must, how you can achieve it without spending a dime using Bitwarden Secrets Manager, and how to integrate it with your local Kubernetes development using K3d.

The walkthrough will be after the why section.

Why Secret Management Matters (Even Locally!) 🔑

You might be thinking, “Why bother with all this security overhead for my local machine?”

Well, here’s why it’s vital, even when developing locally:

(1) Prevent Accidental Commits: Hardcoding secrets in your code, Dockerfiles, or Kubernetes manifests is a recipe for disaster. They can easily be committed to version control, exposing sensitive information, potentially leading to security breaches, data loss, and reputational damage. Always use .gitignore and .dockerignore to prevent sensitive files from being committed.

(2) Consistent Environments: Managing secrets centrally allows you to easily switch between different environments (dev, staging, production) without having to manually edit configuration files or environmental variables. This promotes reproducible builds, deployments, and reduces errors.

(3) Good Habits: Developing secure habits in a local environment encourages consistency and helps ensure that you follow the same standards when deploying to larger and more critical environments. Good security practices should be part of your workflow, not an afterthought.

(4)Environment Parity: Once set up, this approach makes secret management simpler than any manual approach and more secure than using plain text configurations. The initial setup might seem complex, but the long-term benefits are worth it. It’s very similar to your staging and production environments.

Bitwarden: A Free-tier Secret Vault 🔐

Very similar to the external-secrets-operator, I described in this post which can work with AWS / Goolge / Azure & Vault secret managers.

Bitwarden is well-known for its secure password management tools, but it also offers a dedicated Secrets Manager product. When combined with its command-line interface (CLI) and a Kubernetes operator, it provides a powerful and free secrets management solution. The core components that we are leveraging are:

  • Bitwarden Secrets Manager CLI (bws): This command-line tool allows you to manage Bitwarden secrets. Bitwarden provides a free organization for 2 users and a limited number of projects which means you can try it before you buy it…

  • Bitwarden Secrets Manager Operator: This Kubernetes operator acts as a bridge between your cluster and your Bitwarden vault. It allows your pods and deployments to retrieve secrets from Bitwarden and mount them as Kubernetes secrets or environment variables, automating the process and making them available to your applications.

Not related but don’t confuse this with bw-cli with the bitwarden-secrets or bws which is the cli used to manage your personal bitwarden vault Bitwarden CLI (bw) is a command-line tool allows you to interact with your Bitwarden vault (primarily for passwords) directly from your terminal, it quite handy for scripts - out of scope for this post.

sm-operator - The Kubernetes operator for Bitwarden Secrets Manager

The Kubernetes operator pattern is a great way to manage secrets securely and efficiently. We’ll start from the CLI with bws to create the secret and then consume it in our local development environment using a BitwardenSecret CRD, as described in the following diagram:

using 1 token to access the secret vault — similar to how you’d do with any cloud providerusing 1 token to access the secret vault — similar to how you’d do with any cloud provider

This creates a secure workflow where:

  • Secrets are never stored in code

  • Access is controlled through Bitwarden’s authentication

  • Applications can access secrets without direct Bitwarden credentials

  • The process is automated through the operator

So, lets get our hand dirty with Bitwarden Secrets Manager & K3d🧑‍💻

Setting Up Bitwarden Secrets Management with K3d 🚀 — The Walkthrough

For sake of this post a simple k3d create cluster will suffice, once you have kubectl pointing to your cluster, you can proceed with the following steps.

  • k3d cluster list should yield something like this:
NAME          SERVERS   AGENTS   LOADBALANCER
k3s-default   1/1       0/0      true

Step 1: 👣 Install the Bitwarden Secrets Manager CLI (bws)

You can install the bws CLI using either cargo (if you have Rust installed) or a convenient installation script:

# If you have Rust and Cargo installed:
cargo install bws

# Alternatively, using the installation script:
curl -sSL https://bitwarden.com/secrets/install | sh

Official installation page can be found here

Step 2: 👣 Install the Bitwarden Secrets Manager Operator

We’ll use the official helm chart to install the Bitwarden Secrets Manager Operator.

    helm repo add bitwarden https://charts.bitwarden.com
    helm install sm-operator --namespace bitwarden --create-namespace bitwarden/sm-operator

The Bitwarden Secrets Manager Operator acts as a bridge between your Kubernetes cluster and your Bitwarden vault. It automates the process of retrieving secrets and making them available to your applications. In order to use it you will need ot provide a secret containing your Bitwarden access token.

Step 3: 👣 Create a Secret in Bitwarden Secrets Manager

Before you can use secrets in your Kubernetes cluster, you need to create them in your Bitwarden Secrets Manager account. You will need an access token to authenticate the bws CLI.

Generating an Access Token:

  1. Log in to your Bitwarden Secrets Manager web vault.

  2. Go to your organization and then to the Settings tab.

  3. Click on Organization API Key, and generate a new API key.

Copy the displayed access token. Important: This token is only shown once, so store it securely.

Using the Access Token:

For the sake of this walkthrough, set the BWS_ACCESS_TOKEN environment variable like so:

export BWS_ACCESS_TOKEN="your_access_token"

Creating a Botwarden Project:

bws project create k3d-demo

Creating a Bitwarden Secret:

Let’s create a sample secret named a-secret-name with the value a-secret-value. We’ll also associate it with a project (you can create a project in the Bitwarden Secrets Manager UI).

Lest first fetch the bitwarde PROJECT_ID we created in the prvious step:

export PROJECT_ID=`bws project list | jq -r '.[] | select(.name == "k3d-demo") | .id'`

Now lets create the secret in the project:

bws secret create a-secret-name a-secret-value $PROJECT_ID

Step 4: 👣 Create a Kubernetes Manifest to retrieve a secret from Bitwarden

Bwfore we fetch the secret we need to provide the access token to the secret-manager in order to fetch secrets:

  • Before applying the above manifest, you need to create a Kubernetes secret named bw-auth-token in the bitwarden namespace containing your BWS_ACCESS_TOKEN:
kubectl create secret generic bw-auth-token -n bitwarden --from-literal=token="$BWS_ACCESS_TOKEN"

Now, let’s create a BitwardenSecret resource that fetches your credentials from Bitwarden. Let’s create a file named bitwarden-secret.yaml with the following contents Please make sure to replace the placeholders with your actual values:

apiVersion: k8s.bitwarden.com/v1
kind: BitwardenSecret
metadata:
      name: credentials
      # Replace with the namespace where you installed the operator
      namespace: bitwarden 
    spec:
      # Replace with your Bitwarden organization ID
      organizationId: "YOUR_ORGANIZATION_ID" 
      # The name of the Kubernetes secret to be created
      secretName: credentials-from-bw 
      map:
          # Get this from `bws secret list`
        - bwSecretId: "YOUR_a-secret-name_SECRET_ID" 
          secretKeyName: a-secret-name
      authToken:
        # The secret containing your BWS_ACCESS_TOKEN
        secretName: bw-auth-token 
        secretKey: token

Now, apply the Manifest:

kubectl apply -f bitwarden-secret.yaml

This will create a Kubernetes secret named credentials in the bitwarden namespace, containing the sample secret fetched from Bitwarden.

Under the hood we can examine the resources created by the controller, we can see this secret is managed by the Bitwarden Secrets Manager Operator and is automatically fetched from Bitwarden and mounted as a Kubernetes secret like so:

kubectl get bitwardensecrets.k8s.bitwarden.com 
NAME                     AGE
credentials   17h

If we take a closer look at the secret, we can see that it has been populated with the secret value from Bitwarden and labaled with the secret id:

k get secrets --show-labels credentials 
NAME               TYPE     DATA   AGE   LABELS
credentials-from-bw   Opaque   1      17h   k8s.bitwarden.com/bw-secret=204fe41a-5471-4666-a92d-ddedad477136

These are common kubernetes patterns followed by many operators, e.g external-secrets-operator

How would you use this in a common deployment ?

An example of how you cold use the Secret in a Deployment which uses the secret as part of it’s configuration manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-web-app
      namespace: bitwarden
    spec:
      # ...
      template:
        # ...
        spec:
          containers:
            - name: my-web-app-container
              image: traefik/whoami:v1.10.1
              env:
                - name: USERNAME
                  value: admin
                - name: PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: credentials-from-bw
                      key: 'a-secret-name'

To conclude

In this example, we’re injecting a secret from the credentials secret into the my-web-app container as environment variables.

Benefits of This Approach

  • Security: Your secrets are stored securely in your Bitwarden vault, not in your code or local files.

  • Efficiency: The Bitwarden operator automatically retrieves and updates your secrets, removing the manual step.

  • Free: No additional cost with the Bitwarden free tier for a limited number of 2 users and projects.

  • Consistency: Your secrets management is consistent across local development and other environments.

As always, I hope you found this post useful and if you have any questions or feedback, please feel free to reach out.

Yours sincerely, HP

comments powered by Disqus

Related Posts

Navigating the Complexity of Modern Development: Introducing the Self-Service Development…

In the ever-evolving landscape of software development, the journey from idea to implementation has undergone a profound transformation. Cast your mind back to the 90s, where uniformity reigned supreme. Developers were akin to scribes, all writing in the same language, adhering to the dictates of a singular development environment. However, fast forward to today, and you’ll find a vastly different scenario. Each team possesses its arsenal of tools, a medley of programming languages, and a unique infrastructure blueprint. This diversity, while fostering innovation, also presents a formidable challenge: how to streamline the setup of development environments to ensure seamless productivity across the board.

Read More
Infrastructure as Code: Navigating Declarative and Imperative Approaches

Infrastructure as Code: Navigating Declarative and Imperative Approaches

Originally posted on the Israeli Tech Radar on medium.

I read somewhere in my late night browsing that 71% of infrastructure as code is done using Terraform. That’s a huge number, right?, and although I may not be accurate the truth isn’t that far from it. It’s almost become the default choice. And what if I told you that the default isn’t always the best for everyone?

Read More
A CloudNative Dev Experience 🎯 | Tikal TechRadarCon 2021

A CloudNative Dev Experience 🎯 | Tikal TechRadarCon 2021

Originally posted on the Israeli Tech Radar on medium.

TLDR; Just when we thought we’ve got it figured out! We learn there’s a better, faster way of doing things, frameworks, abstractions, and methodologies invented to make our development experience easier, and help us during our journey to building a platform. But…Do we really need one?/Should we adopt one? What impact will that have on our product? The following post will try to tackle some of these questions as we explore building a better Developer Experience.

Read More