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.
Cross-Links
| Topic | Link |
|---|---|
| Kubernetes RBAC | k8s-rbac |
| SPIFFE / SPIRE | spiffe-spire |
| AWS STS | aws-sts |
| GCP Workload Identity | workload-identity-federation |