RBAC no ArgoCD: controlando acesso a projetos
Em ambientes com múltiplas equipes usando o mesmo cluster ArgoCD, a pergunta não é se alguém vai acessar um Application que não deveria — é quando. RBAC mal configurado no ArgoCD é um dos caminhos mais diretos para um incidente de segurança em GitOps: basta uma role genérica demais para que um time de desenvolvimento consiga sincronizar ou deletar Applications de produção que não são dele.
Este post explica o modelo de controle de acesso do ArgoCD — policy.csv, roles, grupos e o papel dos AppProjects como fronteira de isolamento — e como combinar essas peças para restringir o que cada equipe pode ver e fazer.
Premissas e limitações
Não há fontes de pesquisa específicas fornecidas para este post — isso é tratado aqui como limitação explícita, não preenchido com suposição. O conteúdo abaixo se baseia no modelo de RBAC documentado oficialmente pelo projeto ArgoCD (https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/) e no comportamento de AppProjects (https://argo-cd.readthedocs.io/en/stable/user-guide/projects/).
Outras premissas:
- Os exemplos assumem ArgoCD na linha 2.x. A sintaxe de RBAC é estável há várias versões, mas vale confirmar na changelog da sua versão antes de aplicar em produção.
- Os comandos e manifestos de YAML/CSV abaixo não foram executados neste ambiente. Trate-os como ponto de partida e valide em um cluster de teste antes de levar a produção.
- Não há relato de incidente real, cliente ou ambiente específico associado a este conteúdo. Os cenários descritos são ilustrativos.
Como o RBAC do ArgoCD funciona
O ArgoCD não usa o RBAC nativo do Kubernetes para controlar quem faz o quê dentro da própria interface/API. Ele tem um sistema próprio, configurado no ConfigMap argocd-rbac-cm, baseado em três conceitos:
- Subjects: usuários locais ou grupos vindos do seu provedor SSO/OIDC (claim
groupsdo token). - Roles: conjuntos nomeados de permissões, definidos em formato CSV dentro do próprio ConfigMap.
- Policies: linhas no formato
p, subject, resource, action, object, effectque concedem ou negam uma ação sobre um recurso.
O ArgoCD já vem com duas roles embutidas: role:admin (acesso total) e role:readonly (leitura em tudo). Para qualquer cenário com mais de uma equipe, essas duas roles não bastam — você precisa de roles customizadas.
A estrutura do policy.csv
O arquivo fica dentro do ConfigMap argocd-rbac-cm, no campo policy.csv. Uma linha de política tem este formato:
p, <subject>, <resource>, <action>, <object>, <effect>
Onde:
resourcepode serapplications,clusters,repositories,certificates,projects,accounts,logs,exec, entre outros.actioné algo comoget,create,update,delete,sync,override,action.objectidentifica o recurso específico, geralmente no formato<projeto>/<application>(aceita wildcard*).effectéallowoudeny.
Exemplo de ConfigMap com duas roles — uma para o time de plataforma (acesso amplo ao projeto infra) e outra para um time de produto (leitura e sync apenas no próprio projeto):
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.default: role:readonly
policy.csv: |
p, role:platform-team, applications, *, infra/*, allow
p, role:platform-team, clusters, get, *, allow
p, role:platform-team, repositories, *, *, allow
p, role:product-team-checkout, applications, get, checkout/*, allow
p, role:product-team-checkout, applications, sync, checkout/*, allow
p, role:product-team-checkout, applications, action/*, checkout/*, allow
g, platform-engineers, role:platform-team
g, checkout-devs, role:product-team-checkout
scopes: '[groups]'
Pontos importantes nesse exemplo:
policy.default: role:readonlydefine o comportamento padrão para qualquer usuário autenticado que não bata em nenhuma regra explícita — evita que alguém sem role definida acabe sem nenhum acesso ou, pior, com acesso total por omissão.- As linhas
g, <grupo>, <role>fazem o mapeamento entre o grupo retornado pelo seu provedor de identidade (claimgroupsdo OIDC) e a role interna do ArgoCD. Isso é o que permite gerenciar acesso via Okta, Azure AD, Keycloak etc. sem tocar no ConfigMap a cada troca de time. scopes: '[groups]'instrui o ArgoCD a usar o claimgroupsdo token OIDC para resolver as roles — sem isso, o mapeamento de grupo não funciona mesmo que as linhasg,estejam corretas.
AppProjects como fronteira de isolamento
RBAC por si só controla quem pode fazer o quê, mas não impede, por exemplo, que um Application do time "checkout" seja criado apontando para um namespace ou cluster de outro time. Essa segunda camada de controle é o AppProject.
Um AppProject define:
- Quais repositórios Git podem ser usados como fonte para Applications desse projeto (
sourceRepos). - Quais clusters e namespaces de destino são permitidos (
destinations). - Quais tipos de recurso Kubernetes podem ser criados ou sincronizados (
clusterResourceWhitelist,namespaceResourceBlacklist).
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: checkout
namespace: argocd
spec:
description: Projeto do time de checkout
sourceRepos:
- "https://github.com/empresa/checkout-*"
destinations:
- namespace: "checkout-*"
server: "https://kubernetes.default.svc"
clusterResourceWhitelist: []
namespaceResourceBlacklist:
- group: ""
kind: ResourceQuota
- group: ""
kind: LimitRange
A combinação correta é: AppProject define onde e com o quê o time pode trabalhar; RBAC define quem dentro da organização tem permissão de operar dentro desse projeto. Usar só RBAC sem AppProject segmentado é como dar a chave certa para a porta errada — a pessoa autorizada acaba conseguindo abrir mais do que devia, porque o escopo (object na policy) aponta para * ou para um projeto amplo demais.
Permissões em nível de Application: ações e recursos
Além de get, sync e delete, o ArgoCD expõe ações mais granulares sobre um Application via action/<nome-da-acao>, que correspondem aos botões de ação customizada na UI (como restart para um Rollout do Argo Rollouts). Também existe o resource applications com ações específicas:
applications, action, <projeto>/<app>— executar ações customizadas de recursos (ex.: resync, restart).applications, override, <projeto>/<app>— sobrescrever parâmetros de sync na UI/CLI.applications, exec, <projeto>/<app>— abrir terminal exec para um Pod viaargocd app exec(quando habilitado) — recurso sensível, trate como acesso a shell em produção.
Vale negar explicitamente exec e override para a maioria dos times, concedendo apenas para operadores de plataforma:
p, role:product-team-checkout, exec, create, checkout/*, deny
Políticas de deny têm precedência sobre allow na avaliação do ArgoCD, então são úteis para fechar brechas sem reescrever toda a role.
Validação
Antes de confiar na configuração, valide com a própria CLI do ArgoCD, que simula a decisão de RBAC para um usuário e uma ação:
argocd account can-i sync applications checkout/checkout-api --as checkout-devs
argocd account can-i delete applications infra/vault --as checkout-devs
O primeiro comando deve retornar yes; o segundo, no, se as policies estiverem corretas. Esse comando (account can-i) é a forma documentada de testar RBAC sem precisar logar como o usuário real — essencial para revisar mudanças no policy.csv antes de aplicar em produção.
Outra verificação útil é revisar os logs do argocd-server ao reproduzir a ação negada: mensagens de permission denied mostram exatamente qual linha de policy (ou ausência dela) bloqueou a chamada, o que ajuda a diferenciar "RBAC bloqueando certo" de "AppProject bloqueando antes do RBAC ser avaliado".
Limitações e cuidados
- RBAC do ArgoCD não substitui RBAC do Kubernetes. Um usuário pode ter
syncliberado no ArgoCD e ainda assim o próprio ServiceAccount do ArgoCD precisa ter permissão no cluster de destino para aplicar os recursos. - Mudanças no
policy.csvvia ConfigMap não passam por audit trail de Git a menos que você gerencie esse ConfigMap via GitOps (o próprio ArgoCD aplicando a si mesmo) — recomendável para rastrear quem alterou o quê. - Grupos OIDC exigem que o claim
groupsseja retornado pelo provedor de identidade; nem todo provedor inclui isso por padrão nas configurações básicas — confirme esse ponto na configuração do seu SSO antes de desenhar as roles em torno de grupos.
Próximo passo
Se você já tem múltiplos AppProjects mas RBAC genérico (role:admin para todo mundo autenticado), comece revisando o policy.default do seu argocd-rbac-cm: trocar para role:readonly como padrão e então liberar ações específicas por grupo é a mudança isolada que reduz mais risco com menor esforço, antes de qualquer refino fino de roles por time.