Skip to main content

Minimize Access To Create Pods

More Info:

The ability to create pods can be abused to run privileged workloads or escalate privileges. This permission should be restricted to only required service accounts.

Risk Level

High

Address

Security

Compliance Standards

  • CIS AKS

Triage and Remediation

Remediation

Manual Steps
  1. List all Roles/ClusterRoles that can create pods
    Run on any machine with kubectl access:

    kubectl get clusterrole -o json \
    | jq -r '.items[]
    | select(.rules[]?
    | any(.apiGroups[]? == "" and .resources[]? == "pods" and .verbs[]? == "create"))
    | .metadata.name'

    kubectl get role --all-namespaces -o json \
    | jq -r '.items[]
    | select(.rules[]?
    | any(.apiGroups[]? == "" and .resources[]? == "pods" and .verbs[]? == "create"))
    | (.metadata.namespace + " " + .metadata.name)'
  2. Review each identified Role/ClusterRole’s scope and necessity
    For each name from step 1, inspect full rules and annotations:

    kubectl get clusterrole <clusterrole-name> -o yaml
    kubectl get role -n <namespace> <role-name> -o yaml

    Decide whether the subjects using this role truly need to create pods, or if they could use higher‑level abstractions (Deployments/Jobs) or narrower permissions instead.

  3. Identify who is bound to these Roles/ClusterRoles
    For each Role/ClusterRole discovered, list RoleBindings/ClusterRoleBindings:

    # ClusterRole bindings
    kubectl get clusterrolebindings -o json \
    | jq -r '.items[]
    | select(.roleRef.kind=="ClusterRole" and .roleRef.name=="<clusterrole-name>")
    | .metadata.name'

    # Role bindings in all namespaces
    kubectl get rolebindings --all-namespaces -o json \
    | jq -r '.items[]
    | select(.roleRef.kind=="Role" and .roleRef.name=="<role-name>" and .metadata.namespace=="<namespace>")
    | (.metadata.namespace + " " + .metadata.name)'

    Then inspect each binding to see which users/groups/serviceaccounts are granted pod creation:

    kubectl get clusterrolebinding <binding-name> -o yaml
    kubectl get rolebinding -n <namespace> <binding-name> -o yaml
  4. Decide and implement least-privilege changes
    For each combination of Role/ClusterRole and binding:

    • If pod creation is not required: remove create on pods from the role rules.
      kubectl edit clusterrole <clusterrole-name>
      kubectl edit role -n <namespace> <role-name>
      In the editor, delete create from the verbs list for the pods resource, or remove the rule stanza entirely if no longer needed.
    • If some but not all subjects need this permission: create a new, narrowly scoped Role/ClusterRole just for those subjects, and update bindings so only those specific serviceaccounts/users/groups are bound to it.
  5. Tighten or remove unnecessary bindings
    Where overly broad subjects are found (e.g., groups like system:authenticated or wildcard-like coverage):

    • Replace broad bindings with bindings to specific service accounts or users that actually require pod creation.
    • Remove RoleBindings/ClusterRoleBindings that are no longer needed:
      kubectl delete clusterrolebinding <binding-name>
      kubectl delete rolebinding -n <namespace> <binding-name>
  6. Verify that pod creation access is minimized
    Re-run the evidence commands to confirm only the intended Roles/ClusterRoles retain pod creation:

    kubectl get clusterrole -o json | jq -r '.items[]
    | select(.rules[]?
    | any(.apiGroups[]? == "" and .resources[]? == "pods" and .verbs[]? == "create"))
    | .metadata.name'

    kubectl get role --all-namespaces -o json | jq -r '.items[]
    | select(.rules[]?
    | any(.apiGroups[]? == "" and .resources[]? == "pods" and .verbs[]? == "create"))
    | (.metadata.namespace + " " + .metadata.name)'

    Confirm that each remaining role with create on pods is intentionally required and bound only to the minimal set of subjects.

Using kubectl

Using kubectl

Run the following from any machine with kubectl access.

  1. List all Roles/ClusterRoles that can create pods
kubectl get clusterrole -o json \
| jq -r '.items[]
| select(.rules[]?
| (.verbs[]? | contains("create"))
and (.resources[]? | contains("pods")))
| .metadata.name' \
| sort -u
kubectl get role --all-namespaces -o json \
| jq -r '.items[]
| select(.rules[]?
| (.verbs[]? | contains("create"))
and (.resources[]? | contains("pods")))
| "\(.metadata.namespace)/\(.metadata.name)"' \
| sort -u

Problem indication: Any role/clusterrole appears here that is not clearly intended to create pods (for example, broad “admin”-style roles granted to many subjects, or generic application roles).

  1. Inspect detailed rules for each identified role

For ClusterRoles (replace <clusterrole-name> with each name from step 1):

kubectl get clusterrole <clusterrole-name> -o yaml

For Roles (replace <namespace> and <role-name>):

kubectl get role -n <namespace> <role-name> -o yaml

In the rules section, look for:

  • resources: ["pods"] (or including pods)
  • verbs: ["create"] (or including create or *)

Problem indication:
Rules that allow create on pods (directly, via * verbs, or via * resources) tied to functions that do not strictly require pod creation, or overly broad rules (e.g., verbs: ["*"] or resources: ["*"]).

  1. See who is bound to these Roles/ClusterRoles

For each ClusterRole:

kubectl get clusterrolebinding -o yaml \
| yq '.items[]
| select(.roleRef.kind == "ClusterRole" and .roleRef.name == "<clusterrole-name>")'

For each namespaced Role:

kubectl get rolebinding -n <namespace> -o yaml \
| yq '.items[]
| select(.roleRef.kind == "Role" and .roleRef.name == "<role-name>")'

Problem indication:
Bindings that grant these permissions to:

  • system:authenticated, system:unauthenticated, or other large groups.
  • Generic groups like “developers” or many service accounts that do not need to create pods.
  • User/service account identities where pod creation is not part of their function.
  1. Quick check for obviously over‑broad bindings
kubectl get clusterrolebinding -o json \
| jq -r '.items[]
| select(.subjects[]?
| (.name == "system:authenticated" or .name == "system:unauthenticated"))
| .metadata.name' \
| sort -u
kubectl get clusterrolebinding -o yaml | grep -E "system:authenticated|system:unauthenticated" -n

Problem indication:
Any of these broad subjects bound to roles that have create on pods from step 1.

  1. Verification after manual adjustments

After you manually edit Roles/ClusterRoles/Bindings (with kubectl edit or manifests), re-run:

kubectl get clusterrole -o json | jq -r '.items[]
| select(.rules[]?
| (.verbs[]? | contains("create"))
and (.resources[]? | contains("pods")))
| .metadata.name' | sort -u
kubectl get role --all-namespaces -o json | jq -r '.items[]
| select(.rules[]?
| (.verbs[]? | contains("create"))
and (.resources[]? | contains("pods")))
| "\(.metadata.namespace)/\(.metadata.name)"' | sort -u

Then re-check bindings for any remaining roles that still have pod create and confirm they are restricted only to the specific service accounts/users that truly require this capability.

Automation
#!/usr/bin/env bash
set -euo pipefail

# This script inspects Roles and ClusterRoles that can create pods.
# Run on any machine with kubectl access and a context pointing to the target cluster.

echo "=== ClusterRoles with 'create' on pods ==="
kubectl get clusterroles -o json \
| jq -r '
.items[]
| . as $cr
| [
$cr.metadata.name,
(
[
.rules[]
| select(
(.resources // []) | index("pods")
and
(.verbs // []) | index("create")
)
] | length
)
]
| select(.[1] > 0)
| .[0]
' | sort | uniq \
| while read -r cr; do
echo "ClusterRole: $cr"
kubectl get clusterrole "$cr" -o yaml
echo "-----"
done

echo
echo "=== Roles with 'create' on pods (all namespaces) ==="
kubectl get roles --all-namespaces -o json \
| jq -r '
.items[]
| . as $r
| [
$r.metadata.namespace,
$r.metadata.name,
(
[
.rules[]
| select(
(.resources // []) | index("pods")
and
(.verbs // []) | index("create")
)
] | length
)
]
| select(.[2] > 0)
| .[0] + " " + .[1]
' | sort \
| while read -r ns name; do
echo "Role: $name (namespace: $ns)"
kubectl get role "$name" -n "$ns" -o yaml
echo "-----"
done

echo
echo "=== RoleBindings and ClusterRoleBindings referencing those roles ==="

echo "# ClusterRoleBindings using ClusterRoles that can create pods"
kubectl get clusterrolebindings -o json \
| jq -r '
.items[]
| select(.roleRef.kind == "ClusterRole")
| . as $rb
| $rb.roleRef.name as $crName
| $rb.metadata.name + " " + $crName
' | while read -r rb cr; do
# Check if this ClusterRole is in the risky set (can create pods)
if kubectl get clusterrole "$cr" -o json 2>/dev/null \
| jq -e '
[
.rules[]
| select(
(.resources // []) | index("pods")
and
(.verbs // []) | index("create")
)
] | length > 0
' >/dev/null
then
echo "ClusterRoleBinding: $rb -> ClusterRole: $cr"
kubectl get clusterrolebinding "$rb" -o yaml
echo "-----"
fi
done

echo
echo "# RoleBindings using Roles that can create pods"
kubectl get rolebindings --all-namespaces -o json \
| jq -r '
.items[]
| select(.roleRef.kind == "Role")
| .metadata.namespace + " " + .metadata.name + " " + .roleRef.name
' | while read -r ns rb role; do
if kubectl get role "$role" -n "$ns" -o json 2>/dev/null \
| jq -e '
[
.rules[]
| select(
(.resources // []) | index("pods")
and
(.verbs // []) | index("create")
)
] | length > 0
' >/dev/null
then
echo "RoleBinding: $rb (namespace: $ns) -> Role: $role"
kubectl get rolebinding "$rb" -n "$ns" -o yaml
echo "-----"
fi
done

cat <<'EOF'

How to interpret this output:

1. Any ClusterRole or Role shown has the ability to create pods somewhere in the cluster.
2. For each such Role/ClusterRole, review:
- rules: ensure "resources: [pods]" with "verbs: [create]" is truly required.
- scope: wide-scoped roles (e.g., in kube-system, default, or custom infra namespaces) deserve extra scrutiny.
3. For each associated RoleBinding/ClusterRoleBinding:
- Check the `subjects` section (users, groups, service accounts).
- Investigate whether each subject actually needs to create pods.
- Service accounts that do not run controllers, operators, or deployment tools typically should NOT need pod creation rights.

Potential problem indicators:
- Generic or broad roles (e.g., "admin", "edit", custom "dev-*") that grant create on pods and are bound to many users or groups.
- ClusterRoles with create on pods bound via ClusterRoleBinding to:
- `system:authenticated`, `system:unauthenticated`, or large identity groups (e.g., "Developers", "Everyone").
- Service accounts in application namespaces that are not part of a deployment/orchestration component but still have create on pods.

Use `kubectl edit role -n <namespace> <role>` and
`kubectl edit clusterrole <name>` to manually apply least privilege,
removing "create" on "pods" where it is not strictly required.

EOF