Security¶
Authentication, authorization, encryption, network security, and hardening for ArgoCD deployments.
Authentication¶
ArgoCD supports four authentication mechanisms, configured primarily through the argocd-cm ConfigMap.
Built-in Accounts¶
- Admin account: Created by default with a bcrypt-hashed password stored in the
argocd-secretSecret. Disable after SSO is configured by settingadmin.enabled: falseinargocd-cm. - Local users: Defined in
argocd-cmunderaccounts.<username>. Suitable for service accounts and automation. - API tokens: Generated per user via
argocd account generate-tokenor per project viaargocd proj role create-token. Tokens are JWTs signed by ArgoCD.
SSO via OIDC¶
ArgoCD integrates with external identity providers through native OIDC or via the bundled Dex server.
Native OIDC (recommended for cloud-native IdPs like Okta, Azure AD, Keycloak):
# argocd-cm
data:
url: https://argocd.YOUR_DOMAIN
oidc.config: |
name: Okta
issuer: https://dev-12345.okta.com/oauth2/default
clientID: aaaabbbbccccddd
clientSecret: $oidc.okta.clientSecret
requestedScopes: ["openid", "profile", "email", "groups"]
requestedIDTokenClaims: {"groups": {"essential": true}}
Dex (required for SAML, LDAP, GitHub OAuth, and other non-OIDC connectors):
# argocd-cm
data:
url: https://argocd.YOUR_DOMAIN
dex.config: |
connectors:
- type: github
id: github
name: GitHub
config:
clientID: $dex.github.clientID
clientSecret: $dex.github.clientSecret
orgs:
- name: my-org
Group Claim Mapping
Always configure requestedIDTokenClaims to pull the groups claim. This ensures OIDC groups are available for RBAC policy mapping via g, <oidc-group>, role:<argo-role> entries.
Authorization (RBAC)¶
ArgoCD uses a Casbin-based RBAC engine. Policies are defined in the argocd-rbac-cm ConfigMap.
Policy Format¶
- subject:
role:<name>or a user/group identifier - resource:
applications,projects,repositories,clusters,logs,exec - action:
get,create,update,delete,sync,action/<action-name> - object:
<project>/<app-name>or*/*for all
Built-in Roles¶
| Role | Scope |
|---|---|
role:admin |
Full access to all resources |
role:readonly |
Read access to all resources; no sync or modify |
| Custom roles | Defined in policy.csv with arbitrary permissions |
Project-Level RBAC¶
AppProjects provide namespace-like isolation within ArgoCD. Each project defines:
- Source repos: Which Git repositories apps in this project can pull from
- Destination clusters/namespaces: Where apps can be deployed
- Roles: Fine-grained permissions scoped to the project
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: team-a
namespace: argocd
spec:
sourceRepos:
- "https://github.com/org/team-a-*"
destinations:
- namespace: "team-a-*"
server: "https://kubernetes.default.svc"
roles:
- name: admin
groups:
- org:team-a-admins
policies:
- p, proj:team-a:admin, applications, *, team-a/*, allow
Default Policy¶
Set policy.default in argocd-rbac-cm:
role:readonly-- safest default; unauthenticated users get read-only access""(empty) -- denies all access for unmapped users; recommended for enterprise
Anonymous Access
Setting users.anonymous.enabled: true in argocd-cm grants public read access to all applications. This should be false in production unless the ArgoCD instance is on an internal network.
Encryption and Secret Management¶
At Rest¶
| Asset | Encryption Method |
|---|---|
| Admin password | bcrypt hash in argocd-secret |
| SSO client secrets | Base64-encoded in argocd-secret; use Sealed Secrets or External Secrets Operator for GitOps management |
| Cluster credentials | Base64-encoded in Secret with label argocd.argoproj.io/secret-type: cluster |
| Repository credentials | Base64-encoded SSH keys, HTTPS tokens, or TLS certs in Secrets with label argocd.argoproj.io/secret-type: repository |
| Redis cache | In-memory only; ephemeral; rebuilt on restart |
| Dex storage | ConfigMap-backed or etcd; no persistent database |
In Transit¶
- All inter-component communication uses TLS: server-to-repo-server, server-to-controller, server-to-Redis
- Configure
argocd-serverwith a valid TLS certificate (via Ingress or direct) - The repo server communicates with Git repositories over HTTPS or SSH
Secret Management Integration¶
ArgoCD does not manage secrets natively. Use one of these patterns:
| Approach | Mechanism | Trade-off |
|---|---|---|
| External Secrets Operator (ESO) | ArgoCD deploys ExternalSecret CRDs; ESO syncs from Vault/AWS SM/GCP SM to K8s Secrets |
Best separation of concerns; secrets stay in Vault |
| ArgoCD Vault Plugin (AVP) | Replaces <path:secret#key> placeholders during manifest rendering in repo-server |
True GitOps but requires plugin sidecar |
| SOPS + Kustomize | Encrypt secrets in Git with SOPS; decrypt at render time via ksops plugin |
Everything in Git; key management is critical |
| Sealed Secrets | Encrypt secrets client-side with kubeseal; SealedSecret controller decrypts in-cluster |
Simple; but secrets visible in Git (encrypted) |
Recommended Pattern
Use External Secrets Operator for production. ArgoCD manages the ExternalSecret and SecretStore manifests, while the actual secret values live in an external vault (HashiCorp Vault, AWS Secrets Manager, etc.).
Network Security¶
Network Policies¶
Restrict inter-component and external traffic with Kubernetes NetworkPolicies:
# Restrict ArgoCD server ingress to internal networks
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: argocd-server-ingress
namespace: argocd
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: argocd-server
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- port: 8080
protocol: TCP
Key network segmentation rules:
- Restrict argocd-repo-server egress to only required Git hosts and registries
- Restrict Redis ingress to only ArgoCD components
- Deny argocd-repo-server access to the Kubernetes API server (prevents privilege escalation from compromised repos)
- Use a separate argocd-repo-server deployment for untrusted repositories
Cluster Credential Security¶
- Prefer short-lived ServiceAccount tokens over static bearer tokens
- Use cloud-native auth (AWS IAM Roles, GCP Workload Identity, Azure Managed Identity) for managed clusters
- Store cluster secrets as K8s Secrets with restricted RBAC (only
argocd-application-controllerneeds read access)
Hardening Checklist¶
- Disable admin account after SSO is configured (
admin.enabled: false) - Set
policy.defaultto empty string (deny-by-default) - Enable
server.rbac.log.enforce.enable: trueto enforce RBAC on log access - Use Sealed Secrets or ESO instead of plaintext Secrets in Git
- Run ArgoCD components with
SecurityContext(non-root, read-only root filesystem) - Apply Pod Security Standards (restricted profile) to the
argocdnamespace - Configure NetworkPolicies for all ArgoCD components
- Use
resource.customizations.ignoreDifferencesto prevent reconciliation loops on dynamically-mutated resources (e.g., webhook configurations modified by operators) - Set
repository.credentialsinargocd-cmto use templated credential matching instead of per-repo secrets - Enable audit logging at the Kubernetes API server level for ArgoCD namespace
Known Pitfalls¶
| Pitfall | Impact | Mitigation |
|---|---|---|
Overly broad policy.default: role:readonly |
Any unauthenticated user can read all application manifests (which may contain sensitive config) | Set policy.default: "" and require authentication |
| Static cluster bearer tokens that never expire | Compromised tokens provide persistent cluster access | Use short-lived ServiceAccount tokens or cloud IAM |
Missing ignoreDifferences for operator-managed webhooks |
ArgoCD enters infinite sync loop against operators like cert-manager, gatekeeper | Add resource.customizations.ignoreDifferences in argocd-cm |
| Unrestricted repo-server network access | Compromised Git repo can exfiltrate data or reach internal services | Apply strict egress NetworkPolicies to argocd-repo-server |