Secure secrets management in Kubernetes with Bitwarden and Civo
This step-by-step tutorial covers setting up Bitwarden Secrets Manager, integrating it with Kubernetes, and deploying secrets securely in your clusters.
Written by
Marketing Team @ Civo
Written by
Marketing Team @ Civo
This piece with co-written by Keith Hubner, Integration Engineer (EMEA), at Bitwarden.
Handling secrets directly in Kubernetes presents a myriad of challenges that can complicate the management of sensitive information. Hardcoding secrets can lead to significant difficulties, as they become harder to manage and may create gaps in access control and auditing features. Additionally, the processes of rotating and revoking secrets can be cumbersome, increasing the risk of exposure.
To add to these challenges, integrating Kubernetes with external secrets management tools often proves complicated, making secure and efficient secrets management within Kubernetes a daunting task. Learn more about some of these challenges.
Effective secrets management is crucial in DevOps, as it securely handles sensitive information such as passwords, API keys, and tokens. Proper secrets management ensures secure storage, access control, rotation, and auditing, preventing the risk of exposure within code or pipelines. Tools like Bitwarden Secrets Manager play a vital role in automating secret management, seamlessly integrating it into DevOps workflows to enhance both security and efficiency.
By addressing these challenges and implementing robust secrets management strategies, organizations can protect their sensitive data while benefiting from the collaborative and agile nature of DevOps practices.
Prerequisites
Before getting started with this tutorial, you will need to ensure you have the following:
- Access to Bitwarden Secrets Manager account. You can read the help documentation here.
- You will also need a Civo account. Instructions on how to do this can be found here.
Implementing secrets management for Kubernetes with Bitwarden
Bitwarden Secrets Manager is a unique solution to efficiently and securely store and retrieve secrets. In order to create a more secure digital world, we need to make security convenient and effective.
Adding secrets to Kubernetes workflows is one way Bitwarden is helping make security fast and effective. Using the Secrets Manager Kubernetes Operator, which is deployed using Helm package manager, secrets can be stored and retrieved from Secrets Manager. In this tutorial, we will show you how this solution can be enabled and used with your Civo workflow.
Setting up Secrets Manager
To start, we will need to open the Bitwarden Secrets Manager on the web app. Use the product switcher in the bottom left corner of the web app to navigate to Secrets Manager if you have not yet used Secrets Manager.
From the Secrets Manager web app, create a new project, which will be the primary way of grouping secrets and assigning access later.

Step 1: Select Projects from the navigation bar and create a New project using the dropdown menu.
Step 2: Next, create a secret by selecting Secrets on the navigation. Create a new secret to store your needed access information, such as a cluster access token.
Step 3: Create a Machine Account by navigating to Machine Accounts on the navigation menu. Create a machine account and generate a new access token. This token will be used by your machine to allow Bitwarden Secrets Manager to retrieve secrets from the project.
Step 4: Return to the project that was created and assign the following items to the project:
- The created secrets
- Assign users to the project
- Machine account created
Setting up Civo
In this example we will create a new cluster with one medium node and then save and merge our kubeconfig. For more information, you can refer to the Civo documentation.
Create a cluster:
civo k3s create -n 1 -s g4s.kube.medium --merge --save --wait
Save the Civo API key in Secrets Manager Once the Civo CLI has been installed, you will be required to retrieve a Civo API key. This can be securely stored in secrets manager. Retrieve the API key from your Civo Dashboard. Once the API key has been saved, copy the value and Run:
For additional information on Chart setup and adding the repository to Helm, see https://bitwarden.com/help/secrets-manager-kubernetes-operator/#add-the-repository-to-helm.
Create values:
helm show values bitwarden/sm-operator --devel > my-values.yaml
You may wish to edit fields:
# How often the secrets synchronize in seconds. Minimum value is 180.bwSecretManagerResreshInterval: 300# Cloud region for synccloudRegion: US
After changes have been made, upgrade the release to a new chart by running:
helm upgrade sm-operator bitwarden/sm-operator -i --debug -n sm-operator-system --create-namespace --values my-values.yaml --devel
Return to the Secrets Manager web vault and copy the secret IDs and Machine account access token.
We will deploy a test app and therefore create a test namespace for this and the secrets:
kubectl create namespace test
kubectl create secret generic bw-auth-token -n test --from-literal=token="<your_machine_token_here>"
Create a secret
If using the following example, be sure to replace the values for your_org_id_here and your_secret_id_here. These values can be retrieved from the Secrets Manager web vault.
kubectl apply -f - <<EOFapiVersion: k8s.bitwarden.com/v1kind: BitwardenSecretmetadata:namespace: testlabels:app.kubernetes.io/name: bitwardensecretapp.kubernetes.io/instance: bitwardensecret-sampleapp.kubernetes.io/part-of: sm-operatorapp.kubernetes.io/managed-by: kustomizeapp.kubernetes.io/created-by: sm-operatorname: bitwardensecret-samplespec:organizationId: "<your_org_id_here>"secretName: bw-sample-secretmap:- bwSecretId: <your_secret_id_here>secretKeyName: test__secret__1- bwSecretId: <your_secret_id_here>secretKeyName: test__secret__2authToken:secretName: bw-auth-tokensecretKey: tokenEOF
Check that the secrets and operator are working in K8s:
kubectl get bitwardensecrets -n testNAME AGEbitwardensecret-sample 27m
Check the health of the operator:
kubectl describe bitwardensecrets -n test
Check the sync status:
Message: Completed sync for test/bitwardensecret-sampleReason: ReconciliationCompleteStatus: TrueType: SuccessfulSyncLast Successful Sync Time: 2024-09-25T10:36:12Z
Check secrets:
kubectl get secrets -n testNAME TYPE DATA AGEbw-auth-token Opaque 1 7m47sbw-sample-secret Opaque 2 6m56s
More secret information:
kubectl describe secrets bw-sample-secret -n testName: bw-sample-secretNamespace: testLabels: k8s.bitwarden.com/bw-secret=a9468555-5e5e-4621-93a1-eeaf028862bdAnnotations: k8s.bitwarden.com/custom-map:[{"bwSecretId": "<your_secret_id_here>","secretKeyName": "test__secret__1"},{"bwSecretId": "<your_secret_id_here>","secretKeyName": "test__secret__2"}]k8s.bitwarden.com/sync-time: 2024-09-25T10:36:12.922941793ZType: OpaqueData====test__secret__1: 10 bytestest__secret__2: 12 bytes
Check the secret contents:
kubectl get secret bw-sample-secret -n test -o jsonpath="{.data}" | jq 'to_entries[] | "\(.key): \(.value | @base64d)"'"test__secret__1: <your_secret_shown_here>""test__secret__2: <your_secret_shown_here>"
Test app to deploy a Bitwarden Secret
We can create and use Bitwarden secrets with the operator. The Kubernetes secret belongs to a namespace and will be injected with the data that the Secrets Manager machine account has access to.
Create test app:
kubectl apply -f - <<EOFapiVersion: apps/v1kind: Deploymentmetadata:name: my-deploymentnamespace: testlabels:app: my-deploymentspec:selector:matchLabels:app: my-deploymenttemplate:metadata:labels:app: my-deploymentspec:containers:- name: my-deploymentimage: nginxenvFrom:- secretRef:name: bw-sample-secretimagePullSecrets:- name: bw-sample-secretEOF
Check that these secrets have been injected into the app:
kubectl get pods -n test
Find the test pod and copy the name:
my-deployment-7ff457d764-qsp5m 1/1 Running 0 15m
View the env vars of the pod:
kubectl exec -it my-deployment-7ff457d764-qsp5m -n test -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=my-deployment-7ff457d764-g82dxNGINX_VERSION=1.27.1NJS_VERSION=0.8.5NJS_RELEASE=1~bookwormPKG_RELEASE=1~bookwormDYNPKG_RELEASE=2~bookwormtest__secret__2=<your_secret_shown_here>"test__secret__1=<your_secret_shown_here>"KUBERNETES_SERVICE_HOST=10.43.0.1KUBERNETES_SERVICE_PORT=443KUBERNETES_SERVICE_PORT_HTTPS=443KUBERNETES_PORT=tcp://10.43.0.1:443KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443KUBERNETES_PORT_443_TCP_PROTO=tcpKUBERNETES_PORT_443_TCP_PORT=443KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1TERM=xtermHOME=/root
If we change the secret value in Secrets Manager, we can restart the deployment and then check the value has changed:
kubectl get secret bw-sample-secret -n test -o jsonpath="{.data}" | jq 'to_entries[] | "\(.key): \(.value | @base64d)"'"test__secret__1: <your_new_secret_vaule_shown_here>""test__secret__2: <your_new_secret_vaule_shown_here>"
You will see the value has changed in the secret, but not yet in the pod, until it is restarted and it grabs the new values:
kubectl exec -it my-deployment-7ff457d764-p2fcl -n test -- envPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=my-deployment-7ff457d764-p2fclNGINX_VERSION=1.27.1NJS_VERSION=0.8.5NJS_RELEASE=1~bookwormPKG_RELEASE=1~bookwormDYNPKG_RELEASE=2~bookwormtest__secret__2=<your_new_secret_vaule_shown_here>"test__secret__1=<your_new_secret_vaule_shown_here>"KUBERNETES_SERVICE_PORT=443KUBERNETES_SERVICE_PORT_HTTPS=443KUBERNETES_PORT=tcp://10.43.0.1:443KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443KUBERNETES_PORT_443_TCP_PROTO=tcpKUBERNETES_PORT_443_TCP_PORT=443KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1KUBERNETES_SERVICE_HOST=10.43.0.1TERM=xtermHOME=/root
Summary
In this tutorial we demonstrated using the Secrets Manager Helm Operator to deploy a secret stored in Secrets Manager. This setup has allowed us to store and retrieve the secrets for use in any Kubernetes project.
Overview of steps completed:
- Setup Secrets Manager from the UI to include: Project, Secret, and Machine Account.
- Setup Kubernetes cluster using Civo’s CLI.
- Setup the Bitwarden Helm repository and created a custom configuration file.
- Created a BitwardenSecret object to be deployed by the operator.

Marketing Team @ Civo
Civo is the Sovereign Cloud and AI platform designed to help developers and enterprises build without limits. We bridge the gap between the openness of the public cloud and the rigorous security of private environments, delivering full cloud parity across every deployment. As a team, we are dedicated to providing scalable compute, lightning-fast Kubernetes, and managed services that are ready in minutes. Through CivoStack Enterprise and our FlexCore appliance, we empower organizations to maintain total data sovereignty on their own hardware.
Our mission is to make the cloud faster, simpler, and fairer. By providing enterprise-grade NVIDIA GPUs and streamlined model management, we ensure that high-performance AI and machine learning are accessible to everyone. Built for transparency and performance, the Civo Team is here to give you total control over your infrastructure, your data, and your spend.
Share this article