Free lesson · GenAI Platform Engineering

Implement ArgoCD RBAC and multi-tenancy

You will configure ArgoCD access control for multiple teams. Define RBAC policies in argocd-rbac-cm ConfigMap: ai-engineers can sync/view applications in ai-* namespaces, platform-engineers have full admin access, viewers can only view application status. Configure SSO integration with Google OAuth for GKE-native authentication. Create ArgoCD Projects to isolate teams: ai-inference-project can only deploy to ai-inference namespace from the ai-inference-* repos, ai-training-project is limited to ai-training namespace. Implement audit logging: enable ArgoCD's audit webhook to log all sync and config change events to Cloud Logging.

Course: DevOps Foundations for GenAI Engineers · Chapter 4 · ArgoCD & GitOps

Free to read — no subscription required.

Introduction

When you run a single ArgoCD installation for an inference team, a training pipelines team, and a platform team, anyone who can log in can sync, delete, or view every Application across every namespace by default — and that's how a contractor ends up reading secrets they shouldn't see, or a junior engineer accidentally deletes a production rollout. By the end of this lesson you will be able to wire SSO-backed roles to scoped Casbin policies and AppProjects so each team can only touch its own namespaces, repositories, and sync actions.

Key Terminology

  • ArgoCD RBAC — Casbin policy layer in argocd-rbac-cm that maps roles to actions (get, sync, update, create, delete) on ArgoCD resources (applications, logs, projects); it's how you stop "any logged-in user can sync anything."
  • AppProject — namespaced CRD that bounds an Application's allowed sourceRepos, destinations (cluster + namespace), and clusterResourceWhitelist; the second wall that blocks a team from targeting a namespace its role would otherwise let it touch.
  • Casbin policy line — a p, <subject>, <resource>, <action>, <object>, allow row where <object> is <project>/<application> — the wildcard you put here is how you scope by team.
  • Default policy — the policy.default value (e.g. role:readonly) that decides what an authenticated user with no group binding can do; the safest setting in a multi-tenant install is role:readonly or empty.
  • Group binding — a g, <group>, role:<name> line that links an SSO group (from Dex/Google/Okta claims) to an ArgoCD role, so membership in ai-inference@company.com automatically grants role:ai-inference.

Concepts

SSO authentication feeding ArgoCD roles

ArgoCD doesn't store users — Dex (bundled) federates Google/Okta/GitHub and emits group claims, and those group claims are the only durable thing your RBAC should bind to. Binding to email addresses is the standard mistake; when someone leaves the team you have to remember to edit argocd-rbac-cm. Bind to the group, manage membership in the IdP, and the ArgoCD config never needs to change.

Loading diagram...

Two walls: RBAC actions vs. AppProject targets

RBAC controls what verbs a role can perform on an Application object (sync, delete, update). AppProject controls what targets an Application is allowed to declare in the first place — its sourceRepos whitelist and its destinations whitelist. A user with applications, create on ai-inference/* still cannot create an Application that points at the platform namespace, because the AppProject the Application belongs to forbids that destination. You need both — RBAC alone leaks across teams once anyone gets create; AppProject alone gives everyone equal verbs (see Code Walkthrough).

Scoping the Casbin object pattern

The 5th field of a policy line (<object>) is <project>/<application>. Use the project name as the discriminator: ai-inference/* lets the role act on every Application inside the ai-inference AppProject. Avoid namespace globs in the object field — Casbin matches the project name, not the Kubernetes namespace.

Cluster-scoped resource whitelist

The clusterResourceWhitelist on an AppProject is the gate for resources that have no namespace — Namespace, ClusterRole, ClusterRoleBinding, CustomResourceDefinition. Leave it empty for application teams. Open it only for the platform AppProject, and only for the kinds that team actually owns. This is what prevents an app team from creating its own ClusterRoleBinding that grants itself cluster-admin.

Code Walkthrough

The snippet below stitches the previous four concepts into one apply: a Casbin policy that binds the ai-inference@company.com SSO group to a sync-level role scoped to the ai-inference AppProject, plus the matching AppProject that fences sourceRepos and destinations. Read it as two halves of one wall.

Code snippetpython
1import json 2import subprocess 3 4RBAC_CSV = """ 5p, role:ai-inference, applications, get, ai-inference/*, allow 6p, role:ai-inference, applications, sync, ai-inference/*, allow 7p, role:ai-inference, applications, update, ai-inference/*, allow 8p, role:ai-inference, logs, get, ai-inference/*, allow 9p, role:platform, applications, *, */*, allow 10p, role:platform, clusters, *, *, allow 11g, ai-inference@company.com, role:ai-inference 12g, platform-eng@company.com, role:platform 13""".strip() 14 15rbac_cm = { 16 "apiVersion": "v1", 17 "kind": "ConfigMap", 18 "metadata": {"name": "argocd-rbac-cm", "namespace": "argocd"}, 19 "data": { 20 "policy.csv": RBAC_CSV, 21 "policy.default": "role:readonly", 22 }, 23} 24 25ai_inference_project = { 26 "apiVersion": "argoproj.io/v1alpha1", 27 "kind": "AppProject", 28 "metadata": {"name": "ai-inference", "namespace": "argocd"}, 29 "spec": { 30 "description": "AI inference team — fenced to its own repos and namespaces", 31 "sourceRepos": ["https://github.com/team/ai-inference-*.git"], 32 "destinations": [ 33 {"server": "https://kubernetes.default.svc", "namespace": "ai-inference"}, 34 {"server": "https://kubernetes.default.svc", "namespace": "ai-inference-staging"}, 35 ], 36 "clusterResourceWhitelist": [], 37 }, 38} 39 40for manifest in (rbac_cm, ai_inference_project): 41 subprocess.run( 42 ["kubectl", "apply", "-f", "-"], 43 input=json.dumps(manifest), 44 text=True, 45 check=True, 46 )
  • RBAC_CSV — each p, line is one allow rule (<role>, <resource>, <action>, <object>, allow); the ai-inference/* object pattern restricts the role to Applications inside the ai-inference AppProject, so this role cannot touch a platform/* Application even if it appears in the UI.
  • g, lines — bind SSO group emails (claims emitted by Dex) to roles. Add a teammate by adding them to the ai-inference@company.com group in your IdP; no ArgoCD config change required.
  • policy.default: role:readonly — authenticated users with no group binding fall through to read-only. Never set this to role:admin on a shared install.
  • sourceRepos — wildcard whitelist of git URLs; an Application pointing at any other repo fails admission with application repo is not permitted.
  • destinations(server, namespace) whitelist; this is the wall that blocks the inference team from declaring an Application targeting the platform namespace.
  • clusterResourceWhitelist: [] — empty list means this team cannot manage any cluster-scoped resource. For the platform AppProject you would add explicit {group, kind} entries for Namespace and ClusterRole.

Done when: kubectl -n argocd get appproject ai-inference -o jsonpath='{.spec.destinations[*].namespace}' prints exactly ai-inference ai-inference-staging, and argocd account can-i sync applications ai-inference/anything --auth-token <ai-inference-user-token> returns yes while the same query against platform/anything returns no.

Do's and Don'ts

Do's

  1. Do bind RBAC to SSO groups, not emails — group membership is managed in the IdP, so people joining or leaving the team never requires a ConfigMap edit.
  2. Do set policy.default: role:readonly — any authenticated user without a group binding gets safe defaults instead of inheriting admin.
  3. Do pair every RBAC role with a matching AppProject — RBAC alone allows a create to point at any namespace; AppProject is what fences the target.

Don'ts

  1. Don't put policy.default: role:admin on a shared install — one misconfigured SSO claim and every user becomes a cluster admin.
  2. Don't open clusterResourceWhitelist for application AppProjects — a team that can create ClusterRoleBinding can grant itself cluster-admin and bypass every other wall.
  3. Don't use email-based subject lines (p, alice@co, ...) — they break on offboarding and don't survive an IdP migration; always go through a role plus a group binding.

This lesson is free to read. Its 3 hands-on labs — real code, in a cloud IDE — are part of the GenAI Platform Engineering subscription.

From · cancel anytime

More free lessons in DevOps Foundations for GenAI Engineers

All free lessons in GenAI Platform Engineering