> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Minimize Access Create Pods

### More Info:

The ability to create pods in a namespace can provide a number of opportunities for privilege escalation, such as assigning privileged service accounts to these pods or mounting hostPaths with access to sensitive data (unless Pod Security Policies are implemented to restrict this access)

### 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 EKS
* 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

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. List all roles/clusterroles that grant `create` on pods (run on any machine with kubectl access):
           ```sh theme={null}
           kubectl get roles,clusterroles -A -o json | jq '
             .items[]
             | select(.rules[]? | (.resources[]? == "pods" and .verbs[]? == "create"))
             | {kind, name:.metadata.name, namespace:.metadata.namespace, rules:.rules}'
           ```

        2. For each **Role** that should not allow pod creation, edit it to remove `create` from any rule that includes `pods` (run on any machine with kubectl):
           ```sh theme={null}
           # Example for a namespaced Role
           kubectl -n <NAMESPACE> edit role <ROLE_NAME>
           ```
           In the editor, find any rule like:
           ```yaml theme={null}
           - apiGroups: [""]
             resources: ["pods"]
             verbs: ["get","list","watch","create"]
           ```
           Change to:
           ```yaml theme={null}
           - apiGroups: [""]
             resources: ["pods"]
             verbs: ["get","list","watch"]
           ```
           or remove the entire rule if `pods` access is not needed, then save and exit.

        3. For each **ClusterRole** that should not allow pod creation, edit it similarly (run on any machine with kubectl):
           ```sh theme={null}
           kubectl edit clusterrole <CLUSTERROLE_NAME>
           ```
           In the editor, remove `create` from any verbs list that applies to `pods`, or remove the `pods` rule entirely, then save and exit.

        4. For any Role/ClusterRole that must still allow **some** users to create pods, consider creating a separate, narrowly-scoped role for them and binding it only to the required subjects (run on any machine with kubectl):
           ```sh theme={null}
           # Example: create a dedicated role allowing pod create only in one namespace
           kubectl -n <NAMESPACE> apply -f - <<'EOF'
           apiVersion: rbac.authorization.k8s.io/v1
           kind: Role
           metadata:
             name: pod-creator-limited
           rules:
           - apiGroups: [""]
             resources: ["pods"]
             verbs: ["create"]
           EOF

           # Bind it to a specific user/group/serviceaccount
           kubectl -n <NAMESPACE> apply -f - <<'EOF'
           apiVersion: rbac.authorization.k8s.io/v1
           kind: RoleBinding
           metadata:
             name: pod-creator-limited-binding
           subjects:
           - kind: User
             name: example-user
           roleRef:
             apiGroup: rbac.authorization.k8s.io
             kind: Role
             name: pod-creator-limited
           EOF
           ```

        5. Review RoleBindings and ClusterRoleBindings to ensure only intended subjects are linked to any remaining roles/clusterroles that have `pods` `create` (run on any machine with kubectl):
           ```sh theme={null}
           kubectl get rolebindings,clusterrolebindings -A -o yaml | less
           ```
           Adjust bindings as needed:
           ```sh theme={null}
           kubectl -n <NAMESPACE> edit rolebinding <ROLEBINDING_NAME>
           kubectl edit clusterrolebinding <CLUSTERROLEBINDING_NAME>
           ```

        6. Verification (run on any machine with kubectl access):
           ```sh theme={null}
           access=$(kubectl get roles,clusterroles -A -o json | jq '
             [.items[] |
               select(
                 .rules[]? |
                 (.resources[]? == "pods" and .verbs[]? == "create")
               )
             ] | length')

           if [ "$access" -gt 0 ]; then
             echo "pods_create_access"
           else
             echo "no_pods_create_access_remaining"
           fi
           ```
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with kubectl access:

        1. List all Roles/ClusterRoles that can create pods

        ```bash theme={null}
        kubectl get roles,clusterroles -A -o json | jq -r '
          .items[]
          | select(
              .rules[]? |
              (.resources[]? == "pods" and .verbs[]? == "create")
            )
          | (.kind + "/" + .metadata.namespace + ":" + .metadata.name)
        '
        ```

        2. For each Role in a namespace where pod creation should be removed, edit it to drop `create` on `pods`:

        Example: remove `create` for pods from a specific Role

        ```bash theme={null}
        # View the Role YAML
        kubectl -n <NAMESPACE> get role <ROLE_NAME> -o yaml > /tmp/role-<ROLE_NAME>.yaml
        ```

        Edit `/tmp/role-<ROLE_NAME>.yaml`:

        * In every `.rules[]` where `resources` includes `pods`, remove `create` from the `verbs` list.
        * If `verbs` becomes empty, remove that rule entry entirely.

        Apply the updated Role:

        ```bash theme={null}
        kubectl apply -f /tmp/role-<ROLE_NAME>.yaml
        ```

        3. For each ClusterRole where pod creation should be removed, do the same:

        ```bash theme={null}
        kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml > /tmp/clusterrole-<CLUSTERROLE_NAME>.yaml
        ```

        Edit `/tmp/clusterrole-<CLUSTERROLE_NAME>.yaml`:

        * In every `.rules[]` where `resources` includes `pods`, remove `create` from `verbs`, or delete the rule if it only provided `create` on pods.

        Apply the updated ClusterRole:

        ```bash theme={null}
        kubectl apply -f /tmp/clusterrole-<CLUSTERROLE_NAME>.yaml
        ```

        4. (Optional) If a role exists only to permit pod creation and is no longer needed, delete it:

        ```bash theme={null}
        kubectl -n <NAMESPACE> delete role <ROLE_NAME>
        kubectl delete clusterrole <CLUSTERROLE_NAME>
        ```

        5. Verify that no Roles/ClusterRoles retain `create` on pods:

        ```bash theme={null}
        access=$(kubectl get roles,clusterroles -N  -o json | jq '
          [.items[] |
            select(
              .rules[]? |
              (.resources[]? == "pods" and .verbs[]? == "create")
            )
          ] | length')

        if [ "$access" -eq 0 ]; then
          echo "OK: no pods create access in Roles/ClusterRoles"
        else
          echo "pods_create_access still present in $access role(s)"
        fi
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Minimize access to create pods (CISEKS 4.1.4)
        #
        # This script:
        #   - Lists all Roles/ClusterRoles that have "create" on "pods"
        #   - Interactively removes that specific permission from each role
        #   - Verifies that no roles remain with pods/create
        #
        # REQUIREMENTS:
        #   - Run on any machine with kubectl access and cluster-admin privileges
        #   - jq must be installed
        #

        set -euo pipefail

        # Namespace to store backups of modified roles
        BACKUP_NS="rbac-backup"

        echo "==> Ensuring backup namespace exists: ${BACKUP_NS}"
        kubectl get ns "${BACKUP_NS}" >/dev/null 2>&1 || kubectl create namespace "${BACKUP_NS}" >/dev/null

        echo "==> Detecting Roles and ClusterRoles with pods/create access"

        # Get all roles/clusterroles that match the condition
        matches_json=$(kubectl get roles,clusterroles -A -o json | jq '
          .items
          | map(
              select(
                .rules[]? as $r
                | ($r.resources[]? == "pods" and $r.verbs[]? == "create")
              )
            )')

        count=$(echo "${matches_json}" | jq 'length')

        if [ "${count}" -eq 0 ]; then
          echo "No Roles or ClusterRoles grant create on pods. Nothing to do."
        else
          echo "Found ${count} Roles/ClusterRoles with pods/create:"
          echo "${matches_json}" | jq -r '.[] | "\(.kind) \(.metadata.namespace // "-")/\(.metadata.name)"'
        fi

        # Function to strip only "create" verb from rules that target "pods"
        strip_pods_create() {
          jq '
            .rules |= map(
              if (.resources? // [] | index("pods")) != null and (.verbs? // [] | index("create")) != null then
                .verbs |= (map(select(. != "create")))
              else
                .
              end
            )
            # Drop any rules that end up with no verbs
            | .rules |= map(select((.verbs // []) | length > 0))
          '
        }

        # Process each matching role/clusterrole
        echo
        echo "==> Reviewing and updating roles"

        for i in $(seq 0 $((count - 1))); do
          item=$(echo "${matches_json}" | jq ".[$i]")
          kind=$(echo "${item}" | jq -r '.kind')
          name=$(echo "${item}" | jq -r '.metadata.name')
          ns=$(echo "${item}" | jq -r '.metadata.namespace // empty')

          if [ "${kind}" = "Role" ]; then
            fqname="Role/${ns}/${name}"
          else
            fqname="ClusterRole/${name}"
          fi

          echo
          echo "------------------------------------------------------------"
          echo "Processing ${fqname}"

          # Show current rules that include pods/create
          echo "Current rules including pods/create:"
          echo "${item}" | jq '.rules[] | select(.resources? // [] | index("pods")) | select(.verbs? // [] | index("create"))'

          # Skip system and well-known controller roles by default; these often need pods/create
          # You may adjust this list based on your environment.
          if [[ "${name}" =~ ^system: ]] || [[ "${name}" =~ controller ]] || [[ "${name}" =~ kubernetes-dashboard ]]; then
            echo "NOTE: ${fqname} appears to be a system or controller role."
            read -r -p "Skip modifying this role? [Y/n]: " ans
            ans=${ans:-Y}
            if [[ "${ans}" =~ ^[Yy]$ ]]; then
              echo "Skipping ${fqname}"
              continue
            fi
          fi

          read -r -p "Remove 'create' on 'pods' from this role? [y/N]: " confirm
          confirm=${confirm:-N}
          if [[ ! "${confirm}" =~ ^[Yy]$ ]]; then
            echo "User chose not to modify ${fqname}"
            continue
          fi

          # Backup current definition
          backup_name="${name}-$(date +%Y%m%d%H%M%S)"
          if [ "${kind}" = "Role" ]; then
            echo "Backing up ${fqname} to namespace ${BACKUP_NS} as role ${backup_name}"
            kubectl get role "${name}" -n "${ns}" -o yaml \
              | sed "s/^  namespace: .*/  namespace: ${BACKUP_NS}/" \
              | sed "s/^metadata:\$/metadata:\n  name: ${backup_name}/" \
              | kubectl apply -n "${BACKUP_NS}" -f -
          else
            echo "Backing up ${fqname} to namespace ${BACKUP_NS} as configmap ${backup_name}"
            kubectl get clusterrole "${name}" -o yaml \
              | kubectl create configmap "${backup_name}" -n "${BACKUP_NS}" --from-file=role.yaml=/dev/stdin --dry-run=client -o yaml \
              | kubectl apply -f -
          fi

          echo "Stripping pods/create from ${fqname} and applying updated role"

          if [ "${kind}" = "Role" ]; then
            kubectl get role "${name}" -n "${ns}" -o json \
              | strip_pods_create \
              | kubectl apply -n "${ns}" -f -
          else
            kubectl get clusterrole "${name}" -o json \
              | strip_pods_create \
              | kubectl apply -f -
          fi

          echo "Updated ${fqname}"
        done

        echo
        echo "==> Verification: re-running pods/create access check"

        access=$(kubectl get roles,clusterroles -A -o json | jq '
          [.items[] |
            select(
              .rules[]? |
              (.resources[]? == "pods" and .verbs[]? == "create")
            )
          ] | length')

        if [ "${access}" -gt 0 ]; then
          echo "pods_create_access still present in ${access} role(s). Review remaining roles manually."
          kubectl get roles,clusterroles -A -o json | jq -r '
            .items[]
            | select(
                .rules[]? |
                (.resources[]? == "pods" and .verbs[]? == "create")
              )
            | "\(.kind) \(.metadata.namespace // "-")/\(.metadata.name)"'
          exit 1
        else
          echo "SUCCESS: No Roles or ClusterRoles grant create on pods."
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
