From 7e9406dff0c3fce7e296a661a3b238dba425b1c0 Mon Sep 17 00:00:00 2001 From: ahmza Date: Thu, 3 Sep 2026 04:31:03 +0100 Subject: [PATCH] Fix .sops.yaml to scope encryption to secret payload fields only --- .sops.yaml | 10 ++++++++++ docs/10-secrets.md | 14 +++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.sops.yaml b/.sops.yaml index df8f906..4a9c153 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -8,6 +8,16 @@ # Convention: any file ending in .sops.yaml is a secret meant to be committed # encrypted. Plain .yaml files are never treated as secrets by this rule, so # an accidental `sops -e` on the wrong file won't silently do nothing useful. +# +# encrypted_regex scopes encryption to only the actual secret payload +# fields (data/stringData), leaving apiVersion, kind, and metadata (name, +# namespace, labels) in plain text. Without this, sops would also encrypt +# metadata.name/namespace into ciphertext strings containing characters +# Kubernetes object names can't legally contain, and the SopsSecret would +# fail to apply at all. sops-secrets-operator decrypts into a *separate* +# new Secret rather than mutating this object in place, so this scoping is +# the standard, safe pattern for it (unlike some other GitOps decrypt tools). creation_rules: - path_regex: .*\.sops\.yaml$ + encrypted_regex: ^(data|stringData)$ age: age1udejf5qa66rd6yvj5r8dz0fjnqfh0quf4t339c3cpqr26mkx2ydq3zel07 diff --git a/docs/10-secrets.md b/docs/10-secrets.md index b7f1d5e..8a0bc12 100644 --- a/docs/10-secrets.md +++ b/docs/10-secrets.md @@ -87,11 +87,15 @@ and it must happen before this file is ever `git add`ed: sops --encrypt --in-place apps/cert-manager/cloudflare-api-token.sops.yaml ``` -`sops` reads `.sops.yaml`, sees the filename ends in `.sops.yaml`, and -encrypts every value under `stringData` for your age public key. Open the -file afterward — the keys (`api-token`) stay readable, the values are now -unintelligible ciphertext with `sops` metadata appended. That's what's safe -to commit. +`sops` reads `.sops.yaml`, sees the filename ends in `.sops.yaml`, and — +because of that file's `encrypted_regex: ^(data|stringData)$` — encrypts +only the values under `stringData`, for your age public key. `apiVersion`, +`kind`, and everything under `metadata` (name, namespace) stay in plain +text on purpose: Kubernetes object names have to be plain DNS-safe +strings, and encrypting them would produce a `SopsSecret` that fails to +apply at all. Open the file afterward — `stringData`'s values are now +unintelligible ciphertext with `sops` metadata appended; everything else is +still readable. That's what's safe to commit. Once pushed and synced, the operator finds this `SopsSecret`, decrypts it using the private key mounted from step 2, and creates an ordinary