diff --git a/.gitignore b/.gitignore index 6e73417..05d1b8e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ helm/argo-stack/charts/external-secrets-*.tgz .coverage htmlcov/ .pytest_cache/ + +# Vault cluster keys created from `make vault-init` +secrets/ +cluster-keys.json diff --git a/Makefile b/Makefile index 832af43..9e76e3c 100644 --- a/Makefile +++ b/Makefile @@ -121,7 +121,7 @@ show-limits: kind: - kind delete cluster || true +# kind delete cluster || true envsubst < kind-config.yaml | kind create cluster --config - minio: @@ -385,19 +385,25 @@ help: # Vault Development Targets (Helm-based in-cluster deployment) # ============================================================================ +vault-init: + @echo "🌱 Initializing Vault..." + @mkdir -p secrets + @kubectl exec -n vault vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > secrets/cluster-keys.json + @echo "🔓 Unsealing Vault..." + @kubectl exec -n vault vault-0 -- vault operator unseal $$(jq -r ".unseal_keys_b64[0]" secrets/cluster-keys.json) + @echo "🔑 Root Token: $$(jq -r ".root_token" cluster-keys.json)" + vault-dev: @echo "🔐 Installing Vault dev server in Kubernetes cluster..." @helm repo add hashicorp https://helm.releases.hashicorp.com 2>/dev/null || true @helm repo update hashicorp + @kubectl create namespace vault 2>/dev/null || true @helm upgrade --install vault hashicorp/vault \ --namespace vault \ - --set server.dev.enabled=true \ - --set server.dev.devRootToken=$(VAULT_TOKEN) \ - --set injector.enabled=false \ - --set ui.enabled=true \ - --set server.dataStorage.enabled=false \ + --values vault/values.yaml \ --wait --timeout 2m + @echo "⏳ Waiting for Vault to be ready..." @kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vault -n vault --timeout=120s @echo "✅ Vault dev server running in cluster" @@ -526,6 +532,7 @@ vault-auth: ttl=1h @kubectl exec -n vault vault-0 -- vault read auth/kubernetes/role/argo-stack @echo "✅ Service account to Vault dev server added" + vault-shell: @echo "🐚 Opening shell in Vault pod..." @kubectl exec -it -n vault vault-0 -- /bin/sh diff --git a/docs/persistence.md b/docs/persistence.md new file mode 100644 index 0000000..943d132 --- /dev/null +++ b/docs/persistence.md @@ -0,0 +1,48 @@ +# Persistence + +> [!IMPORTANT] +> This deployment uses **Vault Integrated Storage (Raft)** with Kubernetes PersistentVolumeClaims (PVCs) +> to persist Vault data across pod restarts and node failures. Read this document carefully before +> enabling persistence in a shared or production cluster. + +## 1. How Vault persistence works with Raft integrated storage + +This setup runs Vault using [Integrated Storage](https://developer.hashicorp.com/vault/docs/configuration/storage/raft), +backed by persistent volumes: + +- Each Vault server pod in the StatefulSet mounts a **PersistentVolumeClaim**. +- The Raft storage backend writes: + - The encrypted Vault data (key/value secrets, auth backends, leases, etc.). + - Raft logs and snapshots used for replication and recovery. +- As long as the underlying PVCs remain intact, Vault’s data survives: + - Pod restarts + - Node drains or re-scheduling + - Helm upgrades of the chart + +In Raft mode: + +- One Vault node is the **leader**, handling reads and writes. +- Other nodes are **followers**, replicating the Raft log. +- If the leader fails, Raft elects a new leader from the remaining healthy nodes. + +The persistence behavior therefore depends on the lifecycle of the **PVCs**: + +- If pods are deleted but PVCs remain, **all Vault data is preserved**. +- If PVCs are deleted or the underlying storage is recreated from scratch, Vault behaves like a **new, + empty cluster** and must be re-initialized. + +## 2. Initializing and unsealing Vault with persistent storage + +The first time you deploy Vault with persistent storage, the Raft backend is empty and Vault starts in a +**sealed** and **uninitialized** state. + +### 2.1 One-time initialization + +Run these steps **once per new Raft storage** (per new set of PVCs). Do **not** re-run `vault operator init` +against an already-initialized storage backend. + +1. Port-forward or otherwise access the active Vault pod: + + ```bash + kubectl port-forward -n \ + svc/ 8200:8200 diff --git a/vault/values.yaml b/vault/values.yaml new file mode 100644 index 0000000..f64a8a3 --- /dev/null +++ b/vault/values.yaml @@ -0,0 +1,38 @@ +injector: + enabled: false + +ui: + enabled: true + +server: + dev: + enabled: false + + ha: + enabled: true + replicas: 3 + + raft: + enabled: true + setNodeId: true + config: | + ui = true + listener "tcp" { + # TLS is disabled here to simplify local development and testing only. + # Do NOT use this configuration as-is in production environments. + # For production, you should enable TLS by setting `tls_disable = 0` + # and configuring `tls_cert_file` and `tls_key_file` (and related options) + # as documented in the Vault listener TCP configuration: + # https://developer.hashicorp.com/vault/docs/configuration/listener/tcp + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "raft" { + path = "/vault/data" + } + + dataStorage: + enabled: true + size: 10Gi + mountPath: "/vault/data" diff --git a/docs/vault-architecture-diagrams.md b/vault/vault-architecture-diagrams.md similarity index 100% rename from docs/vault-architecture-diagrams.md rename to vault/vault-architecture-diagrams.md diff --git a/docs/vault-integration-summary.md b/vault/vault-integration-summary.md similarity index 100% rename from docs/vault-integration-summary.md rename to vault/vault-integration-summary.md diff --git a/docs/vault-seeding-strategy.md b/vault/vault-seeding-strategy.md similarity index 100% rename from docs/vault-seeding-strategy.md rename to vault/vault-seeding-strategy.md