> ## 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 ClusterRoleBindings (any machine with kubectl access)
           ```sh theme={null}
           { kubectl get clusterroles,roles --all-namespaces -o json
             kubectl get clusterrolebindings,rolebindings --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, namespace: ($m.namespace // ""), name: $m.name, roleRef: $ref, subject: . }
             ]'
           ```
           Review the JSON output and list all bindings where `subject.name` is one of the “broad” subjects. Decide which bindings should be changed, and who the legitimate human operators are.

        2. Inspect each flagged binding to understand its scope and usage (any machine with kubectl access)\
           Replace the names below with each binding from step 1:
           ```sh theme={null}
           # For a ClusterRoleBinding
           kubectl get clusterrolebinding <binding-name> -o yaml

           # For a RoleBinding (if any are flagged)
           kubectl get rolebinding <binding-name> -n <namespace> -o yaml
           ```
           Confirm that the `roleRef` indeed needs `pods/exec` and determine the specific users or service accounts that should retain this ability.

        3. Remove broad subjects from the binding (any machine with kubectl access)\
           For each flagged binding, edit it and remove any `subjects` entries with
           `name: system:authenticated`, `system:unauthenticated`, `system:anonymous`, or `system:serviceaccounts`. Keep or add only specific named users or a single, purpose-built ServiceAccount.

           ```sh theme={null}
           # ClusterRoleBinding
           kubectl edit clusterrolebinding <binding-name>

           # RoleBinding (if present)
           kubectl edit rolebinding <binding-name> -n <namespace>
           ```

           In the editor, modify the `subjects:` list. Example before:

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

           Example after (replace with your real identities):

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

           Save and exit to apply changes.

        4. (Optional) Split shared bindings into dedicated ones (any machine with kubectl access)\
           If a binding currently mixes broad subjects and specific operators, it may be clearer to:
           * Remove the broad subject from the existing binding (as in step 3), and
           * Create a dedicated binding for the specific operator(s), if one does not already exist:
           ```sh theme={null}
           kubectl create clusterrolebinding <new-binding-name> \
             --clusterrole=<exec-role-name> \
             --user=<human-operator-identity>
           ```
           Adjust `--user` or use `--serviceaccount=<namespace>:<sa-name>` as appropriate.

        5. (Optional) Tighten the ClusterRole itself if it is overly broad (any machine with kubectl access)\
           If a ClusterRole grants `create` on `pods/exec` (or `*` resources/verbs) more broadly than required, refine it:
           ```sh theme={null}
           kubectl edit clusterrole <clusterrole-name>
           ```
           In the `rules:` section, restrict `resources` and `verbs` to the minimum necessary. For example, avoid `resources: ["*"]` or `verbs: ["*"]` if not strictly required. If possible, move `pods/exec` into a separate, tightly bound ClusterRole used only by the small set of human operators.

        6. Verify remediation (any machine with kubectl access)\
           Re-run the audit and ensure it reports `is_compliant=true` or no offending rows:
           ```sh 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="Using kubectl">
        On any machine with kubectl access:

        1. Identify the offending binding and role

        ```bash theme={null}
        kubectl get clusterrolebindings.rbac.authorization.k8s.io \
          -o wide | grep -i pods-exec || true

        kubectl describe clusterrolebindings.rbac.authorization.k8s.io <BINDING_NAME>
        kubectl describe clusterroles.rbac.authorization.k8s.io <CLUSTERROLE_NAME>
        ```

        2. Remove broad subjects from the binding

        Edit the binding to remove any of these subjects:

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

        ```bash theme={null}
        kubectl edit clusterrolebinding.rbac.authorization.k8s.io <BINDING_NAME>
        ```

        In the `subjects:` list, delete any entries whose `name` is one of the above. Save and exit.

        Alternatively, patch to remove a specific subject (example for system:authenticated):

        ```bash theme={null}
        kubectl get clusterrolebinding <BINDING_NAME> -o yaml > /tmp/crb.yaml

        # edit /tmp/crb.yaml and remove undesired subjects from spec.subjects

        kubectl apply -f /tmp/crb.yaml
        ```

        3. Optionally, create a restricted ClusterRoleBinding for named human operators only

        Prepare a manifest like:

        ```yaml theme={null}
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
          name: pods-exec-operators
        roleRef:
          apiGroup: rbac.authorization.k8s.io
          kind: ClusterRole
          name: <EXISTING_POD_EXEC_CLUSTERROLE_NAME>
        subjects:
          - kind: User
            name: alice@example.com
            apiGroup: rbac.authorization.k8s.io
          - kind: User
            name: bob@example.com
            apiGroup: rbac.authorization.k8s.io
        ```

        Apply it:

        ```bash theme={null}
        kubectl apply -f pods-exec-operators.yaml
        ```

        4. Verification (same logic as the audit)

        ```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
        # Purpose: Review and narrow broad pods/exec RBAC grants on OKE using kubectl.
        # Scope:   Run on any machine with kubectl access and current-context set.
        # Impact:  Changes to RoleBinding/ClusterRoleBinding take effect immediately.

        set -euo pipefail

        # --- Configuration ----------------------------------------------------------
        # Namespace where privileged human operators live (adjust if needed).
        PRIV_NS="kube-system"

        # Name of the ServiceAccount to which narrowed exec rights will be granted.
        # This SA will be created in $PRIV_NS if it does not exist.
        PRIV_SA="human-operators-exec"

        # Label to mark bindings that this script has already processed.
        MANAGED_LABEL_KEY="security.cloudanix.com/exec-rbac-managed"
        MANAGED_LABEL_VAL="true"

        # --- Preconditions ----------------------------------------------------------
        if ! command -v kubectl >/dev/null 2>&1; then
          echo "kubectl not found in PATH" >&2
          exit 1
        fi

        if ! command -v jq >/dev/null 2>&1; then
          echo "jq not found in PATH; install jq to use this script" >&2
          exit 1
        fi

        echo "Using kubectl context: $(kubectl config current-context)"

        # --- Ensure privileged ServiceAccount exists -------------------------------
        echo "Ensuring privileged ServiceAccount ${PRIV_SA} exists in namespace ${PRIV_NS}..."

        if ! kubectl get namespace "${PRIV_NS}" >/dev/null 2>&1; then
          echo "Namespace ${PRIV_NS} does not exist; creating..."
          kubectl create namespace "${PRIV_NS}"
        fi

        if ! kubectl get sa "${PRIV_SA}" -n "${PRIV_NS}" >/dev/null 2>&1; then
          echo "Creating ServiceAccount ${PRIV_SA} in namespace ${PRIV_NS}..."
          kubectl create sa "${PRIV_SA}" -n "${PRIV_NS}"
        else
          echo "ServiceAccount ${PRIV_SA} already exists in ${PRIV_NS}."
        fi

        # --- Find broad exec-granting bindings -------------------------------------
        echo "Discovering RoleBindings/ClusterRoleBindings that grant pods/exec to broad subjects..."

        TMP_DIR="$(mktemp -d)"
        trap 'rm -rf "${TMP_DIR}"' EXIT

        # Get all roles/clusterroles and bindings JSON
        kubectl get roles,clusterroles --all-namespaces -o json > "${TMP_DIR}/roles.json"
        kubectl get rolebindings,clusterrolebindings --all-namespaces -o json > "${TMP_DIR}/bindings.json"

        # Produce JSON list of offending bindings with enough info to patch
        jq -n -f /dev/stdin --slurpfile roles "${TMP_DIR}/roles.json" --slurpfile binds "${TMP_DIR}/bindings.json" <<'JQ' > "${TMP_DIR}/offenders.json"
          ($roles[0]) as $roles | ($binds[0]) as $b |
          def broad: ["system:authenticated","system:unauthenticated","system:anonymous","system:serviceaccounts"];
          # First, roles/clusterroles that can create pods/exec (or *)
          [ $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
          |
          # Now, bindings that reference those roles and include broad subjects
          [ $b.items[]
            | .kind as $kind | .metadata as $m | .roleRef as $ref
            | .subjects as $subjects
            | select(any($execRoles[];
                .name == $ref.name and .kind == $ref.kind
                and (.ns == "" or .ns == ($m.namespace // ""))))
            | ($subjects // []) as $subjs
            | [ $subjs[] | select(.name as $n | broad | index($n)) ] as $broadSubs
            | select($broadSubs | length > 0)
            | {
                kind: $kind,
                name: $m.name,
                namespace: ($m.namespace // null),
                apiVersion: $m.apiVersion,
                uid: $m.uid,
                roleRef: $ref,
                broadSubjects: $broadSubs,
                allSubjects: $subjs
              }
          ]
        JQ

        if [[ ! -s "${TMP_DIR}/offenders.json" || "$(jq '. | length' "${TMP_DIR}/offenders.json")" -eq 0 ]]; then
          echo "No broad pods/exec grants detected; cluster appears compliant."
          echo "Re-running verification command from control:"
          { 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'
          exit 0
        fi

        echo "Found $(jq '. | length' "${TMP_DIR}/offenders.json") offending bindings."

        # --- Process each offending binding ----------------------------------------
        jq -c '.[]' "${TMP_DIR}/offenders.json" | while read -r item; do
          KIND="$(echo "${item}" | jq -r '.kind')"
          NAME="$(echo "${item}" | jq -r '.name')"
          NS="$(echo "${item}"   | jq -r '.namespace // ""')"

          if [[ -n "${NS}" ]]; then
            FULL="${KIND}/${NS}/${NAME}"
          else
            FULL="${KIND}/${NAME}"
          fi

          echo "Processing ${FULL}..."

          # Skip if already labeled as managed
          if [[ -n "${NS}" ]]; then
            MANAGED_VAL="$(kubectl get "${KIND}" "${NAME}" -n "${NS}" -o jsonpath="{.metadata.labels.${MANAGED_LABEL_KEY}}" 2>/dev/null || true)"
          else
            MANAGED_VAL="$(kubectl get "${KIND}" "${NAME}" -o jsonpath="{.metadata.labels.${MANAGED_LABEL_KEY}}" 2>/dev/null || true)"
          fi

          if [[ "${MANAGED_VAL}" == "${MANAGED_LABEL_VAL}" ]]; then
            echo "  Already managed by this script; skipping."
            continue
          fi

          # Build new subjects: remove broad subjects, add the privileged SA if not present
          ALL_SUBJECTS="$(echo "${item}" | jq '.allSubjects')"
          if [[ "${ALL_SUBJECTS}" == "null" ]]; then
            ALL_SUBJECTS="[]"
          fi

          NEW_SUBJECTS="$(echo "${ALL_SUBJECTS}" | jq --arg ns "${PRIV_NS}" --arg sa "${PRIV_SA}" '
            def broad: ["system:authenticated","system:unauthenticated","system:anonymous","system:serviceaccounts"];
            # Filter out broad subjects
            [ .[] | select(.name as $n | (broad | index($n) | not)) ] as $filtered
            |
            # Ensure the privileged SA is present
            if any($filtered[]?; .kind == "ServiceAccount" and .name == $sa and (.namespace // "") == $ns) then
              $filtered
            else
              $filtered + [{ "kind": "ServiceAccount", "name": $sa, "namespace": $ns }]
            end
          ')"

          # Prepare a patch file
          PATCH_FILE="${TMP_DIR}/patch-${KIND}-${NS}-${NAME}.yaml"
          cat > "${PATCH_FILE}" <<EOF
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ${KIND}
        metadata:
          name: ${NAME}
        $( [[ -n "${NS}" ]] && echo "  namespace: ${NS}" )
          labels:
            ${MANAGED_LABEL_KEY}: "${MANAGED_LABEL_VAL}"
        subjects: $(echo "${NEW_SUBJECTS}" | jq -c '.')
        EOF

          echo "  Applying narrowed-subjects patch..."
          if [[ -n "${NS}" ]]; then
            kubectl apply -f "${PATCH_FILE}" -n "${NS}"
          else
            kubectl apply -f "${PATCH_FILE}"
          fi
        done

        # --- Verification -----------------------------------------------------------
        echo "Re-running verification command to confirm no broad pods/exec grants remain..."

        { 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'

        echo "Review the output above. 'is_compliant=true' indicates the broad pods/exec grants have been removed."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
