HashiCorp Vault — CSI Provider

1 minute read

The Vault CSI Provider uses the Kubernetes Secrets Store CSI Driver to mount Vault secrets as files inside pods — without a sidecar agent.


How CSI Provider Works

1. Pod requests a SecretProviderClass volume
2. CSI Driver calls the Vault Provider (DaemonSet)
3. Vault Provider authenticates to Vault (K8s auth)
4. Vault Provider fetches secrets
5. Secrets are mounted as files in the pod

Install CSI Driver + Vault Provider

# Install the 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 secrets-store-csi-driver/secrets-store-csi-driver \
    --namespace kube-system \
    --set syncSecret.enabled=true

# Install Vault CSI Provider
helm install vault hashicorp/vault \
    --namespace vault \
    --set "server.enabled=false" \
    --set "injector.enabled=false" \
    --set "csi.enabled=true"

SecretProviderClass

Define which secrets to mount:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
spec:
  provider: vault
  parameters:
    vaultAddress: "http://vault.vault.svc.cluster.local:8200"
    roleName: "myapp"
    objects: |
      - objectName: "db-password"
        secretPath: "secret/data/myapp/db"
        secretKey: "password"
      - objectName: "db-username"
        secretPath: "secret/data/myapp/db"
        secretKey: "username"
      - objectName: "api-key"
        secretPath: "secret/data/myapp/api"
        secretKey: "key"

Use in a Pod

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      serviceAccountName: myapp
      containers:
        - name: myapp
          image: myapp:latest
          volumeMounts:
            - name: vault-secrets
              mountPath: /mnt/secrets
              readOnly: true
      volumes:
        - name: vault-secrets
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: vault-db-creds

Secrets available at:

  • /mnt/secrets/db-password
  • /mnt/secrets/db-username
  • /mnt/secrets/api-key

Sync to Kubernetes Secrets

CSI can optionally create native K8s Secrets:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
spec:
  provider: vault
  parameters:
    vaultAddress: "http://vault.vault.svc.cluster.local:8200"
    roleName: "myapp"
    objects: |
      - objectName: "db-password"
        secretPath: "secret/data/myapp/db"
        secretKey: "password"
      - objectName: "db-username"
        secretPath: "secret/data/myapp/db"
        secretKey: "username"
  # Sync to a K8s Secret
  secretObjects:
    - secretName: myapp-db-secret
      type: Opaque
      data:
        - objectName: db-password
          key: password
        - objectName: db-username
          key: username

Then use as environment variables:

containers:
  - name: myapp
    image: myapp:latest
    env:
      - name: DB_PASSWORD
        valueFrom:
          secretKeyRef:
            name: myapp-db-secret
            key: password

Agent Injector vs CSI Provider

Feature Agent Injector CSI Provider
Sidecar required Yes No
Dynamic secret renewal Yes (auto) Limited
Custom templates Yes (full Go template) No
K8s Secret sync No Yes
Resource overhead Higher (sidecar per pod) Lower (DaemonSet)
Secret rotation Automatic On pod restart
Setup complexity Simple (annotations) More YAML (SecretProviderClass)

Quick Reference

Resource Purpose
SecretProviderClass Define which Vault secrets to mount
csi.driver: secrets-store.csi.k8s.io CSI volume type in pod spec
secretObjects Sync mounted secrets to K8s Secrets

Next: External Secrets