pods/exec Should Not Be Granted To Broad Subjects
More Info:
Advisory: review Roles/ClusterRoles that grant create on pods/exec. Exec into a running pod bypasses image immutability and admission controls.
Risk Level
High
Address
Security
Compliance Standards
- Cloudanix Best Practice
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List all Roles/ClusterRoles that grant
createonpods/exec.- Run on: any machine with kubectl access
kubectl get clusterroles -o json | jq -r '.items[]| select(.rules[]?| (.resources[]? == "pods/exec")and (.verbs[]? == "create"))| .metadata.name'kubectl get roles -A -o json | jq -r '.items[]| select(.rules[]?| (.resources[]? == "pods/exec")and (.verbs[]? == "create"))| (.metadata.namespace + ":" + .metadata.name)' -
Identify which subjects are bound to those Roles/ClusterRoles.
- Run on: any machine with kubectl access
# For each ClusterRole name found above, substitute into:kubectl get clusterrolebindings -o yaml | \yq 'select(.roleRef.kind == "ClusterRole" and .roleRef.name == "NAME_HERE") | {metadata:{name}, roleRef, subjects}'# For each namespaced Role "NAMESPACE:NAME" found above:kubectl get rolebindings -n NAMESPACE -o yaml | \yq 'select(.roleRef.kind == "Role" and .roleRef.name == "NAME_HERE") | {metadata:{name,namespace}, roleRef, subjects}' -
Evaluate whether subjects are “broad” and if they truly need exec.
- Treat these as broad and usually inappropriate for
pods/exec:system:authenticated,system:unauthenticated,system:serviceaccounts,system:serviceaccounts:*- Any group that represents all developers or all users
- Wildcard subjects like many service accounts across multiple namespaces
- Keep
createonpods/execonly for:- A small, named set of human users or groups (e.g. on-call SRE group)
- Very limited support automation, with strong justification
- Treat these as broad and usually inappropriate for
-
Design the least-privilege change.
For each over‑broad binding you found:- Decide whether to:
- Remove the binding entirely, or
- Replace broad groups with specific human users/groups, or
- Move
pods/execinto a separate, more restricted Role/ClusterRole and bind only to a small operator group.
Document which Role/ClusterRole and which RoleBinding/ClusterRoleBinding you will edit and the new subject list.
- Decide whether to:
-
Apply the RBAC changes via manifests or kubectl edit.
- Preferred (GitOps-friendly): export, edit, and apply.
- Run on: any machine with kubectl access
# Example: export an existing ClusterRoleBinding for editingkubectl get clusterrolebinding NAME_HERE -o yaml > crb-updated.yaml# Edit subjects: remove broad groups, keep only named human operatorsvi crb-updated.yaml# Apply the changekubectl apply -f crb-updated.yaml# Similarly for RoleBinding:kubectl get rolebinding NAME_HERE -n NAMESPACE -o yaml > rb-updated.yamlvi rb-updated.yamlkubectl apply -f rb-updated.yamlIf manifests are managed by IaC, make equivalent edits in the source repo instead of using
kubectl edit, then redeploy. -
Verify that
pods/execis only granted to the intended narrow subjects.- Run on: any machine with kubectl access
# Re-run evidence gatheringkubectl get clusterroles -o json | jq -r '.items[]| select(.rules[]?| (.resources[]? == "pods/exec")and (.verbs[]? == "create"))| .metadata.name'kubectl get roles -N -o json | jq -r '.items[]| select(.rules[]?| (.resources[]? == "pods/exec")and (.verbs[]? == "create"))| (.metadata.namespace + ":" + .metadata.name)'# For each Role/ClusterRole listed, confirm subjects are only the small, named operator set:kubectl get clusterrolebindings -o yaml | yq 'select(.roleRef.name == "NAME_HERE") | {metadata:{name}, roleRef, subjects}'kubectl get rolebindings -n NAMESPACE -o yaml | yq 'select(.roleRef.name == "NAME_HERE") | {metadata:{name,namespace}, roleRef, subjects}'
Using kubectl
# 1) List all Roles/ClusterRoles that mention pods/exec
# Run on: any machine with kubectl access
kubectl get clusterroles -o yaml | grep -nE "pods/exec|pods\/exec" -A5 -B5
kubectl get roles --all-namespaces -o yaml | grep -nE "pods/exec|pods\/exec" -A5 -B5
What to look for:
- Any rule with:
resources: ["pods/exec"](or included in a list)- AND
verbsincludingcreate(or*)
Example problematic rule snippet:
rules:
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["get", "list", "watch", "create"]
or:
rules:
- apiGroups: [""]
resources: ["pods", "pods/exec", "pods/log"]
verbs: ["*"]
# 2) Show full definitions for candidate ClusterRoles/Roles
# Replace <name> / <namespace> with names you saw above
# ClusterRoles
kubectl get clusterrole <name> -o yaml
# Namespaced Roles
kubectl get role <name> -n <namespace> -o yaml
What to look for:
- Under
rules::resourcesincludespods/exec(explicitly or as part of a broader pattern like["*"]).verbsincludescreateor*.
Flag as higher-risk if:
- The role is clearly generic or broad, e.g.
cluster-admin,edit,viewcustomizations, or any “developer”, “default”, or “*” style role used by many users/groups/service accounts.
# 3) Identify *who* gets each risky Role/ClusterRole (RoleBindings and ClusterRoleBindings)
# Run on: any machine with kubectl access
# List all bindings so you can correlate names
kubectl get clusterrolebindings -o yaml > /tmp/clusterrolebindings.yaml
kubectl get rolebindings --all-namespaces -o yaml > /tmp/rolebindings.yaml
Then search for each Role/ClusterRole name that can create on pods/exec:
# Example: find all bindings for ClusterRole 'dev-ops'
grep -n "name: dev-ops" -A4 -B4 /tmp/clusterrolebindings.yaml
grep -n "name: dev-ops" -A4 -B4 /tmp/rolebindings.yaml
What indicates a problem:
- Bindings that attach a
pods/execcreate-capable role to broad subjects, such as:system:authenticated- Large identity groups (e.g.,
developers,all-users,ci-users) used by many people - Generic service accounts used cluster-wide (e.g.,
defaultin many namespaces)
- Bindings in many namespaces for the same permissive Role.
Example concerning binding snippet:
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: dev-ops # this ClusterRole can create on pods/exec
apiGroup: rbac.authorization.k8s.io
# 4) Focus on the default high-privilege roles for context (read-only)
kubectl get clusterrole admin -o yaml
kubectl get clusterrole edit -o yaml
kubectl get clusterrole cluster-admin -o yaml
What to note:
- These built-in roles are intentionally broad; if they (or custom equivalents) are widely granted, then many subjects may indirectly get
pods/execcreate. - This does NOT by itself say “fix this” — it tells you where exec is inherited from.
# 5) Optional: summarize all roles that include pods/exec with create
# (uses jsonpath and jq; jq must be installed)
kubectl get clusterroles -o json \
| jq '.items[]
| {name: .metadata.name,
rules: (.rules // [])
| map(select((.resources // []) | index("pods/exec"))
| select(((.verbs // []) | index("create")) or ((.verbs // []) | index("*"))))
}
| select(.rules | length > 0)'
Interpretation:
- Every object printed is a ClusterRole that grants
create(or*) onpods/exec. - For each of these, you must manually review:
- Is this meant only for a very small, named set of human operators?
- Are its bindings limited to those identities?
- If the same role shows up here and is bound to broad groups or many service accounts (as seen in step 3), that’s a likely policy concern that needs design and approval rather than an automatic change.
Automation
#!/usr/bin/env bash
# Report Roles/ClusterRoles that grant create on pods/exec, and who can use them.
# Run on: any machine with kubectl access and current context set to target cluster.
set -euo pipefail
echo "=== Searching for Roles and ClusterRoles that grant create on pods/exec ==="
echo
echo "--- ClusterRoles ---"
kubectl get clusterroles -o json \
| jq -r '
.items[]
| select(
(.rules // [])
| map(
(.resources // [] | index("pods/exec"))
and
(.verbs // [] | index("create"))
)
| any
)
| .metadata.name
' | sort -u | tee /tmp/pods-exec-clusterroles.txt
echo
echo "--- Namespaced Roles ---"
kubectl get roles -A -o json \
| jq -r '
.items[]
| select(
(.rules // [])
| map(
(.resources // [] | index("pods/exec"))
and
(.verbs // [] | index("create"))
)
| any
)
| [.metadata.namespace, .metadata.name] | @tsv
' | sort -u | tee /tmp/pods-exec-roles.txt
echo
echo "=== Finding Subjects bound to these Roles/ClusterRoles ==="
echo
echo "--- ClusterRoleBindings using affected ClusterRoles ---"
if [ -s /tmp/pods-exec-clusterroles.txt ]; then
cr_list=$(paste -sd, /tmp/pods-exec-clusterroles.txt)
kubectl get clusterrolebindings -o json \
| jq -r --argjson crs '("'"$cr_list"'")|split(",")' '
.items[]
| select(.roleRef.kind=="ClusterRole" and (.roleRef.name as $n | $crs | index($n)))
| {
name: .metadata.name,
roleRef: .roleRef.name,
subjects: (.subjects // [])
}
| "ClusterRoleBinding: \(.name)\n Role: \(.roleRef)\n Subjects:\n" +
( .subjects[]
| " - kind=\(.kind) name=\(.name) namespace=\(.namespace // "-")"
)
'
else
echo "No ClusterRoles grant create on pods/exec."
fi
echo
echo "--- RoleBindings using affected Roles (namespaced) ---"
if [ -s /tmp/pods-exec-roles.txt ]; then
# Build a set of "namespace|name" for quick checking in jq
mapfile -t role_lines < /tmp/pods-exec-roles.txt
roles_json=$(printf '%s\n' "${role_lines[@]}" | awk -F '\t' '{print "{\"ns\":\""$1"\",\"name\":\""$2"\"}"}' | jq -s '.')
kubectl get rolebindings -A -o json \
| jq -r --argjson roles "$roles_json" '
.items[]
| select(
.roleRef.kind=="Role" and
([$.metadata.namespace, .roleRef.name] as $pair
| $roles | map(select(.ns==$pair[0] and .name==$pair[1])) | length > 0)
)
| {
namespace: .metadata.namespace,
name: .metadata.name,
roleRef: .roleRef.name,
subjects: (.subjects // [])
}
| "RoleBinding: \(.namespace)/\(.name)\n Role: \(.roleRef)\n Subjects:\n" +
( .subjects[]
| " - kind=\(.kind) name=\(.name) namespace=\(.namespace // "-")"
)
'
else
echo "No namespaced Roles grant create on pods/exec."
fi
echo
echo "=== Interpretation Guidance ==="
cat <<'EOF'
Potential problems to review manually:
1) Very broad subjects:
- kind=Group name=system:authenticated
- kind=Group name=system:serviceaccounts
- kind=Group name=system:serviceaccounts:<namespace>
- Wildcard-like or catch-all groups (e.g. "developers", "everyone").
2) Non-human subjects:
- kind=ServiceAccount for application workloads.
3) Cluster-wide scope:
- ClusterRoles with pods/exec create bound cluster-wide when most users
only need exec in specific namespaces.
4) Excessive bindings:
- Same pods/exec-granting role bound to many users/groups/namespaces.
Remediation is manual: restrict pods/exec (create) to a small, named set of
trusted human operators, and use separate, tightly scoped Roles where needed.
EOF
What output indicates a problem
- Any ClusterRole or Role listed at the top sections is a candidate for review.
- In the bindings sections, pay special attention if:
Subjectsinclude wide groups likesystem:authenticated,system:serviceaccounts, or generic org-wide groups.SubjectsareServiceAccountobjects for workloads instead of human users.- The same exec-granting role is bound in many namespaces or via ClusterRoleBindings.