Vault Secrets Operator¶
Vault Secrets Operator - HashiCorp Documentation
Challenge¶
Vault offers a complete solution for secrets lifecycle management, but that requires developers and operators to learn a new tool. Instead, developers want a cloud native way to access the secrets through Kubernetes and have no need to understand Vault in great depth. Vault Secrets Operator (VSO) updates Kubernetes native secrets. The user accesses Kubernetes native secrets managed on the backend by HashiCorp Vault.
Solution¶
[...] The Vault Secrets Operator syncs the secrets between Vault and the Kubernetes secrets in a specified namespace. Within that namespace, applications have access to the secrets. The secrets are still managed by Vault, but accessed through the standard way on Kubernetes.
The Vault Secrets Operator syncs the secrets between Vault and the Kubernetes secrets in a specified namespace as opposed to Vault CSI Driver which manages secret via mount volumes it manages kubernetes-native secret resources.
Prerequisites¶
- Vault installed and running (external)
- Helm HashiCorp repository added
- Kubernetes cluster
- Kubernetes authentication method setup in Vault see this guide
Installation¶
Part 1.Configuring Vault¶
Note
We will use pre-existing secret engine in Vault, we will be using kv2 engine named secret, secrets operator also supports kv2 and PKI (certificate) engines.*
Tip
This guide uses Vault Web UI for convinience, however the same steps can be performed easily using Vault CLI or API.
Firstly, we need to create secret we're gonna mount later on. We're gonna create a secret containing username and password for our application (in this case grafana).
- Open Vault's Web UI and login.
- On the left hand menu click on
Secret Enginesand thensecret. - You should see following screen:

- Click on
Create Secretand fill out a follow-up form

- Click
Saveand you should see your secret in the following form. Note the API path.

- Second you will need a policy that will allow your app to access the secret you've just created. Go to
Policies>Create ACL Policyand create a policy, likewise:

- Click
Saveand remember the policy name. - Lastly you will need to create a role that will bind the policy to the Kubernetes service account. Go to
Access>Kubernetes>Rolesand create a role, likewise:
Do not forget about adding the policy to the role.

Part 2. Installing Vault Secrets Operator¶
Now that we have our secret and policies set up in Vault, we can proceed with the installation of the Vault Secrets Operator.
The process is very simple using a Helm chart, we will create a minimal values.yaml file and install the chart.
You can also enable things like telemetry via Prometheus ServiceMon, or a transit encrpytion.
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault-secrets-operator hashicorp/vault-secrets-operator .
Part 3. Deploying a Kubernetes native secret¶
Now that we have the Vault Secrets Operator installed, we can deploy a Kubernetes secret that will be synced with Vault.
First of all we need to create an VaultAuth CRD that will be used for authenticating our secret.
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: grafana
name: grafana-web-gui
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: grafana-auth
namespace: grafana
spec:
method: kubernetes
mount: kubernetes
kubernetes:
role: grafana
serviceAccount: grafana-web-gui
Where mount is the name you gave your auth method in vault, and role is the name of the role you created earlier in this guide. It's important that the name of SA matches the name specified in the role in previous steps. You may also use preexisting SA.
Then we need to create a VaultSecret CRD that will represent the secret in Vault.
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: vault-kv-app
namespace: app
spec:
type: kv-v2
mount: secret
path: grafana/admin-credentials
destination:
name: grafana-admin-credentials-vault
create: true
refreshAfter: 30s
vaultAuthRef: grafana-auth
Where:
- type is the type of secret engine, in our case kv-v2
- mount is the name of the secret engine in Vault
- path is the path within the secret engine to the secret (so, without the secret/data prefix)
- destination is the name of the Kubernetes secret
- create is a boolean that specifies whether to create the secret if it doesn't exist
- name is the name of the Kubernetes secret
- refreshAfter is the time after which the secret will be refreshed
- vaultAuthRef is the name of the VaultAuth CRD that will be used for authenticating to Vault it must be in the same namespace as the VaultSecret CRD.
And that's it, after applying the CRD to the cluster Vault Secrets Operator will create a Kubernetes secret in the specified namespace and sync it with Vault. You should be able to descrbe it, and the CRD's decribe should also reflect state of the secret.
# kubectl describe vaultstaticsecret.secrets.hashicorp.com/grafana-creds -n grafana
Name: grafana-creds
Namespace: grafana
Labels: <none>
Annotations: <none>
API Version: secrets.hashicorp.com/v1beta1
Kind: VaultStaticSecret
Metadata:
Creation Timestamp: 2025-03-29T14:35:53Z
Finalizers:
vaultstaticsecret.secrets.hashicorp.com/finalizer
Generation: 2
Resource Version: 34176308
UID: d2cd0cc3-b77c-4e74-91af-97192dea31e1
Spec:
Destination:
Create: true
Name: grafana-admin-credentials-vault
Overwrite: false
Transformation:
Hmac Secret Data: true
Mount: secret
Path: grafana/admin-credentials
Refresh After: 30s
Type: kv-v2
Vault Auth Ref: grafana-auth
Status:
Last Generation: 2
Secret MAC: H2HF1r9yY844zTp0Ndjer6+m0qvezonUMzjiEAW77Xs=
Events: <none>
# kubectl get -n grafana secrets/grafana-admin-credentials-vault -o=yaml
apiVersion: v1
data:
_raw: <...>
password: <...>
username: <...>
kind: Secret
metadata:
creationTimestamp: "2025-03-29T21:34:39Z"
labels:
app.kubernetes.io/component: secret-sync
app.kubernetes.io/managed-by: hashicorp-vso
app.kubernetes.io/name: vault-secrets-operator
secrets.hashicorp.com/vso-ownerRefUID: d2cd0cc3-b77c-4e74-91af-97192dea31e1
name: grafana-admin-credentials-vault
namespace: grafana
ownerReferences:
- apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
name: grafana-creds
uid: d2cd0cc3-b77c-4e74-91af-97192dea31e1
resourceVersion: "34176278"
uid: 1b43465d-2f2e-4683-b119-8d3b1fb6c476
type: Opaque