HashiCorp Vault external secrets
Use HashiCorp Vault to deliver secrets to Aidbox on Kubernetes — from deploying Vault to configuring a Client with a vault-backed secret.
This functionality is available starting from version 2602.
Overview
This tutorial walks through setting up HashiCorp Vault as an external secret store for Aidbox running on Kubernetes. By the end you will have:
- A HashiCorp Vault instance holding a client secret
- The Secrets Store CSI Driver mounting that secret as a file inside the Aidbox pod
- A vault config mapping the file to a named secret with resource scope
- A
Clientthat references the secret via a FHIR extension — the actual value is never stored in the database
flowchart LR
V(HashiCorp Vault):::blue2 -->|fetches secret| CSI(Vault CSI Provider):::neutral2
CSI -->|mounts as file| Pod(/run/vault-secrets/client-secret):::blue2
Config(vault-config.json):::blue2 -->|maps name → path + scope| Aidbox(Aidbox):::violet2
Pod -->|reads at runtime| Aidbox
Aidbox -->|stores reference| DB(_secret extension):::neutral2
Prerequisites
- A running Kubernetes cluster (this guide uses minikube for local testing)
kubectlandhelminstalled- Setup the local Aidbox instance using Getting Started guide
Step 1. Install Secrets Store CSI Driver
helm repo add secrets-store-csi-driver \
https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system \
--set syncSecret.enabled=true \
--set enableSecretRotation=true \
--set rotationPollInterval=30s
enableSecretRotation and rotationPollInterval control how often the driver checks Vault for updated secrets. Set the interval based on your rotation requirements.
Step 2. Install HashiCorp Vault
Deploy Vault with the CSI Provider enabled:
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault \
--set "server.dev.enabled=true" \
--set "csi.enabled=true"
Wait for pods to be ready:
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=vault \
--timeout=120s
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=vault-csi-provider \
--timeout=120s
This guide uses Vault in dev mode for simplicity. In production, deploy Vault with proper storage backend, TLS, and auto-unseal. See the Vault documentation for production deployment guidance.
Step 3. Configure Vault
Store a secret
kubectl exec vault-0 -- vault kv put secret/aidbox/client \
client-secret='super-secret-value'
Enable Kubernetes authentication
kubectl exec vault-0 -- vault auth enable kubernetes
kubectl exec vault-0 -- sh -c 'vault write auth/kubernetes/config \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"'
Create a policy and role
The policy grants read-only access to secrets under secret/data/aidbox/*. The role binds this policy to the aidbox ServiceAccount in the default namespace.
kubectl exec -i vault-0 -- vault policy write aidbox /dev/stdin <<'EOF'
path "secret/data/aidbox/*" {
capabilities = ["read"]
}
EOF
kubectl exec vault-0 -- vault write auth/kubernetes/role/aidbox \
bound_service_account_names=aidbox \
bound_service_account_namespaces=default \
policies=aidbox \
ttl=1h
Step 4. Create ServiceAccount
The Vault CSI Provider uses this ServiceAccount to authenticate with Vault via Kubernetes auth:
Step 5. Create SecretProviderClass
Defines which Vault secrets to mount and where to find them:
Step 6. Create vault config
The vault config JSON maps named secrets to file paths and declares which resources may access them:
Each entry under "secret" maps a secret name to:
| Field | Description |
|---|---|
path | Absolute path to the file containing the secret value |
scope | Array of resource references allowed to access this secret. Entries can be "ResourceType/id" (specific instance, e.g. "Client/basic") or "ResourceType" (any instance of that type, e.g. "Client") |
Step 7. Deploy Aidbox
See Recommended environment variables for the full list of required Aidbox settings.
Step 8. Verify secret mount
Confirm that the CSI Driver has mounted the secret file:
kubectl exec deploy/aidbox -- ls /run/vault-secrets/
Expected output:
client-secret
Verify the content:
kubectl exec deploy/aidbox -- cat /run/vault-secrets/client-secret
Step 9. Create Client with vault-backed secret
Create a Client that references the secret by name using the FHIR primitive extension pattern. The _secret element carries two extensions: data-absent-reason with value masked (indicating the field is intentionally absent) and secret-reference with the secret name from the vault config.
Reading the Client back returns the extension with the secret name, never the actual value:
At runtime, Aidbox resolves client-secret through the vault config, verifies that Client/basic is in scope, and reads the file content. You can verify this by authenticating as the client:
# Forward the Aidbox port
kubectl port-forward svc/aidbox 8888:8080
# Succeeds (returns 200 OK)
curl -u basic:super-secret-value http://localhost:8888/fhir/Patient
# Fails (returns 401 Unauthorized)
curl -u basic:wrong-password http://localhost:8888/fhir/Patient
Secret rotation
Update the secret in Vault:
kubectl exec vault-0 -- vault kv put secret/aidbox/client \
client-secret='new-secret-value'
The CSI Driver picks up the change based on rotationPollInterval (30s in this tutorial). Aidbox detects the file modification and invalidates its cache — the new value is used on the next access. No pod restart required.
Next steps
For the full reference on the vault config format, extension pattern, scope enforcement, and the list of supported resources, see External Secrets.
Last updated: