Git, CI/CD & GitOpsLXXXIII · GitOps RBACRBACModel
The GitOps RBAC model — repository, controller, and cluster as three layers
What you'll learn
- Name the three permission layers in a GitOps delivery path and what each one gates
- Explain why the effective privilege of a change is set by the weakest of the three layers
- Trace a merge through repository, controller, and cluster authorisation to the applied object
- Identify the audit question each layer can answer and the ones it cannot
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Ask an engineer who can deploy to production in a GitOps estate and you will usually get one answer: whoever can merge. That answer is a third of the truth. A merged commit still has to be read by a controller that is scoped to particular sources and destinations, and that controller still has to apply the manifests using a Kubernetes identity with particular verbs on particular resources. Three separate permission systems sit between a keystroke and a running Pod, and they are owned by three different teams.
Three layers, three owners
The delivery path from a pull request to a reconciled object crosses three authorisation boundaries. Nothing in the pipeline collapses them into a single check.
flowchart TB
A["Engineer opens pull request"] --> B["Layer 1: repository merge gate"]
B -->|"merged commit"| C["Layer 2: controller scope gate"]
C -->|"rendered manifests"| D["Layer 3: cluster credential gate"]
D --> E["Applied object in namespace"]
B -.->|"owned by repo admins"| F["Branch protection, CODEOWNERS"]
C -.->|"owned by platform team"| G["AppProject, Kustomization scope"]
D -.->|"owned by cluster admins"| H["ServiceAccount, Role, ClusterRole"]
- Layer 1 — the repository. Decides who can change desired state at all. Enforced by branch protection, required reviews, and code ownership rules on the paths that hold manifests.
- Layer 2 — the controller. Decides which sources may become desired state and which destinations they may target. Enforced by Argo CD AppProjects or by Flux source and Kustomization scoping.
- Layer 3 — the cluster. Decides what the reconciling identity is permitted to create, update, and delete. Enforced by ordinary Kubernetes RBAC against a ServiceAccount.
The layers are ordered but not redundant. Layer 1 knows the human, layer 2 knows the repository and the destination, layer 3 knows the verbs. No single layer knows all three, which is why an audit that reads only one of them produces a confident wrong answer.
What each layer can prove
Each layer produces evidence, and the evidence is not interchangeable:
- The repository proves intent and review: an author, a reviewer, a timestamp, a diff, and a signature if commits are signed.
- The controller proves provenance: which commit produced which rendered manifest, and which destination it was allowed to reach.
- The cluster proves effect: which object changed, under which identity, at which resource version, in the API server audit log.
An incident review needs all three. “Who deployed the bad network policy” is a repository question. “Why was it allowed into the payments namespace” is a controller question. “How did it also delete a ClusterRoleBinding” is a cluster question, and only the third layer can answer it.
Where the model is usually broken
Three failure shapes account for most real findings:
- A single repository behind every environment with one merge gate. Layer 1 collapses to one rule, so production and sandbox share a reviewer set.
- A controller with a wildcard source list. Layer 2 collapses, because any repository becomes a legitimate source of desired state.
- One reconciling ServiceAccount for every tenant. Layer 3 collapses, because every tenant inherits the union of every tenant permission.
Each of these is individually defensible on day one of a platform and individually indefensible by the time a second team is onboarded.
Production discipline
- Name an owner per layer. Repository admins own layer 1, the platform team owns layer 2, cluster admins own layer 3. A layer with no named owner is a layer nobody reviews.
- Review the chain, not the links. A quarterly access review that reads branch protection and stops has audited one third of the delivery path.
- Write down the controller identity. Every GitOps estate should have a one-page answer to “which ServiceAccount applies to which namespace, with which verbs”.
- Assume layer 1 will eventually fail. Tokens leak and reviewers approve tired diffs. The scoped controller and the scoped ServiceAccount are what limit the damage when it does.
Cross-course references
- Kubernetes for Production Sysadmins - Part LVIII (RBAC) and Part LX (ServiceAccounts) cover the primitives that layer 3 is built from.
- Git, CI/CD & GitOps for Infrastructure Engineers - Part XXXII (ProtectedBranches) is layer 1 in detail, and Part LXXVII (ArgoCD) introduced the AppProject that layer 2 is expressed with.
- Terraform for Production Sysadmins - Part XIX (Security) covers the same layering problem where the applying identity is a cloud role rather than a ServiceAccount.
Quiz
Knowledge check · 4 questions
Q1. A platform has strict branch protection, two required reviewers, and signed commits on its GitOps repository. The reconciling controller runs with cluster-admin. What is the effective privilege of anything that reaches the reconciled path?
Q2. The Kubernetes API server will refuse to let the reconciling ServiceAccount create a Role that grants permissions the ServiceAccount does not itself hold.
Q3. Name the three permission layers a GitOps change passes through, and state the single question each one answers.
Q4. Work out which layer failed and what evidence each layer can contribute.
A ClusterRoleBinding granting cluster-admin to a team ServiceAccount appears in a production cluster. The GitOps repository requires two reviewers on every path, and the pull request history shows no such change. The controller reconciles a directory of Kustomizations, one of which references a Kustomize remote base hosted on a public repository. The controller ServiceAccount holds cluster-admin.
Passing score: 75%. Answers are checked in this browser.