What Are the Security Risks of the Default ServiceAccount?

intermediate|rbacdevopssrecloud architectCKACKS
TL;DR

Every namespace has a default ServiceAccount that is automatically assigned to Pods without an explicit serviceAccountName. If RBAC bindings are added to the default ServiceAccount, every Pod in that namespace inherits those permissions, creating an over-privileged attack surface.

Detailed Answer

Every Kubernetes namespace is created with a ServiceAccount called default. When a Pod does not specify a serviceAccountName, it automatically runs as this default ServiceAccount. This seemingly harmless behavior creates a significant security risk when the default ServiceAccount accumulates permissions.

The Problem

Consider this common scenario:

  1. A developer needs a Pod to list ConfigMaps in the app namespace.
  2. Instead of creating a dedicated ServiceAccount, they bind the permission to default.
  3. Now every Pod in the namespace — including those that never need API access — can list ConfigMaps.
  4. Over time, more permissions are added to default for convenience.
  5. A compromised Pod (e.g., through a vulnerable dependency) can now query the API server with all accumulated permissions.
# This is dangerous — granting permissions to the default ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: default-can-read-secrets
  namespace: app
subjects:
  - kind: ServiceAccount
    name: default
    namespace: app
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

With this binding, every Pod in the app namespace (unless explicitly using another ServiceAccount) can read Secrets.

Real-World Attack Scenario

  1. An attacker exploits an RCE vulnerability in a web application Pod.
  2. The Pod runs as the default ServiceAccount, which has accumulated permissions.
  3. The attacker reads the mounted token from /var/run/secrets/kubernetes.io/serviceaccount/token.
  4. Using that token, the attacker queries the API server to list Secrets, read ConfigMaps, or discover other workloads.
  5. If the default ServiceAccount has create pods permission, the attacker can launch new Pods with even broader access.

Mitigation: Lock Down the Default ServiceAccount

Step 1: Disable automatic token mounting.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: production
automountServiceAccountToken: false

Apply this to every namespace:

# Patch the default ServiceAccount in all namespaces
for ns in $(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}'); do
  kubectl patch serviceaccount default -n "$ns" \
    -p '{"automountServiceAccountToken": false}'
done

Step 2: Create dedicated ServiceAccounts for each workload.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: order-processor
  namespace: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-processor
  namespace: production
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-processor
  template:
    metadata:
      labels:
        app: order-processor
    spec:
      serviceAccountName: order-processor
      containers:
        - name: app
          image: order-processor:3.1
          resources:
            requests:
              cpu: "200m"
              memory: "256Mi"

Step 3: Remove any existing bindings to the default ServiceAccount.

# Find all bindings referencing the default ServiceAccount
kubectl get rolebindings --all-namespaces -o json | \
  jq -r '.items[] |
    select(.subjects[]? | .name == "default" and .kind == "ServiceAccount") |
    "\(.metadata.namespace)/\(.metadata.name)"'

kubectl get clusterrolebindings -o json | \
  jq -r '.items[] |
    select(.subjects[]? | .name == "default" and .kind == "ServiceAccount") |
    .metadata.name'

Step 4: Enforce with admission control.

Use a policy engine to reject Pods that use the default ServiceAccount:

# Example: Kyverno policy to block default ServiceAccount
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-non-default-sa
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-sa
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Pods must use a dedicated ServiceAccount, not 'default'."
        pattern:
          spec:
            serviceAccountName: "!default"

Audit Script

Run this periodically to check for default ServiceAccount misuse:

#!/bin/bash
echo "=== Pods using default ServiceAccount ==="
kubectl get pods --all-namespaces -o json | \
  jq -r '.items[] |
    select(.spec.serviceAccountName == "default" or
           .spec.serviceAccountName == null) |
    "\(.metadata.namespace)/\(.metadata.name)"'

echo ""
echo "=== Bindings referencing default ServiceAccount ==="
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | \
  jq -r '.items[] |
    select(.subjects[]? | .name == "default" and .kind == "ServiceAccount") |
    "\(.kind): \(.metadata.namespace // "cluster")/\(.metadata.name)"'

echo ""
echo "=== Default ServiceAccounts with token mounting enabled ==="
kubectl get serviceaccount default --all-namespaces -o json | \
  jq -r '.items[] |
    select(.automountServiceAccountToken != false) |
    .metadata.namespace'

What About the Default ServiceAccount in kube-system?

The kube-system namespace deserves special attention. Some cluster components may rely on the default ServiceAccount. Do not blindly disable token mounting in kube-system without verifying that no critical components depend on it. Test in a staging environment first.

Summary of Best Practices

| Practice | Why | |---|---| | Disable automountServiceAccountToken on default | Prevents token exposure in Pods that do not need API access | | Create per-workload ServiceAccounts | Limits blast radius — one compromised SA does not affect others | | Never bind permissions to default | Prevents accidental privilege inheritance | | Enforce with admission control | Catches misconfiguration before it reaches the cluster | | Audit regularly | Detect drift from the desired security posture |

Why Interviewers Ask This

Interviewers ask this to probe your security awareness. The default ServiceAccount is a common source of privilege escalation in real-world clusters, and knowing how to lock it down is essential.

Common Follow-Up Questions

How do you prevent Pods from using the default ServiceAccount?
Set automountServiceAccountToken: false on the default ServiceAccount and create dedicated ServiceAccounts for each workload. Admission controllers or OPA policies can enforce that Pods must specify a non-default ServiceAccount.
Does the default ServiceAccount have any permissions by default?
On a properly configured cluster, it has almost no permissions — just the ability to create SelfSubjectAccessReviews. The risk comes when teams add bindings to it for convenience.
How do you audit which bindings reference the default ServiceAccount?
Query all RoleBindings and ClusterRoleBindings for subjects with name 'default' and kind 'ServiceAccount' using kubectl get with jq or similar filtering.

Key Takeaways

  • Every namespace has a default ServiceAccount — Pods use it unless overridden.
  • Adding RBAC bindings to the default ServiceAccount affects every Pod in the namespace.
  • Always create dedicated ServiceAccounts and disable token mounting on the default.
  • Use admission policies to enforce that Pods specify a non-default ServiceAccount.

Related Questions