calricon.

General

The Kubernetes ServiceAccount Token Trap: Why Static Secret Mounts Bleed Cloud Credentials

Oct 22, 2026/8 min read

calricon / field note

Kubernetes clusters routinely auto-mount legacy ServiceAccount tokens into every container, believing default configurations are secure. In reality, static tokens never expire and lack audience constraints, allowing attackers who exploit a single container LFI to steal cluster-admin credentials and move laterally across the cloud.

Oct 22, 2026/General
213
Advertisement

The Kubernetes ServiceAccount Token Trap: Why Static Secret Mounts Bleed Cloud Credentials

The short answer: In Kubernetes architecture, every pod is assigned a ServiceAccount to authenticate against the cluster API server. By default, the Kubernetes control plane automatically mounts a JSON Web Token (JWT) into every running container at /var/run/secrets/kubernetes.io/serviceaccount/token. In legacy and misconfigured clusters, this default mechanism is an existential security vulnerability. These auto-mounted tokens are static, unexpiring, and completely unbounded by audience or pod lifecycle. If an attacker exploits a routine Local File Inclusion (LFI) bug, a path-traversal vulnerability, or a Server-Side Request Forgery (SSRF) in a public web application, they can read this single file. Because the token has no audience restrictions and never expires, the attacker can use it from any terminal on the public internet to interrogate the Kubernetes API server, elevate privileges, and compromise the entire cluster control plane.

"Leaving automountServiceAccountToken enabled on pods that don't talk to the Kubernetes API is the architectural equivalent of taping a master key to your front door for every visitor to see. The moment an attacker finds a single read vulnerability in your web tier, they aren't just in your container—they own your entire cluster."

1. The Mechanical Reality of Legacy ServiceAccount Tokens

To understand the vulnerability, you must examine how Kubernetes handles pod identity:

  • When a ServiceAccount is created in Kubernetes (prior to v1.22, or in clusters retaining legacy behavior), the Token Controller creates a permanent Secret of type kubernetes.io/service-account-token.
  • When a pod starts, the kubelet mounts this Secret as a volume into the container filesystem at /var/run/secrets/kubernetes.io/serviceaccount/.
  • The volume contains three files: ca.crt (the cluster root certificate), namespace (the current namespace name), and token (the raw signed JWT bearer token).

This design prioritized developer convenience over zero-trust security. The token is stored as plaintext in etcd, remains valid until the ServiceAccount itself is deleted, and can be used by anyone, from anywhere, against any API endpoint that trusts the cluster's signing key.

2. The 4 Fatal Attack Vectors of Static ServiceAccount Tokens

A. The LFI / Path Traversal Exfiltration

A web application running in a pod does not need to suffer a container escape to compromise the cluster. Consider a routine application vulnerability—such as an image rendering service vulnerable to directory traversal (e.g., GET /preview?file=../../../../var/run/secrets/kubernetes.io/serviceaccount/token) or an unpatched Spring4Shell / Log4j flaw.

The application dutifully returns the raw string from the filesystem. The attacker copies the JWT token, sets their local kubeconfig: kubectl --token="eyJhbGci..." get pods --all-namespaces, and begins querying cluster resources directly from their external attack laptop.

B. The Audience-Less Blast Radius (Replay Attacks)

In secure identity protocols (OAuth 2.0 / OpenID Connect), tokens enforce an aud (Audience) claim: a token minted for a backend payment service cannot be presented to an internal analytics service. If Service B receives a token meant for Service A, Service B must reject it.

Legacy Kubernetes ServiceAccount tokens contain zero audience restrictions. If a developer uses a pod's ServiceAccount token to authenticate against an internal microservice or a third-party logging vendor, that third party can replay the exact same token against the Kubernetes API server to impersonate the pod with full RBAC permissions.

C. The Zombie Token Problem (Immunity to Pod Termination)

When a pod is deleted, scaled down by an HPA, or terminated due to an eviction, what happens to its legacy ServiceAccount token? Nothing.

Because the token was minted as a static Kubernetes Secret tied to the ServiceAccount rather than the ephemeral pod instance, the stolen token remains 100% valid. An attacker who exfiltrates a token from a transient batch job or temporary preview environment can continue using it for months or years after the pod that generated it has ceased to exist.

D. Cloud Workload Identity Hijacking (AWS IRSA & GCP Workload Identity)

Modern cloud architectures bind Kubernetes pods to cloud IAM roles using OIDC federation (such as AWS IAM Roles for Service Accounts - IRSA or GCP Workload Identity). The cloud provider trusts the JWT token emitted by the cluster to exchange it for temporary AWS STS or Google Cloud IAM credentials.

If an attacker steals an unconstrained ServiceAccount token associated with an IAM role (e.g., arn:aws:iam::123456789:role/data-lake-reader), they can exchange that token directly via the AWS STS AssumeRoleWithWebIdentity API from outside the VPC. The attacker bypasses cloud network security controls and accesses corporate S3 data buckets directly.

3. Architecture Comparison: Pod Identity Paradigms

Identity Mechanism Token Expiration Lifetime Audience Binding (aud) Pod Lifecycle Binding Enterprise Security Verdict
Legacy Secret-Based Tokens (Pre-v1.22) Infinite (Never expires) None (Universal replay vulnerability) None (Survives pod deletion) Severe security vulnerability
Default Auto-Mount (Unconfigured) Varies by cluster version Cluster API only Weak Anti-pattern for non-infrastructure pods
Bound Projected ServiceAccount Tokens Short-lived (Default 1 hour; auto-rotated) Strictly Enforced (e.g., sts.amazonaws.com) Tied to Pod UID (Dead on pod exit) Gold Standard for Kubernetes Security
Explicit Token Disabling (automount: false) Zero tokens mounted N/A N/A Mandatory for 95% of Business Microservices

4. The Calricon Production Runbook: Hardening Pod Identity

1. Disable Auto-Mounting by Default (The Principle of Least Privilege)

The vast majority of web applications, background workers, and APIs never need to communicate with the Kubernetes API server. Enforce automountServiceAccountToken: false at both the ServiceAccount and Pod spec levels:

# Disable Token Mounting at the ServiceAccount Level
apiVersion: v1
kind: ServiceAccount
metadata:
  name: billing-api-sa
  namespace: production
automountServiceAccountToken: false # NEVER mount tokens by default!

---
# Enforce in Pod Specification
apiVersion: apps/v1
kind: Deployment
metadata:
  name: billing-api
spec:
  template:
    spec:
      serviceAccountName: billing-api-sa
      automountServiceAccountToken: false # Explicit double-lock
      containers:
      - name: api
        image: billing-api:v2.1

2. Adopt Bound Projected ServiceAccount Tokens (TokenRequest API)

For applications that legitimately require tokens (e.g., ingress controllers, monitoring agents, or Cloud Workload Identity integrations), mandate Bound Service Account Tokens (KEP-1205) using projected volumes:

# Projected ServiceAccount Token with Audience and Expiration
volumes:
- name: bound-token
  projected:
    sources:
    - serviceAccountToken:
        audience: "sts.amazonaws.com" # Restricted strictly to AWS STS!
        expirationSeconds: 3600       # Rotated automatically every 1 hour
        path: token

Bound tokens introduce three critical security guarantees:

  • Time-Bounded: Tokens expire in 1 hour. The kubelet automatically refreshes and re-writes the token file before expiration without restarting the container.
  • Audience-Bound: The token cannot be presented to any service other than the explicit audience declared in audience.
  • Object-Bound: The token is cryptographically bound to the pod's unique UID. The exact millisecond the pod is deleted, the Kubernetes API server marks the token permanently invalid.

3. Enforce Invariant Policies via Kyverno / OPA Gatekeeper

Stop relying on developers to remember to set automountServiceAccountToken: false. Deploy an admission controller rule that automatically injects this setting or rejects non-compliant deployments:

# Kyverno Policy: Forbid Auto-Mount on Default Namespaces
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-automount-serviceaccount-token
spec:
  validationFailureAction: Enforce
  rules:
  - name: validate-automount
    match:
      any:
      - resources:
          kinds: ["Pod"]
          namespaces: ["production", "staging"]
    validate:
      message: "Automounting ServiceAccount tokens is forbidden. Set automountServiceAccountToken: false."
      pattern:
        spec:
          automountServiceAccountToken: false

Strategic Verdict for Platform Engineers & CISOs

Zero-trust security requires treating every container as potentially compromised. Auto-mounting static, unexpiring administrative credentials into every application pod violates the most fundamental principles of security engineering. Modernize your Kubernetes identity architecture: disable automatic token mounting across all standard business workloads, mandate bound projected tokens with strict audience constraints for cloud integrations, and ensure an isolated container vulnerability never compromises your entire enterprise cloud infrastructure.

Advertisement
Talk to Calricon