Skip to main content

Prefer Using Dedicated Service Accounts

More Info:

Assign each pod a dedicated service account instead of relying on the default service account, so that permissions can be scoped per workload.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS OKE

Triage and Remediation

Remediation

Manual Steps
  1. List all workloads and their service accounts

    • Run on: any machine with kubectl access
    • Command (per namespace; start with production namespaces):
      kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,POD:.metadata.name,SA:.spec.serviceAccountName' | sort
    • Identify pods where SA is empty or default, especially for internet-facing, privileged, or data-sensitive workloads.
  2. Review namespace-level usage of the default service account

    • Run on: any machine with kubectl access
    • Commands:
      kubectl get sa -A
      kubectl get sa default -n <namespace> -o yaml
      kubectl get rolebinding,clusterrolebinding -A | grep default | grep ServiceAccount
    • Decide per namespace whether the default service account is (a) unused and should stay unprivileged, or (b) used by many workloads and should be replaced with per-workload service accounts.
  3. Design dedicated service accounts and permissions (IaC / control-plane configuration)

    • In your cloud/IaC configuration (e.g., Helm values, Terraform, CD pipeline manifests, or cloud console workload configuration screens), for each application:
      • Define a dedicated ServiceAccount object name (e.g., orders-api-sa).
      • Bind that service account only to the minimal Role/ClusterRole needed.
    • Example manifest snippet to model in IaC (do not apply with kubectl if you manage via IaC):
      apiVersion: v1
      kind: ServiceAccount
      metadata:
      name: orders-api-sa
      namespace: prod
  4. Update workload definitions to use dedicated service accounts

    • In your cloud provider console, CLI, or IaC for each deployment/statefulset/cronjob:
      • Set spec.template.spec.serviceAccountName to the dedicated service account.
    • Example pod template snippet for IaC/console:
      spec:
      template:
      spec:
      serviceAccountName: orders-api-sa
    • Redeploy workloads via your normal CI/CD or cloud provider deployment mechanism.
  5. Optionally restrict the default service account

    • In namespaces where dedicated service accounts are now used for all meaningful workloads:
      • Ensure the default service account has no elevated RoleBinding/ClusterRoleBinding. Remove or adjust bindings in IaC/cloud console where they reference default.
    • If your provider supports toggling “automount service account token” at namespace or service account level, disable it for default where practical.
  6. Verify that dedicated service accounts are in use

    • Run on: any machine with kubectl access
    • Commands:
      kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,POD:.metadata.name,SA:.spec.serviceAccountName' | sort
      Confirm that all non-trivial workloads show a specific, non-default service account. For a spot check, inspect a pod’s full spec:
      kubectl get pod <pod-name> -n <namespace> -o yaml | grep -n 'serviceAccountName'
Using kubectl

kubectl cannot remediate this finding because it concerns how workloads are defined and deployed in your cloud provider / IaC configuration, not a one-off API change. Make the changes in your cloud console, CI/CD pipeline, or IaC templates to assign explicit, non-default service accounts to pods, and follow the guidance in the Manual Steps section to review and update those definitions.

Automation
#!/usr/bin/env bash
#
# Report pods that use the default service account in each namespace.
# Run on: any machine with kubectl access and correct kubeconfig.
# Requirements: kubectl, jq
set -euo pipefail

# Ensure kubectl works
kubectl auth can-i list pods >/dev/null 2>&1 || {
echo "ERROR: Current identity cannot list pods cluster-wide." >&2
exit 1
}

echo "=== Pods using default service accounts per namespace ==="
echo

# Header
printf "%-30s %-50s %-30s\n" "NAMESPACE" "POD" "SERVICE_ACCOUNT"
printf -- "%0.s-" {1..120}; echo

# Iterate all namespaces
for ns in $(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'); do
# List all pods with their namespace, name, serviceAccountName
kubectl get pods -n "$ns" -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
sa: (.spec.serviceAccountName // "default")
}
| select(.sa == "default")
| "\(.ns) \(.pod) \(.sa)"
' 2>/dev/null \
| while read -r ns_name pod_name sa_name; do
printf "%-30s %-50s %-30s\n" "$ns_name" "$pod_name" "$sa_name"
done
done

echo
echo "=== Summary: count of pods using the default service account per namespace ==="
echo

kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
sa: (.spec.serviceAccountName // "default")
}
| select(.sa == "default")
| .ns
' \
| sort \
| uniq -c \
| awk '{printf "namespace=%-40s pods_using_default_sa=%s\n", $2, $1}'

How to interpret the output

  • Any line in the first table shows a pod currently using the default service account in its namespace.
  • Namespaces with a non-zero pods_using_default_sa count in the summary need review.
  • For each such pod, decide (manually) whether:
    • It is acceptable for it to use the namespace’s default service account with its current RBAC, or
    • You should create and assign a dedicated, least-privilege service account via your cloud/IaC configuration.