Files
devops-infra-helm-charts-gcp/helm-overrides/gke-toolshed-prd-usc1/vault/custom-values.yaml
T
Mukul SharmaandClaude Opus 5 63c243ce51 GKE: Vault values, auto-unsealing from Cloud KMS
Same chart (0.34.1) and same shape as the homelab — production mode,
file storage, standalone, no HA, injector off — with one substantial
difference: this Vault unseals itself.

The homelab unseals with 3 of 5 Shamir keys after every restart, which
was fine on an always-on VM. These nodes are spot and can be reclaimed
at any hour, and a sealed Vault means every secret in the cluster is
unavailable until someone notices. The trade is named rather than
buried: unsealing now depends on GCP IAM rather than on people holding
key shares.

The seal stanza's values come from terraform output vault_seal, so
Terraform and this file cannot disagree about which key is used, and no
credential appears in either: the pod authenticates to KMS as its
Workload Identity. That binding names exactly vault/vault, so
serviceAccount.name is pinned and the GSA annotation set — miss either
and Vault starts, fails to reach KMS, and stays sealed with an error
that never mentions Workload Identity.

Verified against the live project: the rendered seal block matches the
existing key ring and key, which grant encrypt/decrypt to that service
account and nothing else.

Storage is 10Gi on standard-rwo. The homelab's 5Gi exists only because
local-path cannot expand a bound volume; this class can.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N
2026-09-12 14:23:17 +05:30

92 lines
3.3 KiB
YAML

vault:
# GKE counterpart of helm-overrides/k8s-admin-prd-ase1/vault, same chart
# (0.34.1) and same shape — production mode, file storage, standalone, no
# HA — with one substantial difference: this Vault unseals itself from
# Cloud KMS instead of by hand.
#
# The homelab unseals with 3 of 5 Shamir keys after every restart. That
# was fine on an always-on VM. Here the nodes are spot and can be
# reclaimed at any hour, and a sealed Vault means every secret in the
# cluster is unavailable until a human notices. The trade, stated plainly:
# unsealing now depends on GCP IAM rather than on people holding key
# shares.
#
# This is a fresh install, not an adoption. Init is still a one-off manual
# step (`vault operator init`), and with a KMS seal it returns RECOVERY
# keys plus a root token — recovery keys cannot unseal a running Vault,
# they exist to recover or rekey it. Keep them and the root token in a
# password manager; they belong in neither Git nor Vault itself.
injector:
enabled: false
server:
# The Workload Identity binding Terraform created names exactly
# vault/vault — this namespace and this service account name. The
# annotation is the other half of that pair. Miss either and Vault
# starts, fails to reach KMS, and stays sealed with a permission error
# that does not mention Workload Identity at all.
serviceAccount:
create: true
name: vault
annotations:
iam.gke.io/gcp-service-account: toolshed-vault@toolshed-testing-508208.iam.gserviceaccount.com
dataStorage:
enabled: true
# 10Gi rather than the homelab's 5Gi: that number exists only because
# local-path cannot expand a bound volume. standard-rwo can expand, so
# this is the chart default, not a constraint.
size: 10Gi
storageClass: standard-rwo
ha:
enabled: false
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
standalone:
enabled: true
# `ui = true` here AND ui.enabled below are both required — the chart
# has two separate toggles for the same thing, and setting only one
# leaves the UI 404ing (claude.md issue #10).
#
# The seal stanza's values come from terraform output vault_seal, so
# Terraform and this file cannot disagree about which key Vault
# unseals with. No credentials appear here: the pod authenticates to
# KMS as its Workload Identity, so there is no key file to mount,
# rotate, or leak.
config: |
ui = true
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_disable = "true" # lab only - enable TLS for anything beyond local testing
}
storage "file" {
path = "/vault/data"
}
seal "gcpckms" {
project = "toolshed-testing-508208"
region = "us-central1"
key_ring = "toolshed-vault"
crypto_key = "unseal"
}
# Single host — no Tailscale here. Plain HTTP through Contour, matching
# tls_disable above.
ingress:
enabled: true
ingressClassName: contour
hosts:
- host: "vault.35.238.248.203.nip.io"
paths: []
ui:
enabled: true