> ## 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.

# 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

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify the offending bindings and roles (any machine with kubectl access)
           ```bash theme={null}
           kubectl get rolebindings,clusterrolebindings --all-namespaces -o wide
           kubectl get roles,clusterroles --all-namespaces -o yaml | grep -n "pods/exec" -n
           ```

        2. Inspect a flagged binding and its role (any machine with kubectl access)\
           Replace the names from the finding/audit output.
           ```bash theme={null}
           # Inspect the binding that uses broad subjects
           kubectl get clusterrolebinding <BINDING_NAME> -o yaml

           # Inspect the referenced ClusterRole to confirm pods/exec create permissions
           kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml
           ```

        3. Decide how pods/exec should be used (manual review)
           * Determine which specific human users or a small set of named service accounts truly need `kubectl exec`.
           * For any broad subject (`system:authenticated`, `system:unauthenticated`, `system:anonymous`, `system:serviceaccounts`), decide whether:
             * exec should be removed entirely for that binding, or
             * the binding should be replaced by a binding to specific users/groups/service accounts.

        4. Restrict or remove the broad binding (any machine with kubectl access)\
           Example: edit an existing ClusterRoleBinding to remove broad subjects and add specific ones.
           ```bash theme={null}
           kubectl edit clusterrolebinding <BINDING_NAME>
           ```
           In your editor:
           * Under `subjects:`, delete entries where `name` is any of:\
             `system:authenticated`, `system:unauthenticated`, `system:anonymous`, `system:serviceaccounts`.
           * Optionally add tightly scoped subjects such as:
             ```yaml theme={null}
             subjects:
               - kind: User
                 apiGroup: rbac.authorization.k8s.io
                 name: alice@example.com
               - kind: ServiceAccount
                 name: exec-operator
                 namespace: ops
             ```
           * Save and exit to apply.

        5. Optionally narrow the ClusterRole itself (any machine with kubectl access)\
           If the ClusterRole grants more than needed (e.g., `"resources: ["*"]` or `verbs: ["*"]`), edit it:
           ```bash theme={null}
           kubectl edit clusterrole <CLUSTERROLE_NAME>
           ```
           In your editor, change the relevant rule to something like:
           ```yaml theme={null}
           rules:
             - apiGroups: [""]
               resources: ["pods/exec"]
               verbs: ["create"]
           ```
           or remove `pods/exec` from `resources` entirely if exec is no longer required.

        6. Verify the remediation (any machine with kubectl access)
           ```bash theme={null}
           { kubectl get roles,clusterroles --all-namespaces -o json
             kubectl get rolebindings,clusterrolebindings --all-namespaces -o json
           } | jq -rs '
             .[0] as $roles | .[1] |
             def broad: ["system:authenticated","system:unauthenticated","system:anonymous","system:serviceaccounts"];
             [ $roles.items[]
               | select(any(.rules[]?;
                   (any(.resources[]?; . == "pods/exec" or . == "*"))
                   and (any(.verbs[]?; . == "create" or . == "*"))))
               | { kind: .kind, ns: (.metadata.namespace // ""), name: .metadata.name } ] as $execRoles
             | [ .items[]
               | .kind as $kind | .metadata as $m | .roleRef as $ref
               | select(any($execRoles[];
                   .name == $ref.name and .kind == $ref.kind
                   and (.ns == "" or .ns == ($m.namespace // ""))))
               | ((.subjects // [])[] | select(.name as $n | broad | index($n)))
               | "kind=\($kind)"
                 + (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)
                 + " name=\($m.name) uid=\($m.uid) apiVersion=rbac.authorization.k8s.io/v1"
                 + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
                 + " subject=\(.name) roleRef=\($ref.kind)/\($ref.name) grants=pods/exec is_compliant=false"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
           Confirm the output is `is_compliant=true` and no bindings with broad subjects remain for `pods/exec`.
      </Accordion>

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

        1. Identify the offending bindings and roles

        ```bash theme={null}
        kubectl get clusterrolebindings,rolebindings --all-namespaces -o wide
        kubectl get clusterroles,roles --all-namespaces -o yaml | grep -E "name: pods/exec|\- \\\*" -n
        ```

        Focus on bindings where `roleRef` points to a Role/ClusterRole that allows `create` on `pods/exec` and subjects include any of:

        * `system:authenticated`
        * `system:unauthenticated`
        * `system:anonymous`
        * `system:serviceaccounts`

        2. Remove broad subjects from the binding

        For each offending binding, edit it to remove the broad subject and (optionally) replace it with specific named users/groups.

        ClusterRoleBinding example (cluster‑wide):

        ```bash theme={null}
        kubectl edit clusterrolebinding <binding-name>
        ```

        In the editor, change e.g.:

        ```yaml theme={null}
        subjects:
        - kind: Group
          name: system:authenticated
          apiGroup: rbac.authorization.k8s.io
        ```

        to something like:

        ```yaml theme={null}
        subjects:
        - kind: User
          name: alice@example.com
          apiGroup: rbac.authorization.k8s.io
        - kind: User
          name: bob@example.com
          apiGroup: rbac.authorization.k8s.io
        ```

        or remove the `subjects` entry entirely if no one should have `pods/exec` via this binding.

        RoleBinding example (namespace‑scoped):

        ```bash theme={null}
        kubectl edit rolebinding <binding-name> -n <namespace>
        ```

        Adjust the `subjects` section the same way: eliminate `system:authenticated`, `system:unauthenticated`, `system:anonymous`, or `system:serviceaccounts`, keeping only specific users or a single, named service account if absolutely required.

        3. (Optional) Split out a least‑privilege exec role

        If a broad ClusterRole currently contains `pods/exec` among many other permissions, consider:

        ```bash theme={null}
        kubectl get clusterrole <broad-role-name> -o yaml > /tmp/exec-role.yaml
        ```

        Edit `/tmp/exec-role.yaml` to:

        * Change `metadata.name` to a new, restrictive name, e.g. `exec-operators-only`.
        * Remove all rules except the minimal exec rule, for example:

        ```yaml theme={null}
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRole
        metadata:
          name: exec-operators-only
        rules:
        - apiGroups: [""]
          resources: ["pods/exec"]
          verbs: ["create"]
        ```

        Apply it:

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

        Then create a dedicated binding for named human operators only:

        ```bash theme={null}
        cat <<'EOF' | kubectl apply -f -
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
          name: exec-operators-only-binding
        roleRef:
          apiGroup: rbac.authorization.k8s.io
          kind: ClusterRole
          name: exec-operators-only
        subjects:
        - kind: User
          name: alice@example.com
          apiGroup: rbac.authorization.k8s.io
        - kind: User
          name: bob@example.com
          apiGroup: rbac.authorization.k8s.io
        EOF
        ```

        4. Verification

        Run the provided audit on any machine with kubectl access:

        ```bash theme={null}
        { kubectl get roles,clusterroles --all-namespaces -o json
          kubectl get rolebindings,clusterrolebindings --all-namespaces -o json
        } | jq -rs '
          .[0] as $roles | .[1] |
          def broad: ["system:authenticated","system:unauthenticated","system:anonymous","system:serviceaccounts"];
          [ $roles.items[]
            | select(any(.rules[]?;
                (any(.resources[]?; . == "pods/exec" or . == "*"))
                and (any(.verbs[]?; . == "create" or . == "*"))))
            | { kind: .kind, ns: (.metadata.namespace // ""), name: .metadata.name } ] as $execRoles
          | [ .items[]
            | .kind as $kind | .metadata as $m | .roleRef as $ref
            | select(any($execRoles[];
                .name == $ref.name and .kind == $ref.kind
                and (.ns == "" or .ns == ($m.namespace // ""))))
            | ((.subjects // [])[] | select(.name as $n | broad | index($n)))
            | "kind=\($kind)"
              + (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)
              + " name=\($m.name) uid=\($m.uid) apiVersion=rbac.authorization.k8s.io/v1"
              + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
              + " subject=\(.name) roleRef=\($ref.kind)/\($ref.name) grants=pods/exec is_compliant=false"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Restrict broad subjects from Roles/ClusterRoles that grant create on pods/exec.
        # Platform: Amazon EKS
        # Runs on: any machine with kubectl access and jq installed.
        #
        # Behavior:
        # - Detects RoleBindings/ClusterRoleBindings whose role grants create on pods/exec
        #   and which are bound to any of:
        #     system:authenticated, system:unauthenticated, system:anonymous, system:serviceaccounts
        # - Removes ONLY those broad subjects from the binding, leaving other subjects intact.
        # - Safe to re-run: once subjects are removed, no further changes are made.
        # - Prints a final compliance summary using the benchmark audit logic.

        set -euo pipefail

        # Ensure required tools
        command -v kubectl >/dev/null 2>&1 || { echo "kubectl not found in PATH" >&2; exit 1; }
        command -v jq >/dev/null 2>&1 || { echo "jq not found in PATH" >&2; exit 1; }

        BROAD_SUBJECTS='["system:authenticated","system:unauthenticated","system:anonymous","system:serviceaccounts"]'

        echo "Discovering Roles and ClusterRoles that grant create on pods/exec..."

        # Build JSON with exec-capable roles/clusterroles and all bindings
        COMBINED_JSON="$( { kubectl get roles,clusterroles --all-namespaces -o json ; kubectl get rolebindings,clusterrolebindings --all-namespaces -o json ; } )"

        # Extract exec-capable roles/clusterroles
        EXEC_ROLES_JSON="$(printf '%s\n' "$COMBINED_JSON" | jq -rs '
          .[0].items[]
          | select(any(.rules[]?;
              (any(.resources[]?; . == "pods/exec" or . == "*"))
              and (any(.verbs[]?; . == "create" or . == "*"))))
          | { kind: .kind,
              ns: (.metadata.namespace // ""),
              name: .metadata.name }')"

        if [ -z "$EXEC_ROLES_JSON" ] || [ "$EXEC_ROLES_JSON" = "null" ]; then
          echo "No Roles/ClusterRoles grant create on pods/exec; nothing to change."
        else
          echo "Identifying RoleBindings/ClusterRoleBindings with broad subjects bound to exec-capable roles..."

          BINDINGS_JSON="$(printf '%s\n' "$COMBINED_JSON" | jq -rs --argjson execRoles "$EXEC_ROLES_JSON" --argjson broad "$BROAD_SUBJECTS" '
            .[1].items
            | map(
                . as $b
                | .kind as $kind
                | .metadata as $m
                | .roleRef as $ref
                | select(any($execRoles[];
                    .name == $ref.name and .kind == $ref.kind
                    and (.ns == "" or .ns == ($m.namespace // ""))))
                | .subjects // [] as $subjects
                | $subjects
                  | map(select(.name as $n | $broad | index($n))) as $broadSubjects
                | select($broadSubjects | length > 0)
                | {
                    kind: $kind,
                    namespace: ($m.namespace // ""),
                    name: $m.name,
                    apiVersion: "rbac.authorization.k8s.io/v1",
                    uid: $m.uid,
                    roleRefKind: $ref.kind,
                    roleRefName: $ref.name,
                    broadSubjects: $broadSubjects
                  }
              )
          ')"

          if [ -z "$BINDINGS_JSON" ] || [ "$BINDINGS_JSON" = "null" ] || [ "$(printf '%s' "$BINDINGS_JSON" | jq 'length')" -eq 0 ]; then
            echo "No RoleBindings/ClusterRoleBindings bind broad subjects to exec-capable roles; nothing to change."
          else
            echo "The following bindings will have broad subjects removed:"
            printf '%s\n' "$BINDINGS_JSON" | jq -r '
              .[]
              | "kind=\(.kind)"
                + (if .namespace == "" then "" else " ns=\(.namespace)" end)
                + " name=\(.name) roleRef=\(.roleRefKind)/\(.roleRefName)"
                + " removing_subjects=\([.broadSubjects[].name] | join(","))"
            '

            # Iterate and patch each binding
            MAPFILE=()
            while IFS= read -r line; do
              MAPFILE+=("$line")
            done < <(printf '%s\n' "$BINDINGS_JSON" | jq -c '.[]')

            for item in "${MAPFILE[@]}"; do
              kind=$(printf '%s\n' "$item" | jq -r '.kind')
              ns=$(printf '%s\n' "$item" | jq -r '.namespace')
              name=$(printf '%s\n' "$item" | jq -r '.name')
              # Build a subjects list excluding broad subjects
              patch_subjects=$(kubectl get "$kind" "$name" ${ns:+-n "$ns"} -o json \
                | jq --argjson broad "$BROAD_SUBJECTS" '
                    .subjects // []
                    | map(select(.name as $n | $broad | index($n) | not))
                  ')

              # Construct full patch; if resulting subjects list is empty, set subjects: []
              patch_json=$(jq -n --argjson subs "$patch_subjects" '{ "subjects": $subs }')

              echo "Patching $kind ${ns:+namespace=$ns }name=$name to remove broad subjects..."
              kubectl patch "$kind" "$name" ${ns:+-n "$ns"} --type=merge -p "$patch_json" 1>/dev/null
            done
          fi
        fi

        echo "Re-running compliance check..."

        # Verification: run the benchmark audit logic
        { kubectl get roles,clusterroles --all-namespaces -o json
          kubectl get rolebindings,clusterrolebindings --all-namespaces -o json
        } | jq -rs '
          .[0] as $roles | .[1] |
          def broad: ["system:authenticated","system:unauthenticated","system:anonymous","system:serviceaccounts"];
          [ $roles.items[]
            | select(any(.rules[]?;
                (any(.resources[]?; . == "pods/exec" or . == "*"))
                and (any(.verbs[]?; . == "create" or . == "*"))))
            | { kind: .kind, ns: (.metadata.namespace // ""), name: .metadata.name } ] as $execRoles
          | [ .items[]
            | .kind as $kind | .metadata as $m | .roleRef as $ref
            | select(any($execRoles[];
                .name == $ref.name and .kind == $ref.kind
                and (.ns == "" or .ns == ($m.namespace // ""))))
            | ((.subjects // [])[] | select(.name as $n | broad | index($n)))
            | "kind=\($kind)"
              + (if ($m.namespace // "") == "" then "" else " ns=\($m.namespace)" end)
              + " name=\($m.name) uid=\($m.uid) apiVersion=rbac.authorization.k8s.io/v1"
              + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
              + " subject=\(.name) roleRef=\($ref.kind)/\($ref.name) grants=pods/exec is_compliant=false"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
