diff --git a/kubernetes/infrastructure/openbao/rotation/authentik.yaml b/kubernetes/infrastructure/openbao/rotation/authentik.yaml index dd906b68..6717282d 100644 --- a/kubernetes/infrastructure/openbao/rotation/authentik.yaml +++ b/kubernetes/infrastructure/openbao/rotation/authentik.yaml @@ -63,7 +63,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["random"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/claude-oauth.yaml b/kubernetes/infrastructure/openbao/rotation/claude-oauth.yaml index 47d889b8..ff561fab 100644 --- a/kubernetes/infrastructure/openbao/rotation/claude-oauth.yaml +++ b/kubernetes/infrastructure/openbao/rotation/claude-oauth.yaml @@ -68,7 +68,7 @@ spec: type: RuntimeDefault containers: - name: sync-woodpecker - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:828bf4863286411bfd7ef9fdff617586588050c814743bec601a9222692162e4 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["woodpecker-secret"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/cloudflare.yaml b/kubernetes/infrastructure/openbao/rotation/cloudflare.yaml index 6b24d1a2..df71717e 100644 --- a/kubernetes/infrastructure/openbao/rotation/cloudflare.yaml +++ b/kubernetes/infrastructure/openbao/rotation/cloudflare.yaml @@ -61,7 +61,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:978fe53cad83a8d55319f6201ecd171775a101b21aaa21b7ec3c7b14304c6a42 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["cloudflare-roll"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/crowdsec-bouncer-key.yaml b/kubernetes/infrastructure/openbao/rotation/crowdsec-bouncer-key.yaml index d623ee35..61c6c5a0 100644 --- a/kubernetes/infrastructure/openbao/rotation/crowdsec-bouncer-key.yaml +++ b/kubernetes/infrastructure/openbao/rotation/crowdsec-bouncer-key.yaml @@ -63,7 +63,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["random"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/external-dns.yaml b/kubernetes/infrastructure/openbao/rotation/external-dns.yaml index cecc14fd..09e56795 100644 --- a/kubernetes/infrastructure/openbao/rotation/external-dns.yaml +++ b/kubernetes/infrastructure/openbao/rotation/external-dns.yaml @@ -56,7 +56,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:978fe53cad83a8d55319f6201ecd171775a101b21aaa21b7ec3c7b14304c6a42 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["cloudflare-roll"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/flux-git.yaml b/kubernetes/infrastructure/openbao/rotation/flux-git.yaml index 09537af1..0f2576ba 100644 --- a/kubernetes/infrastructure/openbao/rotation/flux-git.yaml +++ b/kubernetes/infrastructure/openbao/rotation/flux-git.yaml @@ -84,7 +84,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:b41cc3645f144179dddaa5ab1d32282c57c257432a613c15a6e15634fd0c4b81 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["gitea-token"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/home-assistant.yaml b/kubernetes/infrastructure/openbao/rotation/home-assistant.yaml index b2b81bb8..b986897e 100644 --- a/kubernetes/infrastructure/openbao/rotation/home-assistant.yaml +++ b/kubernetes/infrastructure/openbao/rotation/home-assistant.yaml @@ -68,7 +68,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["ha-token"] securityContext: allowPrivilegeEscalation: false @@ -130,7 +130,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["ha-token"] securityContext: allowPrivilegeEscalation: false @@ -192,7 +192,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["ha-token"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/kustomization.yaml b/kubernetes/infrastructure/openbao/rotation/kustomization.yaml index a81b73b7..dc26fe3f 100644 --- a/kubernetes/infrastructure/openbao/rotation/kustomization.yaml +++ b/kubernetes/infrastructure/openbao/rotation/kustomization.yaml @@ -4,6 +4,46 @@ kind: Kustomization # (intra-namespace OpenBao access → no NetworkPolicy needed). One file per rotated # secret. The matching OpenBao policy/role for each lives in ../policies/ and is # bootstrapped manually (not GitOps-managed). +# +# IMAGE PINNING: every rotator here uses a DATED TAG of secret-rotator +# (`:YYYY-MM-DD`), never a digest and never `:latest`. This is a registry-local +# image, so it does not need digest pinning, and a dated tag makes "what actually +# runs" a reviewable diff. +# +# Digests were tried and actively broke: untagged manifests are garbage-collected +# once :latest and the dated tags move past them, so a digest pinned today stops +# resolving after a few rebuilds. That decay is driven by edits to +# .woodpecker/build-ci-images.yml — a shared file — so adding an UNRELATED image +# republishes secret-rotator and orphans the manifest every rotator here was +# pinned to. 11 of these were pinned to digests that no longer existed and would +# have ImagePullBackOff'd on their first scheduled run (issue #458); because they +# are quarterly, that would have looked like nothing happening rather than like a +# failure. +# +# Keep them all on one tag: it is a single binary containing every mode +# (random | ha-token | cloudflare-roll | netbird-key | proxmox-token | +# gitea-token | woodpecker-secret), so a split serves no purpose and just makes +# the next bump partial. +# +# ADDING A ROTATOR: copy an existing file, and check you did not inherit a +# digest pin from an older example — nothing enforces this automatically, so it +# is easy to reintroduce silently and the failure only shows up months later on +# the first scheduled run: +# +# grep -l 'image:.*@sha256' \ +# kubernetes/infrastructure/openbao/rotation/*.yaml | grep -v kustomization.yaml +# +# (want: no output. The trailing filter is not padding — this file documents the +# pattern, so it necessarily matches its own grep. Verified both directions: +# silent across this directory as it stands, and it still reports a file that +# actually carries an `image: …@sha256:` pin.) +# +# BUMPING the tag: verify the new one exists BEFORE committing — a tag that was +# never built fails exactly like the digests did, and just as quietly: +# +# kubectl run tagcheck --rm -i --restart=Never \ +# --image=gitea.renner.uno/gitea_admin/secret-rotator: \ +# --command -- /secret-rotator # prints the usage/mode list if the tag resolves resources: - paperless.yaml - woodpecker.yaml diff --git a/kubernetes/infrastructure/openbao/rotation/mosquitto-health.yaml b/kubernetes/infrastructure/openbao/rotation/mosquitto-health.yaml index d61f1c10..bc7ec805 100644 --- a/kubernetes/infrastructure/openbao/rotation/mosquitto-health.yaml +++ b/kubernetes/infrastructure/openbao/rotation/mosquitto-health.yaml @@ -88,22 +88,11 @@ spec: type: RuntimeDefault containers: - name: rotate - # DATED TAG, not a digest and never :latest — this is our own - # image, so it does not need digest pinning, and a floating tag - # would make what runs here unreviewable. - # - # Deliberately NOT copying the sibling rotators' digest pins: those - # digests NO LONGER EXIST in the registry (verified by pulling them - # — NotFound), because untagged manifests are garbage-collected - # once :latest and the dated tags move past them. 11 of the 14 - # existing rotators are pinned that way and will ImagePullBackOff - # on their first run — see issue #458. Copying the convention would - # have shipped a CronJob broken from the day it was written. - # - # Verified for THIS tag: it pulls, `random` is among its modes, and - # it carries the RANDOM_BYTES>=16 floor documented below - # (RANDOM_BYTES=8 exits "refusing to generate a weak secret"). - # Bumping the date is a deliberate, reviewable edit. + # Dated tag, per the convention documented in kustomization.yaml — + # every rotator here now uses the same one. Verified for this tag: + # it pulls, `random` is among its modes, and it carries the + # RANDOM_BYTES>=16 floor documented below (RANDOM_BYTES=8 exits + # "refusing to generate a weak secret"). image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["random"] securityContext: diff --git a/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml b/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml index 08d4e0ea..3735d203 100644 --- a/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml +++ b/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml @@ -59,7 +59,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:49bfddd8eeb5fd871cbeb05bc3b4e2fcaaab7d9e24cc3007caae6084b4634c18 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["netbird-key"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/paperless.yaml b/kubernetes/infrastructure/openbao/rotation/paperless.yaml index 258d504e..111cad63 100644 --- a/kubernetes/infrastructure/openbao/rotation/paperless.yaml +++ b/kubernetes/infrastructure/openbao/rotation/paperless.yaml @@ -63,7 +63,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["random"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/pve-exporter.yaml b/kubernetes/infrastructure/openbao/rotation/pve-exporter.yaml index fe204763..dd5ca494 100644 --- a/kubernetes/infrastructure/openbao/rotation/pve-exporter.yaml +++ b/kubernetes/infrastructure/openbao/rotation/pve-exporter.yaml @@ -63,7 +63,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:97c22bcff324a93d2bb7bf3adb2894b405ed1a8035d02ad1ff6baced3813cefe + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["proxmox-token"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/review-bot.yaml b/kubernetes/infrastructure/openbao/rotation/review-bot.yaml index cc863669..91662e2b 100644 --- a/kubernetes/infrastructure/openbao/rotation/review-bot.yaml +++ b/kubernetes/infrastructure/openbao/rotation/review-bot.yaml @@ -81,7 +81,7 @@ spec: type: RuntimeDefault initContainers: - name: rotate-mint - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:828bf4863286411bfd7ef9fdff617586588050c814743bec601a9222692162e4 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["gitea-token"] securityContext: allowPrivilegeEscalation: false @@ -128,7 +128,7 @@ spec: memory: 48Mi containers: - name: sync-woodpecker - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:828bf4863286411bfd7ef9fdff617586588050c814743bec601a9222692162e4 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["woodpecker-secret"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/shelly-manager.yaml b/kubernetes/infrastructure/openbao/rotation/shelly-manager.yaml index 2fe7c5ad..6eb593ca 100644 --- a/kubernetes/infrastructure/openbao/rotation/shelly-manager.yaml +++ b/kubernetes/infrastructure/openbao/rotation/shelly-manager.yaml @@ -63,7 +63,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["random"] securityContext: allowPrivilegeEscalation: false diff --git a/kubernetes/infrastructure/openbao/rotation/woodpecker.yaml b/kubernetes/infrastructure/openbao/rotation/woodpecker.yaml index 0fa7936b..fc32b36c 100644 --- a/kubernetes/infrastructure/openbao/rotation/woodpecker.yaml +++ b/kubernetes/infrastructure/openbao/rotation/woodpecker.yaml @@ -63,7 +63,7 @@ spec: type: RuntimeDefault containers: - name: rotate - image: gitea.renner.uno/gitea_admin/secret-rotator@sha256:159e5888ff4e23f5fe7fb1284711d81591635f840888e61402af311b0a76ba92 + image: gitea.renner.uno/gitea_admin/secret-rotator:2026-07-30 args: ["random"] securityContext: allowPrivilegeEscalation: false