From 1430ef7b14b30afb2123de7866f662e175d4e129 Mon Sep 17 00:00:00 2001 From: Daniel Gines Date: Sun, 23 Aug 2026 03:59:30 +0000 Subject: [PATCH] feat(vault): DigitalOcean overlay with GCP Cloud KMS auto-unseal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DigitalOcean has no managed KMS, so auto-unseal there has no in-provider option: either an external KMS, or operator-held unseal shares re-entered on every pod restart. This standardises on GCP Cloud KMS via seal "gcpckms". Vault reaches that key through Workload Identity Federation — a projected ServiceAccount token, audience-scoped to the WIF provider, exchanged for a short-lived Google token. No service account JSON key exists, so none can leak. The credential configuration file describing the exchange carries no private key (Google documents it as non-confidential), which is why it mounts from a ConfigMap rather than a Secret. The file carries only what platform-root does not inject, mirroring vault-aws.yaml. Additive: no existing file changes, and no AWS or Azure deployment loads it. --- CHANGELOG.md | 14 +++++++ values/platform/vault-digitalocean.yaml | 54 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 values/platform/vault-digitalocean.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index d24cc9e..ded27c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ and the corresponding commit messages. ## [Unreleased] +### Added + +- `values/platform/vault-digitalocean.yaml` — Vault overlay for the + DigitalOcean provider. Auto-unseal uses GCP Cloud KMS (`seal "gcpckms"`), + reached through Workload Identity Federation, because DigitalOcean has no + managed key management service. No static Google credential is placed in the + cluster: a projected ServiceAccount token is exchanged for a short-lived one. + The seal stanza and the federation wiring are injected by platform-root's + DigitalOcean branch; this file carries only the static bits, mirroring + `vault-aws.yaml` and `vault-azure.yaml`. + + Purely additive — no existing file changes, and no AWS or Azure deployment + loads this overlay. + ## [0.43.0] - 2026-08-11 ### Fixed diff --git a/values/platform/vault-digitalocean.yaml b/values/platform/vault-digitalocean.yaml new file mode 100644 index 0000000..80fffb9 --- /dev/null +++ b/values/platform/vault-digitalocean.yaml @@ -0,0 +1,54 @@ +# DigitalOcean-specific overlay for HashiCorp Vault. +# Loaded in addition to vault.yaml when global.provider=digitalocean. +# +# Auto-unseal: GCP Cloud KMS via `seal "gcpckms"` in the Raft HCL config. The +# full `server.ha.raft.config` block, and the Workload Identity Federation +# wiring that lets Vault reach that key, are INJECTED by +# `bootstrap/platform-root/templates/vault.yaml` (DigitalOcean branch). This +# file carries only what is NOT injected. +# +# WHY GOOGLE KMS ON A DIGITALOCEAN CLUSTER +# +# Auto-unseal needs a KMS the cluster can reach and that survives the cluster +# itself. DigitalOcean has no managed key management service, so there is no +# in-provider option — the alternatives are an external KMS or manual unseal +# with operator-held key shares on every pod restart. GCP Cloud KMS is the +# external KMS this platform standardises on for DigitalOcean. +# +# The cross-cloud dependency is real and worth stating plainly: a Vault on +# DigitalOcean cannot unseal while Google KMS is unreachable. That is the +# price of not holding unseal shares by hand, and it is the same shape of +# dependency AWS and Azure deployments already have on their own KMS. +# +# NO STATIC CREDENTIAL REACHES THIS CLUSTER +# +# Vault authenticates to Google through Workload Identity Federation: a +# projected ServiceAccount token, audience-scoped to the WIF provider, is +# exchanged for a short-lived Google token, which then impersonates the +# service account that holds encrypt/decrypt on the key. No service account +# JSON key exists, so there is none to leak or rotate. +# +# The credential configuration file that describes that exchange carries no +# private key — Google documents it as non-confidential — which is why it is +# mounted from a ConfigMap rather than a Secret. +# +# Required IAM on the key (per Vault's gcpckms documentation): +# cloudkms.cryptoKeyVersions.useToEncrypt +# cloudkms.cryptoKeyVersions.useToDecrypt +# cloudkms.cryptoKeys.get +# — i.e. roles/cloudkms.cryptoKeyEncrypterDecrypter plus cryptoKeys.get. + +server: + # PVC storage class — empty so the cluster default is used. On DOKS that is + # `do-block-storage`, which is what the Raft data volume wants. Override per + # cluster via the client repo's `overrides/vault/values.yaml`. + dataStorage: + storageClass: "" + + # No cloud-IAM annotation here, and that is not an omission. IRSA and Azure + # Workload Identity attach an identity to the ServiceAccount because the pod + # authenticates as itself to a KMS in the same cloud. Here the identity is + # federated to a different cloud, so it arrives as a mounted token and a + # credential configuration rather than an annotation. + serviceAccount: + create: true