Hardening Argo CD RBAC defaults and anonymous access

Argo CD's default RBAC settings can grant more access than teams expect. This page shows how to close the gaps in policy.default, anonymous access and local user naming, and how to validate the result.

On this page

Why Argo CD RBAC defaults need hardening before go-live

Argo CD RBAC defaults widen access in two places: the policy.default role that every authenticated user inherits, and the anonymous access switch users.anonymous.enabled, which gives unauthenticated visitors the same permissions as policy.default. To narrow them, set policy.default to a role with no write rights (or to nothing at all), keep anonymous access off, and grant every other permission explicitly through named roles or groups. Teams often inherit these defaults from a quick start guide or a proof of concept, where a single administrator and an open test instance were acceptable, and then carry them into production unchanged.

Argo CD RBAC is the policy layer that decides who can view, sync, override, delete or administer applications, projects, clusters and repositories. It is defined in the argocd-rbac-cm ConfigMap as policy lines and scopes, and it is applied to users from local accounts and to groups from the SSO provider. The hardening work matters most when you enable SSO for the first time, when several teams share one instance, or when change control requires that every deployment permission can be traced to an approved group. A default that was harmless for one administrator becomes an exposure once a contractor, an auditor or a service account can authenticate.

This page walks through the gaps that remain after a standard install: why policy.default cannot be narrowed by deny rules, which minimal default roles to use, the risks of anonymous access, how local users that share a name with an SSO group can pick up roles they should not have, and how to validate the policy before it goes live. Before you start, confirm which SSO connector or OIDC provider you will use, list the groups that will map to roles, and decide who will own the policy file, because each change to it affects every user on the instance.

Which RBAC and access settings to set and when each fits

This table compares the settings that decide what unauthenticated users, local accounts, SSO users and the built-in admin can do by default. Read each row as a control with a safer value for most organizations and the narrow case that justifies changing it. Confirm each setting against the RBAC and user management documentation for your Argo CD version before you change it.

SettingWhat it controlsSafer setting for most organizationsWhen a different value is justified
policy.defaultThe role applied to any authenticated user who matches no explicit policy. Deny rules cannot narrow it, so it sets the floor for every SSO user.Empty, or a role with read-only access to nothing sensitiveA named read-only role for a team that must see application status without any write rights
users.anonymous.enabledWhether unauthenticated requests are accepted, with the same permissions as policy.default.falseA status-only dashboard on a segmented network where policy.default grants nothing beyond read access to non-sensitive applications
admin.enabledWhether the built-in admin account can log in with a password.false once SSO or local users work and a break-glass path is documentedInitial setup, or a short window while SSO is being verified
Local usersAccounts defined in the Argo CD configuration with their own policy lines, such as role assignments and API key capability.Few or none, each with a named owner and an expiry reviewAutomation accounts that need API tokens and cannot use SSO
SSO scopes and groupsRoles granted to users from the identity provider through group or claim mappings.Map each group to one role per project, using the federated claim your Dex or OIDC configuration exposesOrganization-wide admin or read roles for a small platform team, after review
Local user policy versus roleWhether permissions are attached directly to a local user or through a role that an SSO scope could share a name with.Attach policies directly to local users so a same-named SSO scope cannot inherit the roleUsing a shared role only when the team has confirmed no SSO group shares its name
Explicit project or global policiesSpecific allow rules for a project or for the whole instance, which apply on top of policy.default.Project-scoped rules that grant the minimum actions on named applicationsGlobal rules for platform roles that must act across all projects, reviewed as privileged access

What anonymous access exposes and how to decide whether to allow it

Enabling anonymous access does not create a separate permission model. Unauthenticated visitors inherit whatever policy.default grants, so the default role and the reachability of the instance decide the exposure. The cards below cover what to check before allowing it and how to prove the result from outside the cluster.

Read access to applications and projects

With users.anonymous.enabled set to true, an unauthenticated client gets the same permissions as policy.default. If that default role can read applications or projects, anyone who reaches the API can list deployments, their names, their target namespaces and their sync state. Check the policy.default value in argocd-rbac-cm and confirm whether it grants any get or list permission on applications and projects. Treat an empty default as the safe baseline and verify it rather than assume it.

Exposure of cluster and application metadata

Application listings reveal far more than names. Repository URLs, destination cluster addresses, namespaces, image tags and resource trees can help an attacker map your delivery pipeline and pick targets. Even when secrets are redacted, this metadata narrows the search for a later credential or misconfiguration attack. Decide which of these fields you would be comfortable publishing, and confirm what an anonymous session can actually see in the UI and the API.

Interaction with a permissive default

Anonymous access is only as narrow as the default role it inherits. A policy.default that grants sync, override or action permissions turns an unauthenticated session into a deployment actor. Because policy.default also applies to authenticated SSO users without a matching group, a loose default can widen access for every account, not only anonymous ones. Review the default role together with the anonymous setting, since changing one without the other leaves a gap.

Public reachability of the UI and API

Anonymous access is a risk only if the Argo CD API server and UI can be reached by clients who should not see the deployments. Ingress rules, load balancer settings, VPN paths and network policies determine that reach. Enabling anonymous access on an instance that is reachable from the internet extends the exposure to anyone on the internet. Map which networks can open the login page and API endpoint, then decide whether anonymous access is justified for those networks at all.

Confirming the exposure in your install

Manifests, Helm values and earlier changes can leave the anonymous setting in a state nobody remembers. Inspect the live argocd-cm ConfigMap for users.anonymous.enabled and argocd-rbac-cm for policy.default, then run an unauthenticated request against the API and UI from a client outside the cluster. Record the result, the Argo CD version and the date. Repeat the test after every upgrade or RBAC change, because the default role is applied to new requests immediately.

Deciding whether to allow anonymous access

Most organizations that run Argo CD for internal delivery have no need for anonymous users, so the default answer is to leave the setting off and require SSO or local accounts. Allow it only for a documented reason, such as a read-only status page on a segmented network, and pair it with a default role that grants nothing beyond that read. Ask your team to name the owner of that decision and the test that proves the result on each change.

Why local users with SSO group names can inherit the wrong roles

The RBAC guide advises that you assign policies directly to local users rather than to roles, because Argo CD matches SSO scopes against role names. If a role in policy.csv is called platform-admins and an identity provider group with the same name is mapped into the token scopes, a local user who is not in that group can still be granted the role through the name match. Argo CD resolves local user grants and SSO group grants through the same policy table, so a shared name is enough to widen access. Direct grants such as p, local-user, applications, get, */*, allow keep the local account's permissions tied to that account alone.

Collisions usually happen by accident. A team creates a group called sre in the identity provider for a different purpose, reuses a role name that already exists in the RBAC config, or copies a policy file from another installation where a local account carried the same name as a group. Nothing in the Argo CD UI flags this, so the risk shows up only when someone reviews the file. Audit it by listing every local account in argocd-cm under accounts.*, listing every role and group subject in argocd-rbac-cm, and checking the two lists for identical strings. Ask your identity team for the group names in the provider that map to Argo CD, and compare them with the same list. Confirm the exact claim and scope mapping in your version against the current RBAC configuration guide before you rely on the result.

The cleanest outcome is to remove local accounts that are not needed. Once SSO works for every human user, keep only the break-glass path described in the admin disablement guidance, which covers how to plan a recovery account and when to set admin.enabled to false. Automation accounts that need a token should be reviewed separately, since they often carry the broadest grants. Record which accounts remain, who owns each one, and why it exists, and rerun the name comparison after every group change in the identity provider.

How to tighten Argo CD RBAC defaults step by step

This sequence takes an Argo CD instance from its permissive starting point to a state where unauthenticated users, local accounts and SSO groups each get only the access they need. Each step ends with a check you can record as evidence.

  1. Export the current RBAC and login settings

    The platform team exports the argocd-rbac-cm and argocd-cm ConfigMaps from the cluster that runs Argo CD and stores them in version control before any change. Record the current values of policy.default, policy.csv, users.anonymous.enabled, admin.enabled and any accounts.* keys, along with the Dex or OIDC settings. This export is the baseline you compare against and the rollback point if a change blocks access. The step is done when the exported files are committed and a second person has reviewed them.

  2. Inventory accounts, groups and existing grants

    List every local account defined under accounts.* in argocd-cm, every SSO group referenced in policy.csv, and every role that a local user or group inherits. Compare these names against the groups your identity provider actually issues, because a local user and an SSO scope sharing a name can receive the same roles. Note which accounts are used by automation, such as CI pipelines that hold API tokens. The step is done when you have a written table of accounts, groups, roles and owners that matches the live configuration.

  3. Set policy.default to a least-privilege role

    Set policy.default in argocd-rbac-cm to a role that grants only read access to the objects a signed-in user needs to see, or to no role at all. Remember that policy.default applies to every authenticated user who has no other matching grant, and that deny rules cannot narrow it, so the default must be correct on its own. Grant elevated rights through explicit role bindings for named groups. The step is done when a signed-in user with no group membership cannot sync, delete or change any application, which you confirm by logging in as a test user from a group with no bindings.

  4. Disable anonymous access if it is not required

    Check whether users.anonymous.enabled is set in argocd-cm. When it is true, unauthenticated requests receive the same permissions as policy.default, so a permissive default turns into public access. If no team needs anonymous reads, set the key to false and apply the change. Confirm the result with a request that carries no token, which should be refused, and with the same request on a dashboard or CLI session that is signed in. If a dashboard or badge depends on anonymous access, record that dependency and decide whether to grant a narrow default instead.

  5. Move local user grants to direct user policies

    For each local account, replace role names in policy.csv with direct policy lines that name the user, such as p, username, applications, get, project/*, allow, so that the local account is not granted through a role that an SSO group could also reach. Keep the grant set identical to the inventory from the second step. Apply the change and check that each local account still performs its intended actions and nothing more. The step is done when every local account has direct grants and no role in policy.csv is bound to a local user.

  6. Remove role names that collide with SSO scopes

    Compare each role name in policy.csv with the group names your identity provider sends, and rename or remove any role whose name matches an SSO scope or group. This prevents a group from inheriting a role meant for a different audience, and it prevents a local user from gaining the role by name. Update any group bindings to point at the renamed roles and keep the mapping in the exported file. The step is done when the role list contains no name that matches a token claim or group value from your identity provider, which you verify with a test login by a member of each mapped group.

  7. Disable the admin account and validate the policy

    Once SSO or local users work, set admin.enabled to false, as the user management documentation recommends, and keep a documented break-glass path before you do so. Then validate the whole policy: sign in as a sample user from each group, test an unauthenticated request, and test a group member's permitted and forbidden actions with the Argo CD CLI. Record the results in a table with the expected and observed outcome for each test. The sequence is complete when every test matches its expected outcome and the export from the first step is replaced by the new baseline.

How to validate RBAC policies before and after changes

Validate RBAC by writing down what each persona should be allowed and denied to do, then proving it against a running instance. Build a matrix with one row per persona: anonymous access, each local user, each SSO group that maps to a role, and each automation account such as a CI token or sync bot. For every row, list the expected outcome for the actions that matter most: viewing applications, syncing, deleting, reading logs, and changing repositories or clusters. Include the cases that prove a gap is closed, such as confirming that an unauthenticated request receives no more than policy.default allows when users.anonymous.enabled is off. Treat any mismatch between the matrix and observed behavior as a failed change.

Run the matrix with the argocd CLI or the API against a non-production instance that uses the same SSO connector, group claims and policy files as production. Confirm the exact commands, flags and permission checks in the RBAC guide before scripting them, because the syntax for testing a policy as a given user or group is the part most likely to differ between releases. Use a dedicated test account for each persona rather than an administrator token, so the test proves what the persona can do and not what an admin can do. Record the Argo CD version, the Dex or OIDC configuration and the policy commit alongside each run so results can be compared later.

Store the test cases in the same Git repository or review path as the policy itself, and require review for any RBAC change, including changes to policy.default, policy.csv and the SSO group mappings. Re-run the full matrix after every Argo CD upgrade, patch release and identity provider change, since default behavior and claim handling have changed between major versions in the past. Ask your team to confirm who owns the matrix, how often it runs in the pipeline, and what happens when a test fails during an upgrade window. Keep the failing run as evidence of the control operating, not only the passing ones.

Operational risks to monitor after RBAC is hardened

Hardening the RBAC defaults is a point-in-time change. The risks that follow come from drift, new identities joining through SSO, roles that grow broader over time, and upgrades that change how permissions behave. Each card names one thing to check on a regular cadence.

Policy drift outside Git

Someone with cluster access can edit the argocd-rbac-cm ConfigMap directly, and the change takes effect without a review. Treat the RBAC policy as configuration managed in Git, and compare the live ConfigMap with the committed version on a schedule. Any difference, including a restored policy.default or a re-enabled users.anonymous.enabled, should raise an alert.

New SSO groups added without review

A new group created in the identity provider can be mapped to an Argo CD role as soon as someone adds a mapping line. Require every group-to-role mapping to go through the same review as a policy change. Periodically list the groups that hold roles and confirm that each one still has an owner and a current need.

Service accounts with broad roles

Automation accounts often receive wide permissions because a pipeline needed them once and nobody narrowed them afterward. Inventory every account that holds a token or a role, and check whether it can still sync only the applications it needs. Remove unused accounts, and rotate tokens for the ones you keep.

Audit evidence for policy changes

A policy change is only useful to an auditor if you can show who made it, when, and why. Keep the Git history of the RBAC file as the record of intent, and retain the Kubernetes events that show when the ConfigMap changed. Ask your team to confirm which tool stores these events and for how long before you rely on them as evidence.

Retesting after RBAC changes in upgrades

The 2.14 to 3.0 upgrade notes say that update and delete permissions now apply only to the application itself, not to its child resources. The same notes say that viewing logs needs an explicit logs, get permission. A policy that worked before the upgrade can allow less or more afterward, so rerun your access checks on a test instance before every minor upgrade.

Validating policies after every edit

A small typo in a policy line can silently grant or remove access. Keep a list of users and groups with the access each should have, and run it against the policy before merging. Confirm the result with the Argo CD RBAC validation tooling in your test environment, and record the output with the change.

Questions teams ask before changing Argo CD RBAC

Can policy.default be narrowed with deny rules in Argo CD?

No. Argo CD RBAC works additively: a policy grants permissions, and there is no deny statement that removes a permission already given by policy.default. Whatever policy.default grants is available to every authenticated user, including people who sign in through SSO and hold no other role. The practical fix is to lower what policy.default grants, often to read-only or to nothing, and then assign each wider permission explicitly to a named role, group or local user. Before you change it, export the current policy from the argocd-rbac-cm ConfigMap and list which users depend on the existing default, so that tightening it does not break a pipeline or a team's daily access. Confirm the exact behavior against the RBAC configuration reference for your Argo CD version before you rely on it.

Is anonymous access to Argo CD ever needed?

Rarely, and most production installations should leave it off. When users.anonymous.enabled is set, unauthenticated visitors receive the same permissions as policy.default, so anything you grant to the default role is visible to anyone who can reach the login page. Teams sometimes enable it to let a dashboard or status page load without sign-in, but the safer pattern is to keep it disabled, give read access to the default role only where you have decided that users outside your identity provider may see application state, and expose status through a separate monitoring tool. If you do need it, document who can reach the endpoint through your network controls and review the default role together with the anonymous setting, since they change together.

How long does an Argo CD RBAC hardening project take, and what drives it?

The duration depends mainly on how many teams, SSO groups and Argo CD projects the policy must cover, and on how many of those groups have no clear owner. Each team needs a mapping from its identity provider group to an Argo CD role, a set of project permissions, and a test that confirms what it can and cannot do. Projects with many source repositories and destination clusters take longer to review than single-cluster setups. Local users and the built-in admin account add steps, because each must be inventoried and either moved to SSO or disabled. Ask any provider for a schedule that is tied to a written scope, listing the number of teams, groups and projects in that scope, rather than a generic estimate.

What cost components apply when a partner is engaged for RBAC hardening?

Argo CD itself is open source under the Apache License 2.0 with no paid edition from the project, so the costs are labor and the infrastructure you already run. A partner engagement is typically priced on the work in the written scope: discovery of current policies and groups, design of roles and project boundaries, changes to the configuration, validation in a non-production environment, and handover documentation. Some costs recur, such as periodic policy reviews after new teams join, and some do not, such as the initial design. A quote should list each deliverable, the environments touched, the identity provider and Argo CD versions in scope, the test cases used for validation, who performs changes in production, and what is excluded. DeployOpen does not publish a price list, so the quote follows from that written scope.

What must our team provide before RBAC work starts?

The team needs to provide three things. First, access to the identity provider, either through an administrator who can read group membership or through an export of the groups and their members, so that the mapping is built on real data. Second, a group inventory: which SSO groups exist, who owns each, which Argo CD projects and applications they use, and which groups are stale. Third, test accounts for each role in a non-production Argo CD instance, so every policy change can be checked by signing in as that role. You should also provide the current argocd-rbac-cm and argocd-cm configuration, a list of local users, and a named person who can approve changes. Without these inputs, the design will rely on assumptions that later fail during review.

Can an internal platform team harden Argo CD RBAC on its own?

Yes, a capable internal team can do it, and many do. The work is mostly careful configuration and verification rather than new engineering. The parts that most often need outside help are a second pair of eyes on the role model, a repeatable test suite that proves each role's permissions, and time away from daily delivery work. Key points to check are whether the team has someone who understands Argo CD RBAC syntax, whether local users and the admin account are inventoried, and whether changes can be rolled out and reverted safely. If your team has these covered, the work can be done in-house. If not, a fixed-scope engagement can supply the design and tests while your team keeps ownership of the result.

Why should local users not share names with SSO groups?

Argo CD matches policy subjects by name, so a local user called platform-admins can receive roles intended for an SSO group with the same name. The RBAC guide advises assigning policies directly to local users instead of roles for this reason, which keeps the subject explicit and prevents a shared name from carrying privileges across identity sources. Before you change anything, list every local user and every SSO group and look for overlaps. Rename or remove conflicting entries, disable local users you no longer need, and then add a validation step to your change process that flags any new name that appears in both places. Confirm the exact matching behavior against the RBAC configuration reference for your version.

How DeployOpen scopes and delivers Argo CD RBAC hardening

DeployOpen approaches Hardening Argo CD RBAC defaults and anonymous access as a fixed-scope piece of work on a platform you run in your own infrastructure. We start from your current policy, identity setup and access model, then agree in writing which RBAC, anonymous access and local user changes are in scope before any work begins.

How we scope

  • We review your current policy.csv, policy.default setting and users.anonymous.enabled state before proposing changes.
  • The written scope names which roles, SSO groups and local users are covered and which are excluded.
  • We agree the validation approach and the evidence you will receive before work starts, with a quote following from that scope.

How we build

  • We narrow policy.default to a minimal read-only or no-access role, since deny rules cannot restrict it.
  • We move local user permissions onto direct user policies so SSO group names cannot inherit the same roles.
  • We test each policy change with the Argo CD RBAC validation tooling against expected allow and deny outcomes.

How we hand over

  • You receive the final policy files, a list of changed defaults and the reasoning behind each decision.
  • We document the anonymous access state, the local user inventory and the steps to confirm them after future changes.
  • A walkthrough covers how to add a team role or SSO group without reintroducing shared names or broad defaults.

How we support

  • Ongoing support, upgrades and continuous testing are available as a retainer after the fixed-scope work ends.
  • A retainer can cover re-running RBAC validation when Argo CD minor versions change, within the agreed scope.
  • Ask us to confirm the support boundaries, response arrangements and exclusions in the written quote.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.