OCI Minimize Access Create Pods
More Info:
Where possible, remove create access to pod objects in the cluster.
Risk Level
Low
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS OKE
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify who can create pods and through which roles/clusterroles
- Run on any machine with kubectl access:
kubectl get clusterrole,role -A -o yaml \| grep -nE '^(kind: (ClusterRole|Role)| name:| apiGroups:|\- apiGroups:|\- resources:|\- verbs:)' -n
- Review all
ClusterRoleandRoleobjects that include:apiGroups: [""]resources: ["pods"]verbs: ["create"] # or ["*"], or any list that includes "create"
- Run on any machine with kubectl access:
-
Map roles/clusterroles with pod create to subjects and usage
- For each
Role/ClusterRoleidentified in step 1, list who is bound to it:# Cluster-widekubectl get clusterrolebindings -o yaml \| grep -nE '^(kind: ClusterRoleBinding|name:|roleRef:|subjects:)' -n# Per-namespacefor ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); doecho "### Namespace: $ns"kubectl get rolebindings -n "$ns" -o yaml \| grep -nE '^(kind: RoleBinding|name:|roleRef:|subjects:)' -ndone - Correlate
roleRef.namewith the roles/clusterroles that grantpods/create, and list thesubjects(users, groups, serviceaccounts) for each.
- For each
-
Decide least-privilege needs per subject
- For each subject that currently has
pods/create:- Determine if it must create pods directly (e.g., controllers, system components) or if it should instead use higher-level resources like Deployments, Jobs, or CronJobs.
- For human users, prefer that only cluster/platform operators retain pod create rights; application developers normally deploy via workload controllers, not raw pods.
- Document which bindings are:
- Required (must keep
pods/create), - Over-privileged (can remove
pods/create), or - Replaceable (can move to a more restrictive role without
pods/create).
- Required (must keep
- For each subject that currently has
-
Adjust over-privileged roles/clusterroles (edit manifests or live objects)
- For each over-privileged role/clusterrole, remove
create(and, if possible, wildcard*) forpodsfrom the rules:- If you manage RBAC via manifests, edit the YAML in your Git/IaC and apply later.
- To make an immediate targeted change (on any machine with kubectl access):
# Example: edit a specific ClusterRole in placekubectl edit clusterrole <clusterrole-name># or edit a namespaced Rolekubectl edit role <role-name> -n <namespace>
- In the opened editor, adjust entries like:
to remove- apiGroups: [""]resources: ["pods"]verbs: ["get", "list", "watch", "create"]
create, and avoid usingverbs: ["*"]where not strictly required.
- For each over-privileged role/clusterrole, remove
-
Remove or narrow bindings where pod create is not needed
- Where a subject should not create pods at all:
- Rebind it to a more restrictive role without
pods/create, or delete the binding if no access is needed. - Example to delete an unneeded binding (any machine with kubectl access):
# ClusterRoleBindingkubectl delete clusterrolebinding <binding-name># Namespaced RoleBindingkubectl delete rolebinding <binding-name> -n <namespace>
- Rebind it to a more restrictive role without
- Ensure any replacement roles you use have been verified not to include
pods/createunder any rule.
- Where a subject should not create pods at all:
-
Re‑verify pod create permissions
- After changes, re-run the review to confirm that only justified roles have
pods/create:kubectl get clusterrole,role -A -o yaml \| grep -nE '^(kind: (ClusterRole|Role)| name:| apiGroups:|\- apiGroups:|\- resources:|\- verbs:)' -n - Optionally, test from a specific subject’s perspective by impersonation (requires appropriate auth):
# Example: test if a service account can still create podskubectl auth can-i create pods \--as=system:serviceaccount:<namespace>:<serviceaccount-name> \--namespace <namespace>
- Confirm that
kubectl auth can-i create podsreturnsnofor all users/groups/service accounts that should not create pods.
- After changes, re-run the review to confirm that only justified roles have
Using kubectl
Using kubectl
1. List all roles/clusterroles that can create pods
Run on: any machine with kubectl access.
kubectl get clusterroles -o json | jq -r '
.items[]
| select(.rules[]? | any(.resources[]?; . == "pods") and any(.verbs[]?; . == "create" or . == "*"))
| .metadata.name
' | sort | uniq
kubectl get roles --all-namespaces -o json | jq -r '
.items[]
| select(.rules[]? | any(.resources[]?; . == "pods") and any(.verbs[]?; . == "create" or . == "*"))
| [.metadata.namespace, .metadata.name] | @tsv
' | sort | uniq
Problem indication:
- Any
ClusterRoleorRolein these lists represents a principal that can create pods. - Especially concerning are:
- Names like
cluster-admin,admin, or custom broad roles used widely. - Rules where
resources: ["*"]orverbs: ["*"]include pod creation implicitly. - Roles in sensitive namespaces (e.g.,
kube-system) or used by untrusted teams.
- Names like
2. Inspect detailed rules for suspicious roles
# Example: inspect one clusterrole
kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml
# Example: inspect one namespaced role
kubectl get role <ROLE_NAME> -n <NAMESPACE> -o yaml
Problem indication:
resourcesincludespodsor"*"andverbsincludescreateor"*".- Pod creation permissions not limited by
resourceNamesor scoped alternatives (e.g., they can create arbitrary pods).
3. See who is bound to those pod-creating roles
# ClusterRoleBindings for clusterroles that can create pods
for cr in $(kubectl get clusterroles -o json | jq -r '
.items[]
| select(.rules[]? | any(.resources[]?; . == "pods" or . == "*") and any(.verbs[]?; . == "create" or . == "*"))
| .metadata.name
' | sort -u); do
echo "=== ClusterRole: $cr ==="
kubectl get clusterrolebindings -o json | jq -r --arg CR "$cr" '
.items[]
| select(.roleRef.kind == "ClusterRole" and .roleRef.name == $CR)
| [.metadata.name, (.subjects // [] | map(.kind + "/" + .name + (if .namespace then ":" + .namespace else "" end)) | join(","))]
| @tsv
' || true
done
# RoleBindings for roles that can create pods
kubectl get roles --all-namespaces -o json | jq -r '
.items[]
| select(.rules[]? | any(.resources[]?; . == "pods" or . == "*") and any(.verbs[]?; . == "create" or . == "*"))
| [.metadata.namespace, .metadata.name] | @tsv
' | while IFS=$'\t' read ns rn; do
echo "=== Role: $rn (ns: $ns) ==="
kubectl get rolebindings -n "$ns" -o json | jq -r --arg RN "$rn" '
.items[]
| select(.roleRef.kind == "Role" and .roleRef.name == $RN)
| [.metadata.name, (.subjects // [] | map(.kind + "/" + .name + (if .namespace then ":" + .namespace else "" end)) | join(","))]
| @tsv
' || true
done
Problem indication:
- Service accounts, users, or groups that should not be allowed to create workloads (e.g., read-only users, CI jobs that only need logs, monitoring accounts) appear as subjects.
- Broad group subjects like
system:authenticatedorsystem:serviceaccountsare bound to pod-creating roles.
4. (Optional) Verify effective access for a specific principal
# For a service account
kubectl auth can-i create pods --as=system:serviceaccount:<NAMESPACE>:<SA_NAME> -n <TARGET_NAMESPACE>
# For a user
kubectl auth can-i create pods --as=<USERNAME> -n <TARGET_NAMESPACE>
Problem indication:
- Command returns
yesfor principals that, by policy, should not be able to create pods.
5. Re-run to verify after manual changes
After you revise roles/bindings in manifests and apply them, re-run:
kubectl get clusterroles -o json | jq -r '
.items[]
| select(.rules[]? | any(.resources[]?; . == "pods") and any(.verbs[]?; . == "create" or . == "*"))
| .metadata.name
' | sort | uniq
kubectl get roles --all-namespaces -o json | jq -r '
.items[]
| select(.rules[]? | any(.resources[]?; . == "pods") and any(.verbs[]?; . == "create" or . == "*"))
| [.metadata.namespace, .metadata.name] | @tsv
' | sort | uniq
Your goal is to ensure only those roles that genuinely require pod creation (and their intended subjects) still appear in these lists.
Automation
#!/usr/bin/env bash
# Report who can create pods across the cluster and where
set -euo pipefail
echo "=== Cluster-wide and namespace-specific pod CREATE permissions ==="
echo
# 1) Show all Roles/ClusterRoles that grant create on pods (any API group)
echo "-> Roles and ClusterRoles that grant create on pods"
kubectl get clusterrole,role -A -o json \
| jq -r '
.items[]
| {
kind,
name: .metadata.name,
namespace: (.metadata.namespace // "-"),
rules: (.rules // [])
}
| select(
any(.rules[]?; any(.resources[]?; . == "pods")
and any(.verbs[]?; . == "create")
)
)
| "\(.kind)\t\(.namespace)\t\(.name)"
' \
| sort -u \
|| echo "Failed to inspect roles; ensure jq is installed."
echo
echo "Columns: KIND<TAB>NAMESPACE<TAB>NAME"
echo
# 2) For each subject bound to these Roles/ClusterRoles, show bindings
echo "-> RoleBindings and ClusterRoleBindings that attach those permissions"
# Collect names of roles/clusterroles with pod create
role_list=$(kubectl get clusterrole,role -A -o json \
| jq -r '
.items[]
| {
kind,
name: .metadata.name,
namespace: (.metadata.namespace // ""),
rules: (.rules // [])
}
| select(
any(.rules[]?; any(.resources[]?; . == "pods")
and any(.verbs[]?; . == "create")
)
)
| if .kind == "ClusterRole" then .kind + "/" + .name
else .kind + "/" + .namespace + "/" + .name
end
' | sort -u)
if [ -z "${role_list}" ]; then
echo "No Roles or ClusterRoles grant create on pods."
else
echo -e "BINDING_KIND\tBINDING_NAMESPACE\tBINDING_NAME\tROLE_KIND\tROLE_NAMESPACE\tROLE_NAME\tSUBJECT_KIND\tSUBJECT_NAME\tSUBJECT_NAMESPACE"
# Iterate over all bindings and print only those that reference the roles above
kubectl get clusterrolebinding,rolebinding -A -o json \
| jq -r --argjson roles "$(printf '%s\n' "$role_list" | jq -R . | jq -s .)" '
.items[]
| {
bkind: .kind,
bname: .metadata.name,
bns: (.metadata.namespace // "-"),
subjects: (.subjects // []),
rkind: (if .roleRef.kind == "ClusterRole" then "ClusterRole" else "Role" end),
rname: .roleRef.name,
rns: (if .kind == "RoleBinding" then .metadata.namespace else "" end)
}
| . as $b
| (
if $b.rkind == "ClusterRole" then
($b.rkind + "/" + $b.rname)
else
($b.rkind + "/" + $b.rns + "/" + $b.rname)
end
) as $ref
| select($roles | index($ref))
| .subjects[]
| [
$b.bkind,
$b.bns,
$b.bname,
$b.rkind,
($b.rns | if . == "" then "-" else . end),
$b.rname,
.kind,
.name,
(.namespace // "-")
]
| @tsv
' | sort -u
fi
echo
echo "=== Interpretation ==="
cat <<'EOF'
Each row shows a subject (user, group, or serviceaccount) that can create pods:
- BINDING_KIND/BINDING_NAMESPACE/BINDING_NAME:
Which RoleBinding or ClusterRoleBinding grants the permission.
- ROLE_*:
The Role/ClusterRole that contains 'create' on 'pods'.
- SUBJECT_KIND/SUBJECT_NAME/SUBJECT_NAMESPACE:
Who receives this permission.
Potential problems you should review:
- ClusterRoleBindings (BINDING_KIND=ClusterRoleBinding) granting pod create to:
- broad groups (e.g. system:authenticated, system:masters, developers, *),
- generic users (e.g. admin, ci, default),
- or service accounts in many namespaces.
- Any Role/ClusterRole whose rules are overly broad (e.g. verbs=["*"] or resources=["*"])
that appear here, as they implicitly allow creating pods.
- Subjects that do not need to create pods operationally but still appear in this list.
No automatic change is made by this script; use this report to decide where to tighten RBAC.
EOF