Replace the gitignored plaintext infra/tofu/.r2.env and backend.hcl with a single committed, SOPS/age-encrypted dotenv (secrets.sops.env) plus a thin wrapper (tf.sh), so tofu runs with no manual secret entry and secrets live encrypted in git like the repo's other secrets. - .sops.yaml: creation rule encrypting infra/tofu/secrets.sops.env (all values) to the existing age recipient. - secrets.sops.env: encrypted, PLACEHOLDER values only. Holds R2 S3 keys, the state-encryption passphrase (TF_STATE_PASSPHRASE scalar), and the Proxmox API token/endpoint. Operator fills real values via `sops`. - tf.sh: sops exec-env decrypts the dotenv, exports PROXMOX_VE_*/AWS_*, builds the multi-line TF_ENCRYPTION HCL from TF_STATE_PASSPHRASE, and runs tofu (injecting -backend-config=backend.r2.hcl on init). Proxmox token stays env-only, never a tofu variable/state. - backend.r2.hcl: committed non-secret R2 bucket/key/endpoint (account id is not a credential), passed to `tofu init`. - Retire .r2.env/backend.hcl(.example): drop the committed templates; keep the gitignore lines as a safety net for leftover local plaintext. - Update backend.tf/README/REMOTE-STATE for the new day-2 flow and DR story (repo + age key is sufficient). Verified: placeholder file round-trips sops -e/-d with the repo age key; tf.sh exports the expected VAR NAMES and builds valid TF_ENCRYPTION HCL (stub tofu, no real plan/secrets). Co-Authored-By: Claude Opus 4.8 <[email protected]>
45 lines
2.9 KiB
YAML
45 lines
2.9 KiB
YAML
# .sops.yaml — SOPS creation rules for the platform repo (ADR-0007, R-SEC-01).
|
|
#
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# CANONICAL LOCATION: this is the ONE .sops.yaml, at the REPOSITORY ROOT. SOPS
|
|
# discovers it by walking UP from the file being encrypted, so a root file governs
|
|
# every secret under platform/**, apps/**, catalog/**, and secrets/**.
|
|
# There is deliberately NO second copy under bootstrap/sops/ — a duplicate there
|
|
# would be discovered first for files in that subtree and shadow this one (that
|
|
# footgun is why the earlier bootstrap/sops/.sops.yaml was removed).
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
#
|
|
# HOW ENCRYPTION WORKS HERE:
|
|
# - Only committed secrets are encrypted; plaintext never enters git.
|
|
# - `encrypted_regex` encrypts ONLY the value fields (data/stringData), leaving
|
|
# keys and structure in cleartext → readable, reviewable diffs (ADR-0007).
|
|
# - `age` recipient below is the PUBLIC key from age-keygen.sh. Filling it is a
|
|
# bootstrap step; the matching PRIVATE key is installed in-cluster as the
|
|
# `sops-age` Secret (age-key-secret.md) so Flux can decrypt at reconcile.
|
|
#
|
|
# TO ENCRYPT A SECRET (operator, not agents):
|
|
# sops --encrypt --in-place path/to/secret.sops.yaml
|
|
# # picks up the age recipient + rules below automatically.
|
|
|
|
creation_rules:
|
|
# Platform + app Secrets. Convention: encrypted files are named `*.sops.yaml`
|
|
# or live under a `secrets/` directory anywhere in the tree.
|
|
- path_regex: (^|/)(platform|apps|catalog)/.*(secret|sops)\.ya?ml$
|
|
encrypted_regex: "^(data|stringData)$"
|
|
# age PUBLIC recipient (from age-keygen.sh) — safe to commit. To rotate, see
|
|
# bootstrap/sops/age-key-secret.md (add new recipient, re-encrypt, drop old).
|
|
age: "age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg"
|
|
|
|
- path_regex: (^|/)secrets/.*\.ya?ml$
|
|
encrypted_regex: "^(data|stringData)$"
|
|
# same age PUBLIC recipient as above.
|
|
age: "age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg"
|
|
|
|
# OpenTofu runtime secrets (dotenv). Holds the R2 S3 keys, the state-encryption
|
|
# passphrase, and the Proxmox API token that tf.sh injects into the provider's
|
|
# ENVIRONMENT (never a tofu variable/state). Dotenv, so `sops exec-env` / tf.sh
|
|
# can export cleanly; NO encrypted_regex here → SOPS encrypts EVERY value (the
|
|
# keys stay cleartext for reviewable diffs). Encrypted to the same age recipient.
|
|
- path_regex: (^|/)infra/tofu/secrets\.sops\.env$
|
|
age: "age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg"
|