Skip to main content

Kubernetes Service Accounts

Status: Final

Kubernetes Service Accounts are the identity mechanism for workloads running in a cluster. Every pod runs as a service account — by default, the namespace default service account. Understanding service account token mechanics is essential for container escape escalation paths and cloud credential theft via SSRF.


Service Account Tokens

Legacy Token (K8s < 1.21 default)

  • Format: JWT signed by the Kubernetes API server's signing key
  • Storage: Secret object in the namespace → auto-mounted at /var/run/secrets/kubernetes.io/serviceaccount/token
  • Expiry: Never expires (major security issue)
  • Audience: Any Kubernetes API server

This is the classic "steal the token from the pod, use it to call the API server" attack path.

Projected Service Account Tokens (K8s ≥ 1.21 default)

  • Audience-bound: specific to a resource or service
  • Time-bound: 1-hour default expiry, auto-rotated
  • Mounted via: Projected Volume — not stored as a Secret object

Still auto-mounted — the token file exists in every pod at the same path — but it now has a bound audience and expiry.

Token Binding to Cloud Identity (OIDC)

When using cloud-managed K8s (EKS, GKE, AKS):

  • The cluster has an OIDC issuer URL
  • Service account tokens are OIDC-compatible JWTs
  • Cloud IAM trusts the cluster's OIDC issuer
  • Pod → cloud IAM role without any static credentials in the cluster

Example: AWS EKS with IAM Roles for Service Accounts (IRSA):

annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/s3-reader

Attack Surface

Attack 1: Token Theft from Pod Filesystem

# From inside a compromised container:
cat /var/run/secrets/kubernetes.io/serviceaccount/token
# → use with kubectl or API calls
kubectl --token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) get pods -n kube-system

Attack 2: SSRF → Token Theft

If an application is vulnerable to SSRF and runs in Kubernetes:

SSRF → http://169.254.169.254/... (AWS IMDS)
or
SSRF → http://metadata.google.internal/... (GCP IMDS)
or
SSRF → http://kubernetes.default.svc/api/v1/namespaces/default/secrets/
(if SA has secrets read permission)

Attack 3: Container Escape + Cluster Takeover

Privileged container or hostPath mount → host filesystem access → read node-level kubelet credentials or etcd credentials → cluster-wide access.

Attack 4: Cloud Privilege Escalation via IRSA/Workload Identity

If a service account is bound to a cloud IAM role with excessive permissions (e.g., AdministratorAccess), compromising the pod gives cloud admin access.


Default Service Account Anti-Pattern

By default, the default service account in each namespace is auto-mounted into every pod. If the default SA has any permissions (from overly permissive ClusterRoleBindings), every pod is a credential theft target.

Hardening: automountServiceAccountToken: false in pod spec when the pod does not need API access.


TopicLink
Kubernetes RBACk8s-rbac
SPIFFE / SPIREspiffe-spire
AWS STSaws-sts
GCP Workload Identityworkload-identity-federation