0001 — Ship both a namespaced and a cluster-scoped connection kind¶
- Status: Accepted
- Date: 2026-09-14
- Deciders: Project owner
- Supersedes: —
- Superseded by: —
Context¶
Every resource this operator manages — OAuth2Provider, SAMLProvider,
ProxyProvider, Application, Outpost, KubernetesServiceConnection,
DockerServiceConnection — needs to know two things before it can reconcile:
which authentik instance to talk to, and which API token to talk to it with.
That pairing is modelled as a separate connection object rather than repeated
on every resource, so a token rotation is one edit and a URL change is one edit.
The design question is what scope the connection object has, and the two plausible answers serve genuinely different deployments:
The multi-tenant platform. Several teams share a cluster. Each has its own
namespace, its own authentik service account, and its own token. A team must not
be able to read another team's token, and the platform team does not want to
mediate every provider a tenant creates. Here a connection has to be namespaced,
and its token Secret has to be resolved within that namespace.
The single-operator homelab or single-tenant cluster. One authentik, one
token, twenty namespaces full of applications. Copying the same
AuthentikConnection and the same token Secret into every namespace is
duplication that rots: rotating the token means finding and updating every copy,
and a missed copy is a silent reconcile failure in whichever namespace was
forgotten. Here a single cluster-scoped connection is obviously right.
Kubernetes has no scope that is both. The precedent in the ecosystem is to ship
two kinds: cert-manager has Issuer and ClusterIssuer, external-secrets has
SecretStore and ClusterSecretStore, Gateway API splits along similar lines.
Users arriving from those projects already have the mental model.
The genuine hazard is the one those projects also carry. A cluster-scoped
connection must name a namespace to read its token Secret from, which means
the operator needs cluster-wide Secret read permission. If the resolution
rules are sloppy, a tenant in namespace A can point a provider at a connection
and cause the operator to read a Secret in namespace B on their behalf. That
is the classic operator privilege-escalation bug — it is the same shape as the
confused deputy that cert-manager and external-secrets both had to write
explicit rules around, and it is easy to reintroduce by accident when adding a
"convenience" default later.
Options considered¶
Option A — Namespaced only (AuthentikConnection)¶
Ship exactly one kind, namespaced, whose token Secret is always in its own
namespace. The operator never needs cluster-wide Secret access.
- For: The smallest possible blast radius. RBAC is trivially correct: if you
can create a connection in a namespace, you could already read that
namespace's
Secrets, so no privilege is manufactured. No cross-namespace resolution code exists, so the confused-deputy bug is structurally impossible. - Against: Punishes the single-tenant case, which is the majority of
realistic installations. Token rotation becomes an N-namespace operation, and
users route around it — by syncing
Secrets with a third-party tool, which reintroduces cross-namespace credential flow outside our control and outside our audit trail. Making the safe path tedious does not make deployments safer.
Option B — Cluster-scoped only (ClusterAuthentikConnection)¶
Ship exactly one kind, cluster-scoped, naming its token Secret's namespace.
- For: One object, one token, no duplication. Simple for the single-tenant case, which is most of them.
- Against: Unusable in a multi-tenant cluster. Either the platform team
becomes a ticket queue for every provider a tenant wants, or tenants get
permission to create cluster-scoped objects that read arbitrary
Secrets — which is cluster-admin with extra steps. It also forces the operator to hold cluster-wideSecretread permission in installations that have no need for it, which is the wrong default for a security-adjacent component.
Option C — Both kinds, user's choice (chosen)¶
Ship AuthentikConnection (namespaced) and ClusterAuthentikConnection
(cluster-scoped). Every managed resource's spec.connectionRef carries a kind
so it can select either.
- For: Each deployment shape gets the model that fits it. Matches established ecosystem precedent, so the semantics are already familiar. The namespaced kind can be the documented default, leaving the cluster-scoped kind as a deliberate, auditable opt-in.
- Against: Two kinds to implement, document, test and keep in sync. The cluster-scoped kind carries the dangerous privilege, so its resolution rules have to be stated precisely and defended in tests forever.
Decision¶
Ship both. AuthentikConnection is namespaced and is the documented
default. ClusterAuthentikConnection is cluster-scoped and exists for
single-tenant clusters that want one connection for the whole cluster.
spec.connectionRef.kind selects between them.
The following rules are binding on the implementation and are not negotiable conveniences:
-
AuthentikConnectionresolves its tokenSecretonly from its own namespace.spec.tokenSecretRefhas nonamespacefield. There is no mechanism by which a namespaced connection reads aSecretelsewhere. -
ClusterAuthentikConnectionresolves its tokenSecretonly from the namespace named inspec.tokenSecretRef.namespace. That field is required. It has no default, and in particular it never defaults to the namespace of the resource that referenced the connection, nor to the operator's own namespace. A connection whose spec does not name a namespace is rejected at admission; it does not fall back.
This is the rule that prevents the privilege escalation. A tenant referencing
a cluster connection gets exactly the Secret the cluster administrator who
wrote that connection chose — never one selected by the tenant, and never one
selected implicitly by where the tenant's own resource happens to live.
-
A namespaced resource may reference a
ClusterAuthentikConnection, and this is a grant, not a leak. The tenant obtains the use of a token whose identity and scope a cluster administrator chose; they never obtain the token's bytes. The operator does not project the token into the tenant's namespace, echo it in status, or log it. -
A
ClusterAuthentikConnectionmay restrict which namespaces may reference it. An optionalspec.allowedNamespacesselector, empty meaning all, lets an administrator share one connection with a subset of the cluster without sharing it with everything. This is defence in depth on top of rule 2, not a substitute for it. -
Creating a
ClusterAuthentikConnectionis a cluster-admin-level privilege and must be documented as such wherever RBAC for this operator is discussed. It is not shipped in any default role bound to non-administrators.
Consequences¶
Positive¶
- Multi-tenant clusters can use the operator without any cross-namespace credential reach: install the namespaced kind, decline to grant the cluster-scoped one, and the escalation path does not exist for tenants.
- Single-tenant clusters get one connection object and one token to rotate.
- Rule 2 kills the confused-deputy bug by construction rather than by review
vigilance. There is no code path that consults the referencing resource's
namespace when resolving a cluster connection's
Secret, so a future contributor cannot weaken it by omission — only by deliberately adding one, which is a reviewable change. - The
kinddiscriminator inconnectionRefmeans adding a third connection shape later (a token from a workload-identity provider, say) does not break existing manifests.
Negative¶
- The operator's
ClusterRolemust includegetonsecretscluster-wide wheneverClusterAuthentikConnectionis installed. Installations that do not use it should strip that permission, and the Helm chart needs a values flag to make that easy. - Two kinds means duplicated validation, duplicated status handling and duplicated tests. Shared internal code will need care to keep the two resolution paths genuinely distinct where it matters — a shared resolver taking a "namespace" argument is precisely how rule 2 gets accidentally softened.
- Users must understand the difference to pick correctly, and the wrong choice
is silently more dangerous rather than loudly broken. The README and
SECURITY.mdboth have to carry the warning, and warnings decay. spec.allowedNamespacesis additional surface that has to be enforced on every reconcile, not only at admission, since a namespace's labels can change after a reference is established.
Follow-up work¶
- Envtest coverage asserting that a cluster connection's
Secretis not read from the referencing resource's namespace, including the case where aSecretof the same name exists in both. This test is the executable form of rule 2 and should be treated as load-bearing. - A Helm values flag to omit
ClusterAuthentikConnectionand its cluster-wideSecretRBAC entirely. - Guidance, and ideally a shipped example, for a ValidatingAdmissionPolicy that
constrains
spec.urlandspec.tokenSecretRef.namespaceon cluster connections.