In Argo CD, how is the RBAC policy in the argocd-rbac-cm ConfigMap written, and how does a user who arrives through SSO in a group get permission to sync one project's applications?
answer
- one ConfigMap, one CSV
- p grants, g maps
- the object carries the project name
- claims must actually arrive to match
- explicit deny beats allow
basics
~20 sArgo CD RBAC is a CSV in the argocd-rbac-cm ConfigMap. Permission lines start with p and grant a subject an action on a resource object; lines starting with g map an SSO group or user to a role. Objects for applications are written project/application.
solid answer
~40 sThe policy lives in the `policy.csv` key of the `argocd-rbac-cm` ConfigMap. Two line types matter. A permission line is `p, <subject>, <resource>, <action>, <object>, allow|deny` — for example `p, role:team-a-dev, applications, sync, team-a/*, allow`, where the resource is one of Argo CD's own resources such as `applications`, `projects`, `clusters`, `repositories` or `logs`, and the object for an application is `<project>/<application>` so permissions are naturally scoped per project. A group line is `g, <subject>, <role>`, which is how an SSO group becomes a role: `g, acme:team-a, role:team-a-dev`. For the group name to be visible at all, the `scopes` key must include the claim carrying it, typically `'[groups, email]'`. `policy.default` names the fallback role when nothing matches. Deny wins over allow, and the built-in `role:admin` and local `admin` account sit above all of it.
code
yaml · 14 linesapiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.default: ''
scopes: '[groups, email]'
policy.csv: |
p, role:team-a-dev, applications, get, team-a/*, allow
p, role:team-a-dev, applications, sync, team-a/*, allow
p, role:team-a-dev, logs, get, team-a/*, allow
p, role:team-a-dev, applications, delete, team-a/prod-web, deny
g, acme:team-a, role:team-a-devgo deeper
Know that Argo CD has its own permission model separate from the cluster's, and that it is a text policy stored in a ConfigMap rather than Kubernetes Role objects.
Be able to write a permission line from memory — subject, resource, action, object, effect — and explain that the application object is project/application, which ties permissions to projects.
Show operational judgment: deny-by-default via an empty policy.default, disabling the local admin account once SSO works, and debugging a mapping failure by inspecting the token's claims rather than editing the CSV.
Own the identity design across teams: which groups exist in the provider, whether roles are global or delegated to project roles, and how policy changes are themselves reviewed and version-controlled rather than hand-edited in a ConfigMap.
## Where the policy lives Argo CD's own authorization — who may see, edit or sync which Application — is separate from the cluster's authorization. It is configured in a single ConfigMap in the Argo CD namespace, `argocd-rbac-cm`, with three keys that matter: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: argocd-rbac-cm namespace: argocd data: policy.default: '' scopes: '[groups, email]' policy.csv: | p, role:team-a-dev, applications, get, team-a/*, allow p, role:team-a-dev, applications, sync, team-a/*, allow p, role:team-a-dev, logs, get, team-a/*, allow p, role:team-a-dev, applications, delete, team-a/*, deny g, acme:team-a, role:team-a-dev ``` ## Reading a permission line A `p` line has six fields: the literal `p`, the **subject**, the **resource**, the **action**, the **object**, and the **effect**. - **Subject** is a role (`role:something`), a group name as it arrives from the identity provider, a local account name, or a project role (`proj:<project>:<role>`). - **Resource** is one of Argo CD's API resources — `applications`, `applicationsets`, `projects`, `clusters`, `repositories`, `accounts`, `certificates`, `gpgkeys`, `logs`, `exec`, `extensions`. These are Argo CD's objects, not Kubernetes kinds; nothing here grants access to Pods or Secrets in a workload cluster. - **Action** is `get`, `create`, `update`, `delete`, `sync`, `override`, or `action/<group/kind/action-name>` for a resource action such as restarting a workload from the UI. - **Object** is the thing acted on. For `applications` it is `<project>/<application>`, which is exactly why the AppProject is the unit of tenancy: `team-a/*` means every Application in the `team-a` project and nothing else. Wildcards are permitted on either half. - **Effect** is `allow` or `deny`, and an explicit `deny` beats any matching `allow`. That makes it possible to grant broad rights and carve out an exception, for example allowing sync across a project while denying `delete` on the production application. ## Reading a group line A `g` line binds a subject to a role: `g, acme:team-a, role:team-a-dev`. The left side may be an SSO group, an SSO username, or a local account; the right side is a role, and roles may be chained so one role inherits another. This is the join between your identity provider and Argo CD. When a user logs in through OIDC — either an external provider configured in `argocd-cm` under `oidc.config`, or the bundled Dex under `dex.config` for providers such as SAML or GitHub — the ID token carries claims. Argo CD reads the claims named in the `scopes` key of `argocd-rbac-cm`, defaulting to the `groups` claim, and treats each value as a possible subject. If the provider does not emit group memberships in the token, no `g` line will ever match and the user drops to `policy.default`, which is the single most common "my SSO user sees no applications" cause. The fix is on the provider side — request and emit the group claim — not in the CSV. ## The fallback and the escape hatches `policy.default` names the role applied when no line matches. Leaving it empty denies by default, which is what you want for a shared instance; setting it to `role:readonly` gives every authenticated user a view of everything, which is a deliberate and sometimes unwanted choice. Two things sit outside the CSV. The built-in `role:admin` grants everything, and the local `admin` account is a superuser that bypasses policy entirely — hardened installs set `admin.enabled: "false"` in `argocd-cm` once SSO works, so that every action is attributable to a real identity. Local accounts other than `admin` are declared in `argocd-cm` as `accounts.<name>` and then granted rights by ordinary `p` and `g` lines. ## Project-scoped delegation There is a second place to put policy: an AppProject's `spec.roles`. Those roles are defined by the project itself, their subject is `proj:<project>:<role>`, and their policies may only reference objects inside that project. That lets a platform team hand a tenant the ability to define its own roles without letting it grant rights over anything else — and it is the mechanism behind project-scoped API tokens, created with `argocd proj role create-token`, that a CI job can use to trigger a sync of one project's applications. ## The shape of a good answer Name the ConfigMap, show the six-field `p` line, point out that the application object is `project/application`, explain that a `g` line is what makes an SSO group mean something, and mention that deny wins and that the local `admin` account sidesteps the whole file.
- An Argo CD user logs in through SSO successfully but sees no applications at all. Where do you look first?At the claims, not the CSV. Decode the ID token and check that the group claim is actually present and spelled the way your `g` lines expect, and that `scopes` in `argocd-rbac-cm` includes it. If no group arrives, no `g` line matches and the user falls to `policy.default`, which denies when empty. Provider-side group emission is the usual fix.
- What is the difference between a role defined in argocd-rbac-cm and a role defined in an AppProject's spec.roles?A ConfigMap role is global: the platform team writes it and it can reference any project. A project role has the subject `proj:<project>:<role>` and its policies may only cover objects in that project, so it is safe to let the tenant own. Project roles also back project-scoped JWT tokens issued with `argocd proj role create-token`, which is the usual way CI syncs one project's applications.
saying these in an interview costs you the question
- Thinks Argo CD RBAC is Kubernetes RBAC under a different name
- Believes an allow line can override a matching deny line
- Assumes SSO group claims appear without configuring the provider
- Grants role:admin because a per-project rule seemed hard
- Leaves the local admin account enabled after SSO is working