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