# HashiCorp Vault (noble) Standalone Vault with **file** storage on a **Longhorn** PVC (`server.dataStorage`). The listener uses **HTTP** (`global.tlsDisable: true`) for in-cluster use; add TLS at the listener when exposing outside the cluster. - **Chart:** `hashicorp/vault` **0.32.0** (Vault **1.21.2**) - **Namespace:** `vault` ## Install ```bash helm repo add hashicorp https://helm.releases.hashicorp.com helm repo update kubectl apply -f clusters/noble/apps/vault/namespace.yaml helm upgrade --install vault hashicorp/vault -n vault \ --version 0.32.0 -f clusters/noble/apps/vault/values.yaml --wait --timeout 15m ``` Verify: ```bash kubectl -n vault get pods,pvc,svc kubectl -n vault exec -i sts/vault -- vault status ``` ## Initialize and unseal (first time) From a workstation with `kubectl` (or `kubectl exec` into any pod with `vault` CLI): ```bash kubectl -n vault exec -i sts/vault -- vault operator init -key-shares=1 -key-threshold=1 ``` **Lab-only:** `-key-shares=1 -key-threshold=1` keeps a single unseal key. For stronger Shamir splits, use more shares and store them safely. Save the **Unseal Key** and **Root Token** offline. Then unseal once: ```bash kubectl -n vault exec -i sts/vault -- vault operator unseal # paste unseal key ``` Or create the Secret used by the optional CronJob and apply it: ```bash kubectl -n vault create secret generic vault-unseal-key --from-literal=key='YOUR_UNSEAL_KEY' kubectl apply -f clusters/noble/apps/vault/unseal-cronjob.yaml ``` The CronJob runs every minute and unseals if Vault is sealed and the Secret is present. ## Auto-unseal note Vault **OSS** auto-unseal uses cloud KMS (AWS, GCP, Azure, OCI), **Transit** (another Vault), etc. There is no first-class “Kubernetes Secret” seal. This repo uses an optional **CronJob** as a **lab** substitute. Production clusters should use a supported seal backend. ## Kubernetes auth (External Secrets / ClusterSecretStore) Run these **from your workstation** (needs `kubectl`; no local `vault` binary required). Use a **short-lived admin token** or the root token **only in your shell** — do not paste tokens into logs or chat. **1. Enable the auth method** (skip if already done): ```bash kubectl -n vault exec -it sts/vault -- sh -c ' export VAULT_ADDR=http://127.0.0.1:8200 export VAULT_TOKEN="YOUR_ROOT_OR_ADMIN_TOKEN" vault auth enable kubernetes ' ``` **2. Configure `auth/kubernetes`** — the API **issuer** must match the `iss` claim on service account JWTs. With **kube-vip** / a custom API URL, discover it from the cluster (do not assume `kubernetes.default`): ```bash ISSUER=$(kubectl get --raw /.well-known/openid-configuration | jq -r .issuer) REVIEWER=$(kubectl -n vault create token vault --duration=8760h) CA_B64=$(kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}') ``` Then apply config **inside** the Vault pod (environment variables are passed in with `env` so quoting stays correct): ```bash export VAULT_TOKEN="YOUR_ROOT_OR_ADMIN_TOKEN" export ISSUER REVIEWER CA_B64 kubectl -n vault exec -i sts/vault -- env \ VAULT_ADDR=http://127.0.0.1:8200 \ VAULT_TOKEN="$VAULT_TOKEN" \ CA_B64="$CA_B64" \ REVIEWER="$REVIEWER" \ ISSUER="$ISSUER" \ sh -ec ' echo "$CA_B64" | base64 -d > /tmp/k8s-ca.crt vault write auth/kubernetes/config \ kubernetes_host="https://kubernetes.default.svc:443" \ kubernetes_ca_cert=@/tmp/k8s-ca.crt \ token_reviewer_jwt="$REVIEWER" \ issuer="$ISSUER" ' ``` **3. KV v2** at path `secret` (skip if already enabled): ```bash kubectl -n vault exec -it sts/vault -- sh -c ' export VAULT_ADDR=http://127.0.0.1:8200 export VAULT_TOKEN="YOUR_ROOT_OR_ADMIN_TOKEN" vault secrets enable -path=secret kv-v2 ' ``` **4. Policy + role** for the External Secrets operator SA (`external-secrets` / `external-secrets`): ```bash kubectl -n vault exec -it sts/vault -- sh -c ' export VAULT_ADDR=http://127.0.0.1:8200 export VAULT_TOKEN="YOUR_ROOT_OR_ADMIN_TOKEN" vault policy write external-secrets - <