Tekton Security Testing: Pipeline RBAC Escalation, Secret Injection in Tasks, and Workspace Volume Abuse

Tekton is a Kubernetes-native CI/CD framework where pipelines run as Pods with user-defined container images and service accounts. Because pipeline tasks execute arbitrary code in containers with configurable service accounts, Tekton is a natural privilege escalation vector: a TaskRun can use a high-privilege service account, parameter substitution can inject shell commands into scripts, workspaces backed by shared PVCs can leak data across pipeline runs, and the Tekton Dashboard may expose pipeline controls without authentication. This guide covers systematic Tekton security assessment.

Table of Contents

  1. TaskRun Service Account Privilege Escalation
  2. Parameter Injection in Task Scripts
  3. Secret Access and Exposure in Tasks
  4. Workspace Volume Cross-Run Leakage
  5. Tekton Dashboard Unauthenticated Access
  6. Malicious Task Container Image Substitution
  7. Tekton Triggers Webhook Exploitation
  8. Tekton Security Hardening

TaskRun Service Account Privilege Escalation

# TaskRuns and PipelineRuns specify a serviceAccountName
# If a user can create TaskRuns with a privileged SA, they escalate to that SA's permissions

# Check who can create TaskRuns and PipelineRuns
kubectl auth can-i create taskruns \
  --as=system:serviceaccount:developer:developer-sa \
  -n ci-cd

# List service accounts available in the CI/CD namespace and their permissions
kubectl get serviceaccounts -n ci-cd -o json | python3 -c "
import json,sys
sas = json.load(sys.stdin)['items']
for sa in sas:
    print(f\"SA: {sa['metadata']['name']}\")
"

# Test: create a TaskRun using a privileged SA
kubectl apply -f - <

Parameter Injection in Task Scripts

# Tekton Tasks use $(params.NAME) substitution in script fields
# If user-controlled parameters are interpolated unsafely into shell scripts,
# shell injection is possible

# Find Tasks with parameters interpolated in scripts
kubectl get tasks --all-namespaces -o json | python3 -c "
import json,sys,re
tasks = json.load(sys.stdin)['items']
for t in tasks:
    meta = t['metadata']
    for step in t.get('spec',{}).get('steps',[]):
        script = step.get('script','')
        # Find parameter substitutions in scripts
        params_in_script = re.findall(r'\\\$(params\.[^)]+)', script)
        if params_in_script:
            print(f\"Task: {meta['namespace']}/{meta['name']} step: {step.get('name','?')}\")
            for p in params_in_script:
                print(f\"  Substitution: {p}\")
            # If substitution is in a shell command without quoting, injection possible
            if re.search(r'(sh|bash|/bin/sh)\s+-c.*\\\$(params', script):
                print(f\"  [!] Unquoted parameter in shell -c: INJECTION RISK\")
"

# Test: inject shell commands via a parameter
kubectl apply -f - <

Secret Access and Exposure in Tasks

# Tekton tasks can mount Kubernetes secrets as env vars or volumes
# Test what secrets are accessible to pipeline tasks and whether they leak

# Check Tasks and Pipelines for secret references
kubectl get tasks --all-namespaces -o json | python3 -c "
import json,sys
tasks = json.load(sys.stdin)['items']
for t in tasks:
    meta = t['metadata']
    for step in t.get('spec',{}).get('steps',[]):
        for env in step.get('env',[]):
            if 'valueFrom' in env and 'secretKeyRef' in env.get('valueFrom',{}):
                secret = env['valueFrom']['secretKeyRef']
                print(f\"Task: {meta['namespace']}/{meta['name']}\")
                print(f\"  Env {env['name']} from secret: {secret['name']} key: {secret['key']}\")
"

# Check TaskRun logs for secret leakage (secrets printed to stdout)
kubectl get taskruns --all-namespaces -o json | python3 -c "
import json,sys
runs = json.load(sys.stdin)['items']
for r in runs:
    meta = r['metadata']
    # Check for env var injection that could print secrets
    params = r.get('spec',{}).get('params',[])
    for p in params:
        if any(k in str(p.get('value','')).lower() for k in ['password','secret','key','token']):
            print(f\"[!] TaskRun {meta['namespace']}/{meta['name']} param {p['name']} may contain secret: {str(p.get('value',''))[:20]}\")
"

# Check if Tekton stores secrets in PipelineRun results (visible in CR status)
kubectl get pipelineruns --all-namespaces -o json | python3 -c "
import json,sys
runs = json.load(sys.stdin)['items']
for r in runs:
    results = r.get('status',{}).get('pipelineResults',[])
    for result in results:
        val = result.get('value','')
        if len(val) > 20:  # Suspicious length results
            print(f\"{r['metadata']['name']}: result {result['name']} = {val[:50]}\")
"

Tekton Dashboard Unauthenticated Access

# Tekton Dashboard is often deployed without authentication (read-only by default)
# But some deployments allow write operations (creating TaskRuns, PipelineRuns)

# Check Dashboard deployment configuration
kubectl get deployment tekton-dashboard -n tekton-pipelines -o json | python3 -c "
import json,sys
dep = json.load(sys.stdin)
containers = dep['spec']['template']['spec']['containers']
for c in containers:
    args = c.get('args',[])
    read_only = '--read-only' in args
    auth = '--auth-level' in ' '.join(args)
    print(f\"Dashboard args: {args}\")
    print(f\"Read-only: {read_only}\")
    print(f\"Auth configured: {auth}\")
    if not read_only and not auth:
        print('[!] Dashboard allows unauthenticated writes (create/run pipelines)')
"

# Test: access Dashboard without credentials
curl -s http://tekton-dashboard.tekton-pipelines:9097/api/v1/namespaces/ci-cd/taskruns \
  | python3 -m json.tool | head -20

# Test: create a TaskRun via unauthenticated Dashboard API
curl -X POST http://tekton-dashboard.tekton-pipelines:9097/api/v1/namespaces/ci-cd/taskruns \
  -H "Content-Type: application/json" \
  -d '{
    "apiVersion": "tekton.dev/v1",
    "kind": "TaskRun",
    "metadata": {"name": "unauth-test"},
    "spec": {
      "taskSpec": {
        "steps": [{"name": "test", "image": "alpine", "script": "id"}]
      }
    }
  }' 2>/dev/null | head -5

Tekton Security Hardening

Tekton Security Hardening Checklist:
  • Restrict TaskRun/PipelineRun creation RBAC — developers shouldn't specify arbitrary serviceAccountNames
  • Use Kyverno/OPA to enforce an allowed serviceAccountName list per namespace
  • Always quote $(params.X) in shell scripts: "$(params.image)" not $(params.image)
  • Validate parameter values with Tekton's param validation or admission webhooks
  • Deploy Tekton Dashboard with --read-only=true and behind authentication proxy
  • Use dedicated, least-privilege service accounts per pipeline — never reuse cross-pipeline SAs
  • Set non-root securityContext on all Task steps: runAsNonRoot: true
  • Avoid mounting broad secrets as env vars — use External Secrets Operator and mount only needed keys
  • Enable Tekton Chains for supply chain security: sign TaskRun attestations with Sigstore
Security TestMethodRisk
Developer can create TaskRun with privileged SAkubectl auth can-i create taskruns — test SA fieldCritical
Dashboard allows unauthenticated TaskRun creationPOST /api/v1/namespaces/X/taskruns without authCritical
Unquoted parameter substitution in shell scriptsAudit Task scripts for $(params.X) in sh -cHigh
Task steps run as rootCheck step securityContext.runAsNonRootHigh
Workspace PVC reused across pipeline runsCheck Workspace PVC retention policiesMedium
Secrets logged in TaskRun resultskubectl get pipelineruns — inspect status.resultsMedium

Automate Tekton Security Testing

Ironimo tests Tekton deployments for TaskRun service account privilege escalation, parameter injection vulnerabilities in task scripts, workspace PVC cross-run data leakage, Dashboard unauthenticated access, secret exposure in pipeline logs, and supply chain attestation gaps via Tekton Chains.

Start free scan