Skip to content

Mounting secrets via CSI driver

Overview

This is a guide on how to mount secrets to your pods using the Secrets Store CSI Driver for HashiCorp Vault. For overview on installation and configuration process of the CSI Driver, refer to this guide.

Prerequisites

  • Kubernetes cluster
  • Vault installed and running (This guide assumes external installation)
  • Vault CSI Driver installed and configured see this guide
  • Kubernetes authentication method setup in Vault see this guide

Installation

Step 1. Create a secret via Vault UI

  1. Open desired Secret Engine (in this case it's a kv store), you should see a list of secrets, like this: Vault UI - Secret Store
  2. Click on Create secret button, fill in the form with desired key-value pairs and click Save, for example: Vault UI - Create Secret
  3. You should now see your secret in the UI. Note the API path as you will need it later (the overview page may vary depending on the secret engine you're using): Vault UI - Secret Overview
  4. Create a policy that will allow your application to access the secret:
    path "secrets/data/webapp-example" {
      capabilities = ["read"]
    }
    
    Vault UI - Create Policy
  5. Go to Access -> Authentication Methods and click on your kubernetes auth method, you should see list of related roles like this: Vault UI - Kubernetes Roles
  6. Click on Create Role button, fill in the form with desired values, remember to set policy in the Tokens section. Click Save, for example: Vault UI - Create Role

Step 2. Create a Kubernetes Mount & deploy a pod

Note

For the sake of the guide we will be deploying a simple "hello world" webapp container written in Go, from jweissig/app:0.0.1 image.

  1. Create a Kubernetes SecretProviderClass object:
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: vault-database
    spec:
      provider: vault
      parameters:
        vaultAddress: "https://192.168.88.212"
        vaultSkipTLSVerify: "true"
        roleName: "webapp-example"
        objects: |
          - objectName: "db-password"
            secretPath: "secret/data/webapp-example"
            secretKey: "password"
    
    Where:
  2. vaultAddress is the address of your Vault server
  3. vaultSkipTLSVerify is a boolean flag that tells the driver to skip TLS verification (Not recommended for production, the CA certificate should be mounted inside the provider pods, however for the sake of completeness of this guide we will assume that the certificate is not present)
  4. roleName is the name of the role you created in the previous step
  5. objects is a list of objects that you want to mount, where:
  6. objectName is the name of the object that will be mounted in the Kubernetes
  7. secretPath is the path to the secret in the Vault
  8. secretKey is the key from the key-value pair from Vault that will be mounted under this objectName

Additional parameters can be found in the official documentation.

$ kubectl apply -f secretproviderclass.yaml -n webapp
  1. Create a Kubernetes ServiceAccount object:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: vault-auth
    secrets:
    
    Make sure the name maches the one you've specified previously in the Vault role
    $ kubectl apply -f serviceaccount.yaml -n webapp
    

  2. Create a Kubernetes Pod object, tied to the previously created ServiceAccount:

    apiVersion: v1
    kind: Pod
    metadata:
      name: webapp
    spec:
      serviceAccountName: vault-auth
      containers:
      - image: jweissig/app:0.0.1
        name: webapp
        volumeMounts:
        - name: secrets-store-inline
          mountPath: "/mnt/secrets-store"
          readOnly: true
      volumes:
        - name: secrets-store-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "vault-database"
    
    $ kubectl apply -f pod.yaml -n webapp
    

  3. After deploying the pod make sure your secret is mounted correctly. If anything went wrong the pod will be raising warnings in the kubernetes event log.

If you want to use the secret across multiple pods, you can also mount it directly as a kuberntes secret. To do thise you need to modify the SecretProviderClass object, as follows:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-database
spec:
  provider: vault
  secretObjects:
  - data:
    - key: password
      objectName: db-password
    secretName: dbpass
    type: Opaque
  parameters:
    vaultAddress: "https://192.168.88.212"
    vaultSkipTLSVerify: "true"
    roleName: "webapp-example"
    objects: |
      - objectName: "db-password"
        secretPath: "secret/data/webapp-example"
        secretKey: "password"
This will then be accessible to any pod that's using any of the service accounts mentioned in the Vault role configuration, this secret will be created whenever the pod referencing it is started, and deleted when the pod is stopped.
kind: Pod
apiVersion: v1
metadata:
  name: webapp
spec:
  serviceAccountName: webapp-sa
  containers:
  - image: jweissig/app:0.0.1
    name: webapp
    env:
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: dbpass
          key: password
    volumeMounts:
    - name: secrets-store-inline
      mountPath: "/mnt/secrets-store"
      readOnly: true
  volumes:
    - name: secrets-store-inline
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: "vault-database"