A Kubernetes operator for multi-cluster authentication
- Golang
- Kubebuilder
- OCM
- Docker
The problem
Kubernetes RBAC is per cluster. That's the right design for a cluster and the wrong experience for a fleet: granting one engineer read access to twelve clusters means twelve role bindings, twelve identities and twelve kubeconfigs, all drifting independently. Revoking that access later means remembering all twelve.
What's missing is a place to say "this person has this level of access to these clusters" once, and have the clusters converge on it.
What I built
A Kubernetes operator, written in Go with Kubebuilder, that runs on a hub cluster and treats access as a declarative resource. You express the intent on the hub; the controller reconciles it into real identities and role bindings on each managed cluster, and produces a kubeconfig the user can actually use.
It builds on Open Cluster Management rather than reinventing the fleet layer: OCM already provides the cluster inventory, the agent on each managed cluster, and the primitives for projecting identity and reaching clusters that have no inbound route.
How the pieces fit
Intent lives on the hub
Custom resources on the hub describe who gets what, across which clusters. That makes access reviewable the same way any other Kubernetes manifest is reviewable, and it makes revocation a delete rather than an audit.
Controllers do the converging
The reconcile loop is the whole point. A cluster that was offline when permission was granted picks the change up when it comes back; a role binding someone deleted by hand comes back on the next pass. This is the property that hand-run scripts can't give you, and it's the reason this belongs in an operator rather than in CI.
Credentials stay scoped
The generated kubeconfig is per user and per cluster, so there's no shared admin credential being passed around, and no long-lived secret that outlives the person it was issued to.
What working on it taught me
- Writing an operator is mostly deciding what the desired state is. Once the CRDs are honest about that, the controller is straightforward.
- Reconciliation needs to be idempotent and cheap, because it will run far more often than you expect: on resyncs, on restarts, and on every unrelated change.
- Depending on upstream projects means reading their code. Several of my contributions to OCM components came directly out of building on them.