Commit Graph
5 Commits
Author SHA1 Message Date
rock c5058dadc6 fix(iam): merge into the consumer Secret, and retry with a backoff
key-rotation-controller / image (push) Failing after 6s
Two defects that a real rotation exposed:

  merge      writeSecret replaced the whole Secret map, so every key a consumer
             keeps beside the credential disappeared. paperless lost
             SOCIALACCOUNT_PROVIDERS_JSON and immich lost config.json; both pods then
             failed to start while the rotation reported success.
  backoff    a Failed attempt returned 0, so the controller retried in a tight loop.
             It now waits two minutes and then retries.

All eight objects carry both identifiers ZITADEL needs: the app resource id for the
secret endpoint and the client id for introspection.

Result: seven rotations reach Succeeded with every consumer ready (grafana, comfyui,
argocd, forgejo, paperless, immich, minio). api-gw stays suspended because no workload
exists for it.
2026-10-27 19:59:23 -07:00
rock e800e2cf1c fix(iam): make a rotation attempt survive a failure
key-rotation-controller / image (push) Failing after 19s
Three defects, each seen in a live rotation of grafana:

  scope        the access token needs urn:zitadel:iam:org:project:id:zitadel:aud,
               otherwise the management API answers 401 Token.Invalid even though the
               assertion was accepted.
  app id       the objects carry the client id, and the secret endpoint takes the app
               resource id. They differ, so every call answered 404 Application
               doesn't exist. The correct ids are recorded per object.
  attempt time record ActiveCredentialID and LastRotation right after Prepare. Without
               it a later failure leaves LastRotation empty, the due check says rotate
               now, and each reconcile mints a new secret and restarts the consumer:
               grafana was rolled continuously and never became ready.

Also raise the consumer-ready budget to 8 minutes, because grafana needs about four,
and retry a failed attempt after 2 minutes instead of waiting a whole interval.
2026-10-27 18:39:59 -07:00
rock 3630721366 feat(iam): run the controller with the RBAC and image it needs
key-rotation-controller / image (push) Failing after 5s
Three faults stopped the controller from running, each found by running it:

  image arch    the first build was arm64 and every node is amd64, so the pod died
                with 'exec format error'. imagePullPolicy IfNotPresent kept the
                cached wrong-arch layer, so it is now Always.
  leases        leader election needs coordination.k8s.io/leases. Without it the
                controller never acquired its lock and never reconciled.
  secrets list  the manager caches Secrets, so it needs list and watch, not only
                get, create, update and patch.

go.mod and go.sum gained the procfs entry the build needs.
2026-10-27 17:46:25 -07:00
rock 60931fbba1 ci(iam): build and push the key-rotation-controller image
Dockerfile: static binary on distroless, no shell, user 65532.

Workflow runs on the golang runner for a push to main that touches the controller.
It builds two tags, the commit sha and 0.1.0, which is the tag the Deployment asks
for, and runs the image with --help so a broken binary fails the job instead of the
cluster.

The runners were dead before this: every task fetch logged 'lookup
forgejo-gitea-http.cicd.svc.cluster.local: no such host'. DNS resolved from a fresh
pod and the Service had a healthy endpoint, so the runner pods held stale network
state. A rollout restart cleared it and all three poll without error.
2026-10-27 14:50:33 -07:00
rock c2071da9d8 feat(iam): define a KeyRotation for every consumer except poimen-memory
Eight objects cover grafana, argocd, comfyui, minio, paperless, immich, forgejo and
api-gw. Each names its ZITADEL app, the Secret it writes, the consumer to roll and
whether the consumer reloads hot or needs a restart.

All are suspended: the controller image does not exist yet, and an unsuspended
object would claim an intent that no process acts on.

The README records the retirement target for the 34 .enc.yaml files: six
root-of-trust secrets that cannot rotate themselves survive, everything else
becomes a KeyRotation and leaves git.
2026-10-27 14:28:28 -07:00