>samit_hota
Back to research
CLOUD SECURITY

Stopping Kubernetes Escalation: Service Account Tokens and Over-Broad RBAC

Samit Hota·
#kubernetes#cloud-security#rbac

Default configurations in Kubernetes treat every workload as if it might eventually need to talk to the API server, dropping standard JWT bearer tokens into containers that never requested them. When an application vulnerability allows an attacker to execute commands inside a pod, these mounted service account tokens provide an immediate identity context. If that identity carries over-broad Role-Based Access Control (RBAC) permissions, what began as a localized web application compromise quickly escalates into full cluster takeover.

Understanding this attack path requires looking beyond standard vulnerability scanners and examining how default identity propagation interacts with permissive authorization policies.

The Default Token Mount Trap

Unless explicitly instructed otherwise, the Kubernetes kubelet automatically mounts a ServiceAccount’s API token into every container at /var/run/secrets/kubernetes.io/serviceaccount/token. Along with the token, the kubelet injects the cluster CA certificate (ca.crt) and a file containing the current namespace name.

For the vast majority of microservices—such as stateless web frontends, backend APIs, or databases—access to the Kubernetes API server is completely unnecessary. However, because the token exists on the local filesystem, any arbitrary code execution or local file inclusion (LFI) vulnerability exposes these credentials immediately.

An adversary operating inside a compromised container can read this token file and present it directly to the internal API server endpoint, typically reachable via the environment variable KUBERNETES_SERVICE_HOST or the internal DNS name kubernetes.default.svc.

# Inspecting the mounted identity token inside a running pod
cat /var/run/secrets/kubernetes.io/serviceaccount/token

If the token belongs to the default ServiceAccount in a namespace where custom RBAC policies haven’t been tuned, or if a developer bound a high-privilege ClusterRole to a standard workload, the API server will authenticate the request and evaluate the permission set.

Mapping Token Privileges with Native Tooling

Once a token is extracted, an attacker does not need external tools to discover what operations they are permitted to perform. The Kubernetes API exposes a self-subject evaluation endpoint designed specifically for querying one’s own effective permissions.

Using kubectl from outside the cluster—or raw curl commands using the extracted JWT—an operator can enumerate allowed verbs and resources across namespaces.

# Querying API permissions using an extracted token
kubectl auth can-i --list --token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) \
  --server=https://<API_SERVER_IP>:6443 \
  --certificate-authority=/path/to/ca.crt

Common configuration errors that enable rapid escalation include:

  • Binding ClusterRole/cluster-admin to a workload service account instead of a user or deployment controller.
  • Granting create or patch permissions on pods or deployments, allowing an attacker to launch new malicious pods or alter existing specs to mount host paths or administrative tokens.
  • Granting get or list access to secrets across all namespaces, allowing plaintext extraction of sensitive API keys, cloud provider credentials, or TLS private keys.
  • Granting impersonate verbs on users or service accounts.

If the returned capability matrix shows permission to create pods or read secrets, the boundary between the container context and cluster management has effectively dissolved.

Hardening Workloads: Disabling Automatic Token Mounts

The most direct fix for unnecessary token exposure is stopping the kubelet from mounting tokens where they are not required. This control can be applied globally at the ServiceAccount level or overridden for individual Pod specs.

To disable automatic token mounting across all workloads using a specific ServiceAccount, set automountServiceAccountToken: false in the account definition:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: application-sa
  namespace: production
automountServiceAccountToken: false

If a shared ServiceAccount must retain token mounting for specific workloads, enforce the control directly within the Pod manifest:

apiVersion: v1
kind: Pod
metadata:
  name: payment-service
  namespace: production
spec:
  automountServiceAccountToken: false
  serviceAccountName: application-sa
  containers:
  - name: api
    image: payment-api:v2.1.0
    ports:
    - containerPort: 8080

When automountServiceAccountToken is set to false, the directory /var/run/secrets/kubernetes.io/serviceaccount/ is never created inside the container, eliminating the credential file from disk entirely.

Structuring Least-Privilege RBAC Policies

When a workload legitimately requires API access—such as an ingress controller, monitoring agent, or custom operator—its permissions must be strictly scoped using explicit Role definitions rather than permissive ClusterRole bindings.

Avoid wildcard definitions in verbs or resources. Instead, grant only the specific operations required on the exact resources utilized by the application.

# A properly scoped, namespace-bounded Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-status-viewer
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: bind-pod-status-viewer
  namespace: production
subjects:
- kind: ServiceAccount
  name: monitoring-sa
  namespace: production
roleRef:
  kind: Role
  name: pod-status-viewer
  apiGroup: rbac.authorization.k8s.io

Key rules for maintaining secure RBAC configurations:

  1. Use Role and RoleBinding instead of ClusterRole and ClusterRoleBinding whenever access can be restricted to a single namespace.
  2. Never grant asterisk (*) wildcards in verbs, apiGroups, or resources in production configurations.
  3. Audit existing bindings periodically using automated tools like kube-rbac-audit or native kubectl get clusterrolebindings -o yaml queries to flag accounts bound to standard administrative roles.
  4. Separate deployment identities from execution identities; the account running a pod should never hold permission to modify its own deployment manifest or sibling pods.

Want a second set of eyes on your security posture?

Let's talk about where your real exposure is.

Book an advisory call