Skip to main content

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

Manual Steps
  1. 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 ClusterRole and Role objects that include:
      apiGroups: [""]
      resources: ["pods"]
      verbs: ["create"] # or ["*"], or any list that includes "create"
  2. Map roles/clusterroles with pod create to subjects and usage

    • For each Role/ClusterRole identified in step 1, list who is bound to it:
      # Cluster-wide
      kubectl get clusterrolebindings -o yaml \
      | grep -nE '^(kind: ClusterRoleBinding|name:|roleRef:|subjects:)' -n

      # Per-namespace
      for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
      echo "### Namespace: $ns"
      kubectl get rolebindings -n "$ns" -o yaml \
      | grep -nE '^(kind: RoleBinding|name:|roleRef:|subjects:)' -n
      done
    • Correlate roleRef.name with the roles/clusterroles that grant pods/create, and list the subjects (users, groups, serviceaccounts) for each.
  3. 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).
  4. Adjust over-privileged roles/clusterroles (edit manifests or live objects)

    • For each over-privileged role/clusterrole, remove create (and, if possible, wildcard *) for pods from 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 place
        kubectl edit clusterrole <clusterrole-name>
        # or edit a namespaced Role
        kubectl edit role <role-name> -n <namespace>
    • In the opened editor, adjust entries like:
      - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list", "watch", "create"]
      to remove create, and avoid using verbs: ["*"] where not strictly required.
  5. 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):
        # ClusterRoleBinding
        kubectl delete clusterrolebinding <binding-name>

        # Namespaced RoleBinding
        kubectl delete rolebinding <binding-name> -n <namespace>
    • Ensure any replacement roles you use have been verified not to include pods/create under any rule.
  6. 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 pods
      kubectl auth can-i create pods \
      --as=system:serviceaccount:<namespace>:<serviceaccount-name> \
      --namespace <namespace>
    • Confirm that kubectl auth can-i create pods returns no for all users/groups/service accounts that should not create pods.
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 ClusterRole or Role in 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: ["*"] or verbs: ["*"] include pod creation implicitly.
    • Roles in sensitive namespaces (e.g., kube-system) or used by untrusted teams.

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:

  • resources includes pods or "*" and verbs includes create or "*".
  • Pod creation permissions not limited by resourceNames or 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:authenticated or system:serviceaccounts are 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 yes for 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