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

Critical

### Address

Security

### Compliance Standards

* CIS Kubernetes

### 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 wide
           kubectl get clusterrolebindings -o yaml > /tmp/clusterrolebindings-full.yaml
           ```

        2. Identify which subjects truly require `cluster-admin`
           * Run on: any machine with kubectl access
           * Inspect each binding that references `cluster-admin` and note its subjects and usage context:
           ```bash theme={null}
           kubectl get clusterrolebindings -o yaml | \
             awk '/kind: ClusterRoleBinding/{print "---"}1' | \
             sed -n '/roleRef:/{h;:a;n;/subjects:/{p;x;p;q};ba}'
           ```
           * For each binding where `roleRef.name: cluster-admin`, determine (by policy/with app owners) if the service account, user, or group truly needs full cluster-wide admin or can be restricted (for example: namespace-scoped admin, read-only, or limited custom roles).

        3. Create or select a lower-privilege role for each subject that doesn’t need `cluster-admin`
           * Run on: any machine with kubectl access
           * To bind to the built-in `admin` role in a specific namespace (example `team-a` and user `alice`):
           ```bash theme={null}
           kubectl create rolebinding alice-admin-team-a \
             --clusterrole=admin \
             --user=alice \
             --namespace=team-a
           ```
           * Or create a custom, least-privilege ClusterRole (edit rules as needed):
           ```bash theme={null}
           cat << 'EOF' | kubectl apply -f -
           apiVersion: rbac.authorization.k8s.io/v1
           kind: ClusterRole
           metadata:
             name: limited-ops
           rules:
             - apiGroups: [""]
               resources: ["pods","services"]
               verbs: ["get","list","watch"]
           EOF
           ```
           * Then bind that role to the subject instead of `cluster-admin` (example service account `app-sa` in namespace `team-a`):
           ```bash theme={null}
           kubectl create clusterrolebinding app-sa-limited-ops \
             --clusterrole=limited-ops \
             --serviceaccount=team-a:app-sa
           ```

        4. Carefully migrate off `cluster-admin` for each subject
           * Run on: any machine with kubectl access
           * For each subject currently bound to `cluster-admin`, ensure its new binding (from step 3) is in place, then test access (have the subject’s workload or user confirm required operations still work).
           * Example: check current bindings for a given subject before deletion (replace `alice`):
           ```bash theme={null}
           kubectl get clusterrolebindings -o custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECTS:.subjects[*].name --no-headers | \
             grep alice || true
           ```

        5. Remove unnecessary `cluster-admin` clusterrolebindings
           * Run on: any machine with kubectl access
           * Once a subject no longer needs `cluster-admin`, delete its binding. Replace `<binding-name>` with the exact name from step 1 or 4:
           ```bash theme={null}
           kubectl delete clusterrolebinding <binding-name>
           ```
           * If multiple subjects share one `cluster-admin` binding and only some need it, recreate a narrower binding for those that still need it, and then delete the original broad binding.

        6. Verification
           * Run on: any machine with kubectl access
           * Re-run the audit logic and confirm that no non-`cluster-admin` role name is bound to the `cluster-admin` ClusterRole:
           ```bash theme={null}
           kubectl get clusterrolebindings \
             -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name --no-headers | \
           while read -r role_name role_binding subject
           do
             if [[ "${role_name}" != "cluster-admin" && "${role_binding}" == "cluster-admin" ]]; then
               is_compliant="false"
             else
               is_compliant="true"
             fi
             echo "**role_name: ${role_name} role_binding: ${role_binding} subject: ${subject} is_compliant: ${is_compliant}"
           done
           ```
           * The output should show no lines where `role_binding: cluster-admin` is paired with `is_compliant: false`.
      </Accordion>

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

        1. List all ClusterRoleBindings that reference `cluster-admin` and see their subjects

        ```bash theme={null}
        kubectl get clusterrolebindings \
          -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECTS:.subjects[*].name
        ```

        2. For each non‑`cluster-admin` binding that grants `cluster-admin`, inspect details to decide if it really needs full privileges:

        ```bash theme={null}
        kubectl get clusterrolebinding <binding-name> -o yaml
        ```

        3. If a subject should have reduced privileges, first bind it to a less‑privileged ClusterRole (example: `view`; adjust role/subject to your needs):

        * Example manifest to create a safer ClusterRoleBinding:

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

        Apply it:

        ```bash theme={null}
        kubectl apply -f view-access-someuser.yaml
        ```

        4. Once you have confirmed the replacement access works, remove the unnecessary `cluster-admin` binding:

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

        Repeat steps 2–4 for every ClusterRoleBinding where `.roleRef.name == cluster-admin` and the subject is not supposed to be fully privileged.

        5. Verification (same logic as the audit, to ensure no non‑`cluster-admin` binding grants `cluster-admin`):

        ```bash theme={null}
        kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name --no-headers | while read -r role_name role_binding subject
        do
          if [[ "${role_name}" != "cluster-admin" && "${role_binding}" == "cluster-admin" ]]; then
            is_compliant="false"
          else
            is_compliant="true"
          fi
          echo "**role_name: ${role_name} role_binding: ${role_binding} subject: ${subject} is_compliant: ${is_compliant}"
        done
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation for: Ensure Cluster Admin Role Is Only Used Where Required (CIS Kubernetes 5.1.1)
        #
        # Scope: any machine with kubectl access to the cluster
        #
        # This script:
        # 1. Lists all ClusterRoleBindings that grant cluster-admin.
        # 2. For each, prompts for a decision:
        #    - [s]kip (default)
        #    - [d]elete the ClusterRoleBinding
        #    - [r]eplace with a lower-privilege ClusterRole (you specify the new role name)
        # 3. Optionally backs up the original ClusterRoleBinding manifest before changes.
        # 4. Verifies remaining cluster-admin bindings at the end (same logic as the audit).
        #
        # NOTE: This is a MANUAL control – you must decide whether a given subject truly
        # requires cluster-admin or can be bound to a narrower role.

        set -euo pipefail

        # ---------- configuration ----------

        # Directory for backups of modified/deleted ClusterRoleBindings
        BACKUP_DIR="./clusterrolebinding_backups_$(date +%Y%m%d_%H%M%S)"

        # Set to "true" to always take a backup before modifying/deleting
        ALWAYS_BACKUP="true"

        # kubectl context/namespace are cluster-scoped; you may override KUBECONFIG externally if needed.

        # ---------- helpers ----------

        require_kubectl() {
          if ! command -v kubectl >/dev/null 2>&1; then
            echo "ERROR: kubectl not found in PATH. Install/configure kubectl on this machine and retry." >&2
            exit 1
          fi
        }

        ensure_backup_dir() {
          if [[ "${ALWAYS_BACKUP}" == "true" ]]; then
            mkdir -p "${BACKUP_DIR}"
          fi
        }

        backup_crb() {
          local name="$1"
          if [[ "${ALWAYS_BACKUP}" != "true" ]]; then
            return 0
          fi
          local file="${BACKUP_DIR}/${name}.yaml"
          echo "  - Backing up ClusterRoleBinding/${name} to ${file}"
          kubectl get clusterrolebinding "${name}" -o yaml > "${file}"
        }

        replace_crb_role() {
          local crb_name="$1"
          local new_role="$2"

          echo "  - Replacing roleRef of ClusterRoleBinding/${crb_name} with ClusterRole/${new_role}"

          # Patch only the roleRef fields
          kubectl patch clusterrolebinding "${crb_name}" \
            --type='merge' \
            -p "$(cat <<EOF
        {
          "roleRef": {
            "apiGroup": "rbac.authorization.k8s.io",
            "kind": "ClusterRole",
            "name": "${new_role}"
          }
        }
        EOF
        )"
        }

        delete_crb() {
          local name="$1"
          echo "  - Deleting ClusterRoleBinding/${name}"
          kubectl delete clusterrolebinding "${name}"
        }

        prompt_action() {
          local crb_name="$1"
          local role_name="$2"
          local subjects="$3"

          echo
          echo "ClusterRoleBinding: ${crb_name}"
          echo "  roleRef.name  : ${role_name}"
          echo "  subjects      : ${subjects:-<none>}"
          echo
          echo "Decision for ClusterRoleBinding/${crb_name}:"
          echo "  [s] Skip (do nothing)"
          echo "  [d] Delete (remove cluster-admin binding entirely)"
          echo "  [r] Replace with a lower-privilege ClusterRole"
          read -r -p "Choose action [s/d/r] (default: s): " action

          action="${action:-s}"

          case "${action}" in
            s|S)
              echo "  -> Skipping ClusterRoleBinding/${crb_name}"
              ;;
            d|D)
              backup_crb "${crb_name}"
              delete_crb "${crb_name}"
              ;;
            r|R)
              read -r -p "  Enter LOWER-PRIVILEGE ClusterRole name to bind instead of cluster-admin: " new_role
              if [[ -z "${new_role}" ]]; then
                echo "  ! No role entered, skipping replacement for ClusterRoleBinding/${crb_name}"
                return
              fi

              # Basic existence check for the new ClusterRole
              if ! kubectl get clusterrole "${new_role}" >/dev/null 2>&1; then
                echo "  ! ClusterRole/${new_role} does not exist. Create it first or choose another role. Skipping."
                return
              fi

              backup_crb "${crb_name}"
              replace_crb_role "${crb_name}" "${new_role}"
              ;;
            *)
              echo "  ! Invalid choice '${action}', skipping ClusterRoleBinding/${crb_name}"
              ;;
          esac
        }

        # ---------- main logic ----------

        require_kubectl
        ensure_backup_dir

        echo "Discovering ClusterRoleBindings that grant 'cluster-admin'..."

        # List all ClusterRoleBindings where roleRef.name == cluster-admin
        mapfile -t CRB_LINES < <(
          kubectl get clusterrolebindings \
            -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECTS:.subjects[*].name \
            --no-headers | awk '$2=="cluster-admin"'
        )

        if [[ "${#CRB_LINES[@]}" -eq 0 ]]; then
          echo "No ClusterRoleBindings currently grant the 'cluster-admin' ClusterRole."
        else
          echo "The following ClusterRoleBindings grant 'cluster-admin':"
          printf '  %s\n' "${CRB_LINES[@]}"
        fi

        for line in "${CRB_LINES[@]}"; do
          # Expect: NAME ROLE SUBJECTS...
          # NAME may not contain spaces; ROLE is 'cluster-admin'; SUBJECTS may be empty or space-separated.
          crb_name=$(awk '{print $1}' <<<"${line}")
          role_name=$(awk '{print $2}' <<<"${line}")
          # Extract everything from the 3rd field onward as subjects
          subjects=$(cut -d' ' -f3- <<<"${line}" || true)

          prompt_action "${crb_name}" "${role_name}" "${subjects}"
        done

        # ---------- verification (re-run audit-style logic) ----------

        echo
        echo "Verification: remaining ClusterRoleBindings and compliance status:"
        kubectl get clusterrolebindings \
          -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name \
          --no-headers | while read -r role_name role_binding subject; do
            if [[ "${role_name}" != "cluster-admin" && "${role_binding}" == "cluster-admin" ]]; then
              is_compliant="false"
            else
              is_compliant="true"
            fi
            echo "**role_name: ${role_name} role_binding: ${role_binding} subject: ${subject} is_compliant: ${is_compliant}"
          done

        echo
        echo "Completed. Re-run this script anytime after RBAC changes; it is safe to re-run."
        ```
      </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)
