refactor(secrets): one OpenBao path for the one Hetzner S3 credential #676

Merged
k.renner merged 1 commits from hetzner-creds-shared-path into master 2026-08-27 19:04:24 +00:00
7 changed files with 44 additions and 32 deletions
@@ -15,4 +15,4 @@ spec:
name: hetzner-s3-creds # keeps the old Secret's name — consumers unchanged
dataFrom:
- extract:
key: seed/authentik/hetzner-creds
key: infrastructure/hetzner-s3
@@ -1,21 +1,14 @@
---
# Barman S3 credentials for ha-pg.
#
# FOLLOW-UP (#397): sourced from seed/paperless/hetzner-creds, which is not where
# they belong. woodpecker/hetzner-creds-externalsecret.yaml has the identical
# problem and the same note. The values are the same today, so this is correct
# but coupled: rotating paperless's Hetzner credentials would silently change
# the backup credentials for Home Assistant and Woodpecker too.
# One shared path for one shared credential — see the note in
# ../paperless/hetzner-creds-externalsecret.yaml.
#
# The fix is one dedicated path per consumer (seed/home-assistant/hetzner-creds)
# and a one-line change to the `key:` below. Creating it needs an OpenBao write:
#
# bao kv put homelab/seed/home-assistant/hetzner-creds \
# @<(bao kv get -format=json homelab/seed/paperless/hetzner-creds | jq '.data.data')
#
# Note the mount is `homelab`, so the path is homelab/seed/... — #397 records a
# 403 on `bao kv list seed`, which was a wrong-path error rather than a
# permissions problem.
# The earlier note here proposed a dedicated seed/home-assistant path. That was
# the wrong direction: it would have made a fifth copy of the same key look like
# a fourth independent credential. It did get one thing right, and it is worth
# keeping: the 403 recorded in #397 was a wrong-path error, not a permissions
# problem, because the mount is `homelab` and the path is homelab/seed/...
#
# The namespace allowlist on the openbao ClusterSecretStore
# (infrastructure/post-crds/openbao-clustersecretstore.yaml) must include this
@@ -35,4 +28,4 @@ spec:
name: hetzner-s3-creds
dataFrom:
- extract:
key: seed/paperless/hetzner-creds
key: infrastructure/hetzner-s3
@@ -15,4 +15,4 @@ spec:
name: hetzner-s3-creds # keeps the old Secret's name — consumers unchanged
dataFrom:
- extract:
key: seed/immich/hetzner-creds
key: infrastructure/hetzner-s3
@@ -1,3 +1,27 @@
# CANONICAL NOTE on the Hetzner S3 credentials. The other consumers point here
# rather than repeating it.
#
# There is exactly ONE Hetzner S3 key, and every barman ObjectStore in this
# cluster uses it. It used to live under four paths — seed/paperless,
# seed/authentik, seed/immich, seed/trmnl — with byte-identical contents,
# verified by comparing SHA-256 fingerprints of the synced Secrets rather than
# by reading them. The same key also sits in infrastructure/velero-hetzner, in
# a different shape (a single `cloud` field holding an AWS credentials file);
# its access key id fingerprints the same.
#
# Per-app paths implied a separation that did not exist. Rotating the key at
# Hetzner changes it for everyone regardless of how many paths hold a copy, so
# the separate paths bought nothing and cost a standing invitation to believe
# otherwise — #397 was opened on exactly that misreading, proposing to give
# woodpecker a fifth copy.
#
# Real separation needs one Hetzner key per consumer, and Hetzner keys can only
# be created by hand in their console. Until someone wants that, one path is the
# honest representation.
#
# Not yet folded in: velero. It needs the credentials file form, so moving it
# here means templating the INI from these fields — worth doing, not worth
# bundling with a path swap that touches every database backup.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
@@ -15,4 +39,4 @@ spec:
name: hetzner-s3-creds # keeps the old Secret's name — consumers unchanged
dataFrom:
- extract:
key: seed/paperless/hetzner-creds
key: infrastructure/hetzner-s3
@@ -12,4 +12,4 @@ spec:
name: hetzner-s3-creds
dataFrom:
- extract:
key: seed/trmnl/hetzner-creds
key: infrastructure/hetzner-s3
@@ -1,11 +1,8 @@
---
# Barman S3 credentials for wp-pg.
#
# Sourced from seed/paperless/hetzner-creds, which is not where they belong.
# Issue #397 wants woodpecker to own seed/woodpecker/hetzner-creds; creating
# that path needs OpenBao write access (`bao kv list seed` → 403 for the
# identities available here), so it stays open. The values are identical, so it
# is a one-line swap of the `key:` below once the path exists.
# One shared path for one shared credential — see the note in
# ../paperless/hetzner-creds-externalsecret.yaml.
#
# The namespace allowlist on the openbao ClusterSecretStore
# (infrastructure/post-crds/openbao-clustersecretstore.yaml) must include this
@@ -24,4 +21,4 @@ spec:
name: hetzner-s3-creds
dataFrom:
- extract:
key: seed/paperless/hetzner-creds
key: infrastructure/hetzner-s3
@@ -36,11 +36,9 @@ spec:
target:
name: hetzner-s3-creds
dataFrom:
# Reuses the paperless copy of the shared Hetzner S3 credentials (identical
# ACCESS_KEY_ID / ACCESS_SECRET_KEY / REGION) to avoid seeding a new OpenBao
# path for this migration — same approach woodpecker took. FOLLOW-UP: give
# wordpress its own seed/wordpress/hetzner-creds path once OpenBao can be
# written, and repoint this here (the value is the same, so it's a drop-in
# swap).
# One shared path for one shared credential. There is a single Hetzner S3
# key behind all of these — see the note in
# ../paperless/hetzner-creds-externalsecret.yaml for why per-app paths were
# the wrong shape and what would have to change for them to be right.
- extract:
key: seed/paperless/hetzner-creds
key: infrastructure/hetzner-s3