The goal is to replicate values from a Vault secret to K8S. There is a K8S operator called external-secrets (ESO) for this purpose. It can be deployed quite conveniently with Helm:

helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace

Next, we should first create a test secret in Vault.

vault kv put secret/foo my-value=mytopsecretvalue

On to the manifests!

The SecretStore: Connecting Kubernetes to Vault

apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "https://vault.myhomenet.lan"
      path: "secret"
      version: "v2"
      caBundle: "..."   # Base64-encoded CA certificate, see explanation below
      auth:
        tokenSecretRef:
          name: "vault-token"
          key: "token"

What’s happening here?

kind: SecretStore This is ESO’s way of defining a connection backend. Here, we’re telling ESO: “Whenever you need to fetch a secret, talk to this Vault instance.”

Vault provider details:

  • server: The Vault URL (https://vault.myhomenet.lan).
  • path: The mount path in Vault (secret/ in this case).
  • version: v2: This specifies the KV secrets engine version. Version 2 supports multiple versions of the same secret.
  • caBundle: A base64-encoded CA certificate, ensuring Kubernetes trusts Vault’s TLS connection.
  • auth: How ESO authenticates against Vault. Here we’re using a static token stored in a Kubernetes Secret (vault-token).

The ExternalSecret: Syncing Data from Vault into Kubernetes

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: vault-example
spec:
  refreshInterval: "15s"
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: example-sync
  data:
  - secretKey: foobar
    remoteRef:
      key: foo
      property: my-value

Breaking it down:

  • kind: ExternalSecret This is where you declare what you want from Vault and how to present it in Kubernetes.
  • refreshInterval: "15s" ESO checks Vault every 15 seconds to see if the secret changed. If it has, ESO updates the Kubernetes Secret automatically.
  • secretStoreRef Tells ESO to use the Vault connection we defined earlier (vault-backend).
  • target.name: example-sync ESO will create a native Kubernetes Secret called example-sync.

data section:

  • secretKey: foobar: Inside the Kubernetes Secret, the key will be foobar.
  • remoteRef.key: foo: Tells ESO to look up the Vault secret at secret/foo.
  • property: my-value: From that secret, only extract the field my-value.

Now, whenever the foo value changes in Vault, ESO will synchronize it into the secret eample-sync.