Skip to content

docs(adr): propose Reloader for out-of-band secret rotation - #189

Merged
jdwillmsen merged 2 commits into
mainfrom
docs/reloader-decision
Aug 3, 2026
Merged

docs(adr): propose Reloader for out-of-band secret rotation#189
jdwillmsen merged 2 commits into
mainfrom
docs/reloader-decision

Conversation

@jdwillmsen

Copy link
Copy Markdown
Member

Settles the deferred evaluation of Stakater Reloader by measuring the gate
against the live cluster rather than the counts recorded when it was filed.
No cluster object was changed and nothing was installed.

What the evidence shows

The scale half of the gate has not fired. The deployments repo still
has exactly one chart defining a ConfigMap and one workload mounting config
(charts/servicediscovery), against a threshold of more than three.

The rotation half has. A sweep of all 21 ExternalSecrets against every
Deployment, StatefulSet and DaemonSet found 13 landing in a Secret consumed
as environment by nine distinct Deployments across seven namespaces. For
each of those, ESO updates the Secret within its refresh interval and the
running process keeps the value it started with, with every control-plane
signal green throughout.

The checksum/config annotation added in the common library chart works
and is live on both servicediscovery Deployments — it just cannot reach
this class by construction, which is what the original ticket predicted.

Recommendation

Adopt, with four conditions that belong in the install rather than after it:
namespaced RBAC instead of the chart's ClusterRole, --reload-strategy=annotations
instead of the env-vars default, the ignore entry in both ApplicationSets
(services-appset.yaml and deployments-appset.yaml — the two usersrole
Deployments come from the second), and explicit requests and limits.

The memory objection that originally gated this does not survive measurement:
at the namespace default request of 64Mi, Reloader is 0.065% of the cluster's
96.3 GiB allocatable, and the informer cache is bounded by ~1.3 MB of Secret
and ConfigMap data cluster-wide.

Status: proposed, not accepted

The chart's ClusterRole grants cluster-wide Secret read plus workload patch,
which together form a cluster-admin-equivalent escalation path. That is a
judgement call, not a measurement, so this is filed as proposed for a human
decision. Merging this ADR is the decision; it installs nothing on its own.

🤖 Generated with Claude Code

jdwillmsen and others added 2 commits August 2, 2026 23:45
Records the evaluation that settles the deferred gate on out-of-band
secret rotation. A live sweep of every ExternalSecret against every
Deployment, StatefulSet and DaemonSet found nine Deployments consuming an
ESO-managed Secret as environment, where a Vault rotation updates the
Secret and never reaches the running process.

Recommends Stakater Reloader on the annotations strategy, opt-in per
workload, with the ignore entry in both ApplicationSets and explicit
resources. Rejects the app-level file watch and hashed ConfigMap names
with the reasons each fails, and re-measures the memory objection that
originally gated the evaluation: 64Mi is 0.065% of cluster allocatable.

Status is proposed, not accepted -- the cluster-wide RBAC the chart
requires is a judgement call for a human, and nothing was installed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…quisite

Two prose defects that would mislead an executor.

The RBAC mitigation was not implementable. Keeping one global release and
hand-substituting namespaced Roles cannot work: watch scope is set by
KUBERNETES_NAMESPACE, which watchGlobally=false supplies from the pod's own
namespace, and the released chart has no namespaces value while v1.4.19 has
no --namespaces flag. A globally-watching Reloader issues a cluster-scoped
LIST/WATCH that namespaced Roles cannot satisfy, so the informer fails its
list and the likely repair is restoring the ClusterRole with the safeguard
gone. Replaced with the three real options -- seven per-namespace releases,
tracking unreleased master, or accepting the ClusterRole as a stated cost.

The opt-in annotation belongs on the Deployment object, and five of the nine
workloads render from templates offering only podAnnotations. Recorded as a
prerequisite with the templates and repositories, three of the five being
outside this repo. The remaining four were each confirmed by render.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jdwillmsen
jdwillmsen force-pushed the docs/reloader-decision branch from b484636 to a52f991 Compare August 3, 2026 04:46
@jdwillmsen
jdwillmsen merged commit ed831b4 into main Aug 3, 2026
13 checks passed
@jdwillmsen
jdwillmsen deleted the docs/reloader-decision branch August 3, 2026 04:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant