Learn GitOps with ArgoCD - Security Best Practices
Episode 23 of 36

Learn GitOps with ArgoCD - Security Best Practices

Securing ArgoCD from the outside in: SSO and OIDC authentication, project-based RBAC authorization, API and network security, and even image signature verification and policy enforcement with Kyverno.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 22 we built full observability — and honest observability will reveal something uncomfortable: ArgoCD holds an enormous set of keys. It stores repo credentials, cluster credentials, and can apply any resource to any cluster. A hacked ArgoCD is a hacked cluster. In this episode we discuss security best practices, from authentication to supply chain security, to make sure those keys are only used by those entitled to them.

Why does this matter? ArgoCD is a single point of control — that's exactly its strength, and at the same time its biggest attack surface. Zero trust starts to apply here: give the smallest sufficient access, verify identity via SSO, and don't trust images blindly. Each layer we add in this episode raises the cost of attack, and in security, a high attack cost is the best defense.

Authentication

The first step is answering "who are you?". ArgoCD provides several mechanisms.

Built-in Authentication and SSO

Built-in users (admin and those created via argocd-cm) are fine for a lab, not for a team. For production, connect the company's identity provider:

MethodMechanismSuitable for
Built-inUsername/password in argocd-cmLab, initial bootstrap
Dex (SSO)Connector combination: OIDC, SAML, LDAP, GitHub, GitLabOrganizations with a mixed identity provider
Direct OIDCOkta, Auth0, Google directly as oidc.configTeams already fully on OIDC
SAMLVia the Dex connectorEnterprise companies
LDAPVia the Dex connectorTraditional AD infrastructure

SSO configuration is done in argocd-cm, and because ArgoCD itself is GitOps-managed, argocd-cm should be a manifest in the repo:

ArgoCDargocd-cm - SSO OIDC with Okta
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  url: https://argocd.org.dev
  oidc.config: |
    name: Okta
    issuer: https://org.okta.com
    clientID: <client-id>
    clientSecret: $oidc.okta.clientSecret
    requestedScopes: ["openid", "profile", "email", "groups"]
  accounts.andri: login
  accounts.andri.enabled: "true"

The best practice key here: disable local login once SSO is running. Keep only an emergency admin account, store its password in a vault, and rotate it after use. A still-active local login is a backdoor that doesn't need SSO.

Authorization: RBAC

Authentication answers "who"; authorization answers "may do what". ArgoCD's RBAC model has three layers:

  1. Global policy (argocd-rbac-cm) — maps users/groups to roles.
  2. Built-in rolesrole:admin (full), role:readonly, role:ci.
  3. Project roles — per-project roles with their own source/destination scope (episode 10).
ArgoCDargocd-rbac-cm - RBAC policy
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.csv: |
    p, role:readonly, applications, get, */*, allow
    p, role:readonly, clusters, get, *, allow
    g, andri, role:readonly
    g, dev-platform, role:admin
  policy.default: role:readonly

The important last line: policy.default: role:readonly. A default deny makes every user without a role read-only — destructive changes require an explicit role. This follows the least privilege principle: give minimal access, raise it when needed.

Warning

Be careful with global role:admin. That role can read all secrets and sync anything. For small teams it's practical, but as the organization grows, limit global admin to the platform team, and give developers access through project roles — for example an api-deployer role on the api project that can only sync applications in that project.

Project-Level RBAC

Project roles are the way to hand over part of the control without handing over everything. Create a role in the project:

ArgoCDCreate a project role
argocd proj role add-role api api-deployer
argocd proj role add-policy api api-deployer \
  --action sync --permission allow --object '*'
argocd proj role generate-token api api-deployer

The generated token is a JWT that CI can use for argocd app sync on the api project only — without ever holding admin credentials.

API Security

The ArgoCD API is an attack surface: it accepts credentials, tokens, and commands. Mandatory practices:

  • JWTs with expiration. Project role tokens and argocd tokens must have an exp — avoid eternal tokens. Rotate them when a team member leaves.
  • Service accounts for machines. CI must not log in as admin. Create dedicated CI users, give them a minimal project role (sync only), and use that token in pipelines.
  • TLS required. All access goes through HTTPS. Disable the non-TLS HTTP API server port in production.
  • Rate limiting in front. Put ArgoCD behind a gateway (NGINX Ingress, API gateway) with request limits per IP and per user, to slow down brute force.
CI login with a token
argocd login argocd.org.dev --sso
argocd account generate-token --account ci-bot
argocd app sync api --auth-token <token>

Network Security

TLS and Certificate Management

UI and API access must be encrypted end-to-end. Use an Ingress with certificates from cert-manager, and make sure argocd-server runs behind it with --server.insecure=false (the default). For Git repos:

SSH Keys vs HTTPS Tokens

MethodAdvantagesRisks
SSH key (deploy key)Access to only one repo, cannot commit (read-only when configured)Keys need management and rotation
HTTPS token (PAT)Can be scope-limited and expiringToken can leak through logs if not careful

For private repos, a read-only SSH deploy key is the safest choice for ArgoCD: ArgoCD only needs to read. Store the key as a repository secret in ArgoCD (encrypted), and if you use CI that writes manifests, use a separate PAT with minimal scope and a short lifetime.

Network Policies

ArgoCD must reach Git and clusters, but there's no reason for other pods to reach ArgoCD. Apply a NetworkPolicy that restricts:

KubernetesNetworkPolicy - only ingress to argocd-server
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: argocd-server-allow-ingress
  namespace: argocd
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: argocd-server
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: argocd-server

Supply Chain Security

The last layer: making sure what runs in the cluster is truly what the team intended.

Image Signature Verification

With cosign, images are signed during CI. ArgoCD Image Updater (episode 16) or an admission controller can verify the signature before a pod is allowed to run. An example verification with Kyverno:

Kyverno - require cosign signature
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-signature
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-cosign
      imageVerification:
        authorities:
          - name: org-cosign
            keyless:
              subject: "ci@org.dev"
              issuer: "https://token.actions.githubusercontent.com"
      match:
        resources:
          kinds:
            - Pod

Admission Controllers and Policy Enforcement

OPA/Gatekeeper and Kyverno block manifests that violate policy — for example images without tags, resources without limits, or hostPath. Combine with GitOps: policies run as admission webhooks when ArgoCD applies resources, so bad manifests are rejected from the start.

Vulnerability Scanning

Scanning images in CI (Trivy, from episode 18) is the first defense; further scanning in the cluster with ArgoCD Image Updater + Cosign or a controller that periodically checks the image registry is the second layer. A healthy rule: CI fails on critical CVEs, the admission controller blocks unsigned images, and policy forces every image to have a digest.

Closing

This episode locked down ArgoCD from the outside in: SSO/OIDC/Dex authentication with local login disabled, layered RBAC with default deny and project roles, API security with expiring JWTs and CI service accounts, network security with TLS, read-only deploy keys, and NetworkPolicy, and supply chain security with cosign signatures, Kyverno, and vulnerability scanning.

The points you should take with you:

  • ArgoCD is a big key; narrow who holds it with SSO and least privilege.
  • policy.default: readonly makes destructive changes an explicit choice.
  • CI uses short-lived project role tokens, not admin accounts.
  • Private repos are accessed with read-only deploy keys; write tokens are separate and short-lived.
  • Images must be signed, policy-scanned, and vulnerability-scanned before running.

Good security produces one other equally important asset: evidence. In the next episode 24 we discuss compliance & audit — audit trails, SOC 2, HIPAA, and PCI-DSS requirements, reporting, and policy as code. See you in episode 24!

Learn GitOps with ArgoCD - Security Best Practices | Learn GitOps with ArgoCD