Skip to main content
ApplicationSet is a powerful tool. It is crucial to understand its security implications before using it.

Only Admins May Create ApplicationSets

ApplicationSets are privileged resources that should only be managed by cluster administrators.

Why Admin-Only Access?

ApplicationSets have significant privileges:
  1. Arbitrary Project Access
    • ApplicationSets can create Applications under any Project
    • Many Argo CD setups include Projects (like default) with high permissions
    • These Projects often have permissions to manage Argo CD’s own resources (RBAC ConfigMap, Secrets, etc.)
  2. Rapid Resource Creation/Deletion
    • ApplicationSets can quickly create an arbitrary number of Applications
    • They can delete them just as quickly
    • This could overwhelm clusters or disrupt services
  3. Secret Access
    • Some generators (like Git generator) can read Secrets in the Argo CD namespace
    • These Secrets could be sent to arbitrary URLs as auth headers
    • Example: Using the api field to send credentials to attacker-controlled endpoints
Recommendation: Use Kubernetes RBAC to restrict ApplicationSet create/update/delete permissions to admin users only.

RBAC Configuration Example

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: applicationset-admin
  namespace: argocd
rules:
- apiGroups:
  - argoproj.io
  resources:
  - applicationsets
  verbs:
  - create
  - update
  - delete
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: applicationset-admin-binding
  namespace: argocd
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: applicationset-admin
subjects:
- kind: User
  name: [email protected]
  apiGroup: rbac.authorization.k8s.io

Control ApplicationSet Sources of Truth

Even if non-admins cannot create ApplicationSet resources, they may still affect ApplicationSet behavior through generator sources.

Git Generator Risks

If an ApplicationSet uses a Git generator, anyone with push access to the source repository can affect the ApplicationSet’s behavior.

Potential Attacks

Excessive Application Creation:
# Malicious user adds many directories or config files
apps/
├── app1/
├── app2/
├── app3/
# ... hundreds more directories
├── app999/
This could:
  • Strain the ApplicationSet controller
  • Strain the Application controller
  • Trigger SCM provider rate limiting
  • Degrade service for legitimate Applications
Resource Exhaustion:
  • Creating Applications that consume excessive cluster resources
  • Deploying resource-intensive workloads to multiple clusters simultaneously

Mitigation Strategies

  • Limit push access to the source Git repository
  • Require pull request reviews for changes
  • Use branch protection rules
  • Implement approval workflows
Set limits in Argo CD AppProjects:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-project
spec:
  resourceQuotas:
  - applications: 50
  - cpu: 100
  - memory: 200Gi
  • Set up alerts for unusual Application creation patterns
  • Monitor for rapid increases in Application count
  • Track resource consumption by Applications
Use CI/CD to validate config files before merge:
# .github/workflows/validate.yml
name: Validate Config
on: [pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Validate JSON
      run: |
        for file in apps/**/config.json; do
          jq empty "$file" || exit 1
        done
    - name: Check limits
      run: |
        count=$(find apps -name config.json | wc -l)
        if [ $count -gt 50 ]; then
          echo "Too many applications: $count"
          exit 1
        fi

Templated Project Field

The project field deserves special attention when templated.

The Risk

If the project field is templated, a malicious user with write access to the generator’s source could create Applications under Projects with insufficient restrictions.
Example vulnerable ApplicationSet:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: team-apps
spec:
  goTemplate: true
  generators:
  - git:
      repoURL: https://github.com/org/app-configs.git
      revision: main
      files:
      - path: "apps/**/config.json"
  template:
    metadata:
      name: '{{.appName}}'
    spec:
      project: '{{.projectName}}' # DANGEROUS: templated project
      source:
        repoURL: '{{.source}}'
        targetRevision: '{{.revision}}'
        path: '{{.path}}'
      destination:
        server: '{{.server}}'
        namespace: '{{.namespace}}'
Malicious config.json:
{
  "appName": "malicious-app",
  "projectName": "default",
  "source": "https://github.com/attacker/malicious-manifests",
  "revision": "main",
  "path": "k8s",
  "server": "https://kubernetes.default.svc",
  "namespace": "argocd"
}
This could:
  • Create an Application in the default Project (which may have admin permissions)
  • Deploy to the argocd namespace
  • Modify Argo CD’s RBAC ConfigMap
  • Escalate privileges

Secure Pattern

Safe ApplicationSet with hard-coded project:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: team-apps
spec:
  goTemplate: true
  generators:
  - git:
      repoURL: https://github.com/org/app-configs.git
      revision: main
      files:
      - path: "apps/team-a/**/config.json" # Scoped to team folder
  template:
    metadata:
      name: 'team-a-{{.appName}}'
    spec:
      project: team-a-project # SAFE: hard-coded project
      source:
        repoURL: '{{.source}}' # Can be templated
        targetRevision: '{{.revision}}' # Can be templated
        path: '{{.path}}' # Can be templated
      destination:
        server: https://kubernetes.default.svc # SAFE: hard-coded cluster
        namespace: team-a # SAFE: hard-coded namespace

Requirements for Templated Projects

If the project field is templated, admins MUST control all sources of truth for the ApplicationSet’s generators.
For Git generators:
  • PRs must require admin approval
  • Implement mandatory code review
  • Use branch protection
  • Consider using CODEOWNERS files
Example CODEOWNERS:
# All ApplicationSet source repos require admin review
* @platform-team-admins

# Specific team folders can be managed by team leads
apps/team-a/** @team-a-leads @platform-team-admins
apps/team-b/** @team-b-leads @platform-team-admins

Repository Credentials for ApplicationSets

If your ApplicationSet uses a repository requiring credentials and the project field is templated, you must add the repository as “non project scoped”.

Configuring Non-Scoped Repositories

Via UI:
  1. Navigate to Settings → Repositories
  2. Add repository
  3. Set Project field to blank in the dropdown
Via CLI:
# Do NOT supply the --project parameter
argocd repo add https://github.com/org/app-configs.git \
  --username git \
  --password $GITHUB_TOKEN
Declaratively:
apiVersion: v1
kind: Secret
metadata:
  name: repo-credentials
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
type: Opaque
stringData:
  url: https://github.com/org/app-configs.git
  username: git
  password: ghp_xxxxxxxxxxxxxxxxxxxx
  # DO NOT include project: field here

Why Non-Scoped?

Project-scoped repositories restrict which Projects can use them. If the ApplicationSet’s project field is templated, the ApplicationSet doesn’t know in advance which Projects will be used, so it needs access to a non-scoped repository.

Git Generator Specific Concerns

No Signature Verification

Git generators do not support Git commit signature verification for ApplicationSets with templated project fields.
This means:
  • Cannot verify commit signatures
  • Cannot guarantee commits are from trusted sources
  • Must rely on Git provider access controls
Mitigation:
  • Use GitHub/GitLab branch protection with required reviews
  • Enable signed commits at the repository level
  • Monitor for unexpected changes
  • Use audit logs to track who made changes

SCM Provider Authentication

The Git generator can use Secrets for SCM provider authentication:
generators:
- git:
    repoURL: https://github.com/org/private-repo.git
    revision: main
    files:
    - path: "apps/**/config.json"
    tokenRef:
      secretName: github-token
      key: token
A malicious ApplicationSet creator could reference any Secret in the Argo CD namespace and potentially exfiltrate credentials.
Protection:
  • Restrict ApplicationSet create/update to admins only
  • Use Secret scanning tools
  • Audit ApplicationSet resources regularly

Best Practices

Principle of Least Privilege

  • Only admins can manage ApplicationSets
  • Hard-code sensitive fields (project, cluster, namespace)
  • Scope generators to specific paths or labels
  • Use restricted AppProjects

Git Access Control

  • Require PR reviews for generator sources
  • Use branch protection rules
  • Implement CODEOWNERS
  • Enable audit logging

Monitoring & Alerting

  • Alert on rapid Application creation
  • Monitor resource consumption
  • Track ApplicationSet changes
  • Review audit logs regularly

Resource Limits

  • Set AppProject resource quotas
  • Limit Applications per ApplicationSet
  • Use namespace ResourceQuotas
  • Implement rate limiting

Security Checklist

Before deploying ApplicationSets in production:
1

RBAC Configuration

  • ApplicationSet create/update/delete restricted to admins
  • Kubernetes RBAC rules in place
  • Argo CD RBAC configured appropriately
2

ApplicationSet Review

  • Project field hard-coded (or admin-controlled source)
  • Destination cluster hard-coded or restricted
  • Destination namespace hard-coded or restricted
  • Generator sources controlled by admins
3

Git Repository Security

  • Branch protection enabled
  • Required reviews configured
  • CODEOWNERS file in place
  • Audit logging enabled
4

AppProject Configuration

  • Resource quotas set
  • Destination restrictions configured
  • Source repositories restricted
  • Cluster resource deny list configured
5

Monitoring

  • Alerts for unusual Application creation
  • Resource consumption monitoring
  • Audit log review process
  • Incident response plan

Example Secure Configuration

Here’s a complete example of a secure self-service ApplicationSet:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-a-project
  namespace: argocd
spec:
  description: Team A applications
  
  # Restrict source repositories
  sourceRepos:
  - 'https://github.com/team-a/*'
  
  # Restrict destination clusters and namespaces
  destinations:
  - namespace: 'team-a-*'
    server: https://kubernetes.default.svc
  
  # Resource quotas
  resourceQuotas:
  - applications: 25
  
  # Deny dangerous resources
  clusterResourceBlacklist:
  - group: ''
    kind: Namespace
  - group: rbac.authorization.k8s.io
    kind: ClusterRole
  - group: rbac.authorization.k8s.io
    kind: ClusterRoleBinding
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: team-a-apps
  namespace: argocd
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
  - git:
      repoURL: https://github.com/org/app-configs.git
      revision: main
      files:
      - path: "apps/team-a/**/config.json" # Scoped to team
  template:
    metadata:
      name: 'team-a-{{.appName}}'
    spec:
      project: team-a-project # Hard-coded project
      source:
        repoURL: '{{.source}}' # Team can customize
        targetRevision: '{{.revision}}' # Team can customize
        path: '{{.path}}' # Team can customize
      destination:
        server: https://kubernetes.default.svc # Hard-coded cluster
        namespace: 'team-a-{{.appName}}' # Scoped namespace
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
        - CreateNamespace=true

Next Steps

Overview

Learn more about ApplicationSet fundamentals and capabilities

Build docs developers (and LLMs) love