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

# Ensure Cluster Admin Role Is Only Used Where Required

### More Info:

The RBAC role cluster-admin provides wide-ranging powers over the environment and should be used only where and when needed.

### Risk Level

High

### 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)
* NIS2 Directive
* NIST SP 800-171
* NYDFS 23 NYCRR 500
* SWIFT Customer Security Controls Framework
* Sarbanes-Oxley IT General Controls
* Securities and Exchange Board of India (SEBI) - Cloud Security Adoption Framework
* UK NCSC Cyber Assessment Framework

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. List all clusterrolebindings that grant `cluster-admin`
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get clusterrolebindings -o json | jq -r '
             .items[]
             | select(.roleRef.name == "cluster-admin")
             | .metadata.name
           ' | sort -u
           ```

        2. Inspect each binding’s subjects and note which are non-system users/groups
           * Run on: any machine with kubectl access
           ```bash theme={null}
           for crb in $(kubectl get clusterrolebindings -o json | jq -r '
             .items[]
             | select(.roleRef.name == "cluster-admin")
             | .metadata.name
           ' | sort -u); do
             echo "=== $crb ==="
             kubectl get clusterrolebinding "$crb" -o yaml
           done
           ```
           * For each binding, identify subjects where:
             * `kind` is not `Group`, or
             * `kind` is `Group` and `name` is neither `system:masters` nor `system:nodes`.

        3. For each subject that should not have full cluster-admin, design or select a less-privileged role
           * Run on: any machine with kubectl access
           * Example: create a namespace-scoped admin role (adjust `namespace` and rules as needed):
           ```bash theme={null}
           cat << 'EOF' | kubectl apply -f -
           apiVersion: rbac.authorization.k8s.io/v1
           kind: Role
           metadata:
             name: limited-admin
             namespace: default
           rules:
             - apiGroups: [""]
               resources: ["pods","services","configmaps"]
               verbs: ["get","list","watch","create","update","patch","delete"]
           EOF
           ```
           * Or create a `ClusterRole` if cluster-wide but still reduced permissions are required.

        4. Bind affected subjects to the new lower-privilege role/clusterrole
           * Run on: any machine with kubectl access
           * Example for a `Role` (namespace admin):
           ```bash theme={null}
           kubectl create rolebinding limited-admin-binding \
             --role=limited-admin \
             --user="alice@example.com" \
             --namespace=default
           ```
           * Example for a `ClusterRole`:
           ```bash theme={null}
           kubectl create clusterrolebinding limited-cluster-access \
             --clusterrole=limited-cluster-role \
             --user="alice@example.com"
           ```

        5. Remove unnecessary `cluster-admin` bindings once replacement access is in place
           * Run on: any machine with kubectl access
           * To delete a whole binding that is no longer needed:
           ```bash theme={null}
           kubectl delete clusterrolebinding <clusterrolebinding-name>
           ```
           * If a binding mixes system groups with non-system subjects, recreate it without the non-system subjects:
             ```bash theme={null}
             # backup
             kubectl get clusterrolebinding <name> -o yaml > /tmp/<name>.yaml
             # edit out unwanted subjects
             sed -i '/name: alice@example.com/,+2 d' /tmp/<name>.yaml
             # reapply
             kubectl delete clusterrolebinding <name>
             kubectl apply -f /tmp/<name>.yaml
             ```

        6. Verify that only allowed subjects retain `cluster-admin`
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get clusterrolebindings -o json | jq -r '
             .items[]
             | select(.roleRef.name == "cluster-admin")
             | .subjects[]?
             | select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes"))
             | "FOUND_CLUSTER_ADMIN_BINDING"
           ' || echo "NO_CLUSTER_ADMIN_BINDINGS"
           ```
           * Confirm the output is `NO_CLUSTER_ADMIN_BINDINGS` and no non-system subjects are reported.
      </Accordion>

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

        1. List all ClusterRoleBindings that grant `cluster-admin`

        ```sh theme={null}
        kubectl get clusterrolebindings -o json | jq -r '
          .items[]
          | select(.roleRef.name == "cluster-admin")
          | .metadata.name
        ' | sort -u
        ```

        2. Inspect each binding and its subjects to decide if `cluster-admin` is really required\
           Replace `<crb-name>` with each name from step 1:

        ```sh theme={null}
        kubectl get clusterrolebinding <crb-name> -o yaml
        ```

        3. (Optional) Create or bind to a lower-privilege role instead of `cluster-admin`

        Example: create a constrained ClusterRole (edit rules as appropriate for your use case), save as `limited-admin-clusterrole.yaml`:

        ```yaml theme={null}
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRole
        metadata:
          name: limited-admin
        rules:
          - apiGroups: [""]
            resources: ["pods","services","configmaps","secrets"]
            verbs: ["get","list","watch","create","update","patch","delete"]
          - apiGroups: ["apps"]
            resources: ["deployments","daemonsets","statefulsets","replicasets"]
            verbs: ["get","list","watch","create","update","patch","delete"]
        ```

        Apply it:

        ```sh theme={null}
        kubectl apply -f limited-admin-clusterrole.yaml
        ```

        Then bind a user/group/serviceaccount to this new role, e.g. `user@example.com`, as `limited-admin-<name>.yaml`:

        ```yaml theme={null}
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
          name: limited-admin-user-example-com
        roleRef:
          apiGroup: rbac.authorization.k8s.io
          kind: ClusterRole
          name: limited-admin
        subjects:
          - kind: User
            name: user@example.com
            apiGroup: rbac.authorization.k8s.io
        ```

        Apply it:

        ```sh theme={null}
        kubectl apply -f limited-admin-user-example-com.yaml
        ```

        4. Remove unnecessary `cluster-admin` ClusterRoleBindings

        After you have confirmed that affected identities work with reduced privileges (or no access, if appropriate), delete each unnecessary binding:

        ```sh theme={null}
        kubectl delete clusterrolebinding <crb-name>
        ```

        Repeat for all ClusterRoleBindings that should no longer grant `cluster-admin`.

        5. Verification

        Run the benchmark audit command again and ensure it prints `NO_CLUSTER_ADMIN_BINDINGS` (or only finds the allowed system groups):

        ```sh theme={null}
        kubectl get clusterrolebindings -o json | jq -r '
          .items[]
          | select(.roleRef.name == "cluster-admin")
          | .subjects[]?
          | select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes"))
          | "FOUND_CLUSTER_ADMIN_BINDING"
        ' || echo "NO_CLUSTER_ADMIN_BINDINGS"
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Purpose:
        #   Identify and clean up risky clusterrolebindings that grant cluster-admin
        #   to subjects other than the default system:masters and system:nodes groups.
        #
        # Requirements:
        #   - Run on any machine with kubectl access and correct kubeconfig.
        #   - jq and kubectl must be installed and in PATH.
        #
        # Behavior:
        #   - For each offending ClusterRoleBinding to cluster-admin:
        #       * Prints the binding and its non-system subjects.
        #       * Prompts for confirmation before deletion (per binding).
        #   - Safe to re-run; deleted bindings stay deleted.
        #
        # NOTE:
        #   This script does NOT automatically create replacement lower-privilege roles.
        #   You must ensure that affected subjects either do not need cluster-admin
        #   or already have appropriate alternative bindings before confirming deletion.

        set -euo pipefail

        # Fail early if dependencies are missing
        for bin in kubectl jq; do
          if ! command -v "$bin" >/dev/null 2>&1; then
            echo "ERROR: Required binary '$bin' not found in PATH" >&2
            exit 1
          fi
        done

        echo "Discovering ClusterRoleBindings that grant 'cluster-admin' to non-system subjects..."
        echo

        # Get all clusterrolebindings that reference cluster-admin
        mapfile -t CRBS < <(
          kubectl get clusterrolebindings -o json |
            jq -r '
              .items[]
              | select(.roleRef.kind == "ClusterRole" and .roleRef.name == "cluster-admin")
              | .metadata.name
            ' | sort -u
        )

        if [ "${#CRBS[@]}" -eq 0 ]; then
          echo "No ClusterRoleBindings reference the 'cluster-admin' ClusterRole."
          echo "Verification:"
          kubectl get clusterrolebindings -o json | jq -r '
            .items[]
            | select(.roleRef.name == "cluster-admin")
            | .subjects[]?
            | select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes"))
            | "FOUND_CLUSTER_ADMIN_BINDING"
          ' || true
          exit 0
        fi

        deleted_any=false

        for crb in "${CRBS[@]}"; do
          # Extract subjects of interest: anything except system:masters or system:nodes groups
          subjects_json=$(kubectl get clusterrolebinding "$crb" -o json)
          mapfile -t SUBJECTS < <(
            jq -r '
              .subjects[]? |
              select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes")) |
              "\(.kind) \(.apiGroup // "") \(.namespace // "") \(.name)"
            ' <<<"$subjects_json" | sed 's/  / /g'
          )

          # If this CRB has no non-system subjects, skip it
          if [ "${#SUBJECTS[@]}" -eq 0 ]; then
            continue
          fi

          echo "--------------------------------------------------------------------------------"
          echo "ClusterRoleBinding: $crb"
          echo "Grants 'cluster-admin' to the following non-system subjects:"
          for s in "${SUBJECTS[@]}"; do
            echo "  - $s"
          done
          echo
          echo "Per remediation guidance, these subjects should typically be bound to"
          echo "less-privileged roles if they do not strictly require cluster-admin."
          echo

          # Confirm deletion interactively
          read -r -p "Delete this ClusterRoleBinding '$crb'? [y/N]: " ans
          case "$ans" in
            y|Y|yes|YES)
              echo "Deleting ClusterRoleBinding '$crb'..."
              kubectl delete clusterrolebinding "$crb"
              deleted_any=true
              ;;
            *)
              echo "Skipping deletion of '$crb'."
              ;;
          esac
          echo
        done

        echo "--------------------------------------------------------------------------------"
        echo "Post-change verification:"
        # Re-run the benchmark audit logic to prove compliance
        audit_output=$(kubectl get clusterrolebindings -o json | jq -r '
          .items[]
          | select(.roleRef.name == "cluster-admin")
          | .subjects[]?
          | select(.kind != "Group" or (.name != "system:masters" and .name != "system:nodes"))
          | "FOUND_CLUSTER_ADMIN_BINDING"
        ')

        if [ -z "$audit_output" ]; then
          echo "NO_CLUSTER_ADMIN_BINDINGS"
          echo "No remaining non-system subjects are bound to 'cluster-admin'."
        else
          echo "$audit_output"
          echo
          echo "There are still ClusterRoleBindings granting 'cluster-admin' to non-system subjects."
          echo "Review those bindings and re-run this script as needed."
        fi

        # Exit code reflects whether non-compliant bindings remain
        if [ -z "$audit_output" ]; then
          exit 0
        else
          exit 1
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/admin/authorization/rbac/#user-facing-roles](https://kubernetes.io/docs/admin/authorization/rbac/#user-facing-roles)
