The problem GitOps RBAC solves
Teams adopt GitOps and keep the old habits. Deployments come from git, but twelve engineers still hold cluster-admin “just in case”. Someone edits a Deployment by hand during an incident, the controller either reverts it or, worse, has self-heal switched off and the change lingers. The audit trail says one thing, the cluster says another.
The goal of GitOps access control is simple to state: developers can ship to production quickly and safely, and nobody needs routine write access to the cluster to do their job. This page describes how we build it within our GitOps, Helm, ArgoCD and Flux service. It works alongside a good repository structure, because folder boundaries become permission boundaries.
What good looks like
- Production changes arrive only by merged pull request with the right reviewers.
- Each team can deploy to its namespaces and nowhere else, enforced by the controller and not by trust.
- People log in through your identity provider, with groups driving roles.
- Humans have read and diagnostic access, and a break-glass route for real emergencies.
- Every elevated action leaves a record someone can find.
The three layers
| Layer | Question it answers | Mechanism |
|---|---|---|
| Git | Who can propose and approve a change? | Branch protection, CODEOWNERS, required reviews |
| GitOps controller | What may this team’s apps deploy? | ArgoCD AppProjects and RBAC policy, Flux tenant service accounts |
| Kubernetes | What can a person do directly? | Roles, bindings, SSO group mapping, policy engine |
Teams usually configure one layer well and forget the others. A strict ArgoCD project means little if the same engineer can push to the production folder without review, or hold an admin kubeconfig.
How it works in practice
Scoping teams in ArgoCD
An AppProject restricts repositories, destinations and resource kinds. This example gives a payments team a namespace-bound project and blocks cluster-scoped resources:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: payments
namespace: argocd
spec:
sourceRepos:
- https://github.com/example-org/gitops-config.git
destinations:
- namespace: payments-*
server: https://kubernetes.default.svc
clusterResourceWhitelist: []
namespaceResourceBlacklist:
- group: ""
kind: ResourceQuota
roles:
- name: deployer
policies:
- p, proj:payments:deployer, applications, sync, payments/*, allow
groups:
- payments-engineers
The group name comes from your identity provider through OIDC, so joining or leaving the team changes access with no ArgoCD edits. Policy lines in the global argocd-rbac-cm set the default role to read-only, so a new user starts with the least privilege.
Scoping teams in Flux
Flux applies manifests using its own service account unless told otherwise. For multi-tenant clusters we set serviceAccountName on each tenant’s Kustomization so reconciliation runs with only that tenant’s RBAC, and we enable the default service account setting on the controller so a forgotten field cannot fall back to cluster-admin. Each tenant gets a namespace, a role limited to it and a git path owned by its team.
Locking down human access
Kubernetes roles for engineers include get, list and watch on workloads, plus logs and events. Remove pods/exec, pods/portforward where policy demands, and any create, update or delete on workloads. Break-glass is a separate role, granted through your identity provider for a limited time, with API server audit logging switched on so every command is traceable. On AWS this ties to IAM and access entries, covered under EKS consulting, and on any platform to wider Kubernetes consulting work such as network policies.
Decisions and trade-offs
Strict from day one or phased. Removing write access overnight angers people who have no working alternative. We phase it: first deploy through git for everything, then reduce access in non-production, then production once confidence is real.
One ArgoCD per environment or one shared. A shared instance is cheaper and simpler to view; separate instances give a smaller blast radius for the controller itself, which holds powerful credentials. We pick based on your risk appetite and cluster count.
Policy engine or plain RBAC. RBAC cannot say “no privileged containers” or “images only from our registry”. Kyverno or OPA Gatekeeper can. Add one when you have rules RBAC cannot express, not before.
A worked example: a developer’s first week
A new engineer joins the payments team and is added to the payments-engineers group in the identity provider. That one change gives them login to ArgoCD with the deployer role for the payments project, read access to the payments namespaces in Kubernetes, and review rights on the payments folder through CODEOWNERS. They cannot see other teams’ applications in write mode, cannot create namespaces, and cannot exec into a production pod.
On day two they open a pull request changing a replica count. A teammate approves, the merge triggers a sync, and they watch it complete in ArgoCD. When something looks wrong they read logs and events, which they are permitted to do, and raise a fix through git. They never needed a kubeconfig with write rights. That path, from joining a group to shipping safely, is what we aim to make routine.
Auditing who can do what
Access models decay. We leave you a short script or query pattern for reviewing bindings, for example listing subjects bound to powerful roles and checking that group membership still matches the org chart. A quarterly review of those results, plus the break-glass log, keeps the model honest.
Common failure modes
- The controller is the new cluster-admin. Anyone who can edit an Application without a project boundary can deploy anything. Projects and protected branches fix this.
- Everyone is an ArgoCD admin because the default policy was never changed.
- Unprotected production folder. Anyone with push rights bypasses review.
- Break-glass becomes the normal way in, so we report on its use and review each activation.
- Secrets readable by developers through
kubectl get secret. See secret management in git for how to handle that.
Delivery and handover
We deliver branch rules, CODEOWNERS, AppProjects or tenant definitions, SSO group mapping, Kubernetes roles and a one-page access matrix that shows who can do what. The final step is a test: we try forbidden actions as each role and keep the results. Your platform team receives a runbook for onboarding a team, granting break-glass and reviewing its use.
How to tell it is working
Production changes all trace to a pull request. Break-glass activations are rare and reviewed. A new engineer can ship on day one with the right group membership and cannot exec into a production pod. If you want an access review of your current setup, book a consultation.