> ## 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 To Webhook Configuration Objects

### More Info:

Access to validating or mutating webhook configurations can be used to intercept or alter admission decisions. Restrict this access.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **List all ClusterRoles that grant webhook access**\
           Run on any machine with kubectl access:
           ```bash theme={null}
           kubectl get clusterroles -o json \
           | jq -r '
             .items[]
             | select(
                 .rules[]
                 | select(
                     (.apiGroups[]? == "admissionregistration.k8s.io")
                     and (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                 )
               )
             | .metadata.name
           ' | sort -u
           ```
           Save the output list of ClusterRole names for review.

        2. **Inspect each identified ClusterRole’s permissions**\
           For each ClusterRole name from step 1:
           ```bash theme={null}
           CLUSTERROLE_NAME="<clusterrole-name>"
           kubectl get clusterrole "${CLUSTERROLE_NAME}" -o yaml
           ```
           Review `.rules` and note which verbs (`get`, `list`, `watch`, `create`, `update`, `patch`, `delete`) are allowed on `validatingwebhookconfigurations` or `mutatingwebhookconfigurations`.

        3. **Determine which subjects receive these ClusterRoles**\
           For each ClusterRole name from step 1, find ClusterRoleBindings:
           ```bash theme={null}
           CLUSTERROLE_NAME="<clusterrole-name>"
           kubectl get clusterrolebindings -o json \
           | jq -r --arg cr "${CLUSTERROLE_NAME}" '
             .items[]
             | select(.roleRef.kind == "ClusterRole" and .roleRef.name == $cr)
             | .metadata.name
           '
           ```
           For each returned ClusterRoleBinding:
           ```bash theme={null}
           CRB_NAME="<clusterrolebinding-name>"
           kubectl get clusterrolebinding "${CRB_NAME}" -o yaml
           ```
           Review `.subjects` (users, groups, service accounts) and confirm which ones truly need webhook configuration access.

        4. **Decide least-privilege adjustments**\
           For each ClusterRole and its bindings:
           * Confirm whether any subject actually needs to read or modify webhook configurations (e.g., admission controller operators).
           * If only read is required, plan to remove `create`, `update`, `patch`, `delete`.
           * If only specific webhook type is required, plan to remove access to the other (`validating` vs `mutating`).
           * If a subject does not need any webhook access, plan to:
             * Rebind it to a different, less-privileged ClusterRole, or
             * Remove the ClusterRoleBinding if not needed.

        5. **Apply targeted changes to ClusterRoles/ClusterRoleBindings**\
           For each ClusterRole that needs reduction:
           ```bash theme={null}
           kubectl edit clusterrole "<clusterrole-name>"
           ```
           In the editor, under `rules` for `admissionregistration.k8s.io`, remove unnecessary verbs and/or resources, following your decisions from step 4, then save.\
           For each ClusterRoleBinding that should no longer grant webhook access:
           ```bash theme={null}
           kubectl edit clusterrolebinding "<clusterrolebinding-name>"
           ```
           Adjust `.roleRef` to a less-privileged ClusterRole or remove unneeded subjects; if the binding is not needed at all, delete it:
           ```bash theme={null}
           kubectl delete clusterrolebinding "<clusterrolebinding-name>"
           ```

        6. **Re-verify effective access after changes**\
           Re-run the identification command from step 1:
           ```bash theme={null}
           kubectl get clusterroles -o json \
           | jq -r '
             .items[]
             | select(
                 .rules[]
                 | select(
                     (.apiGroups[]? == "admissionregistration.k8s.io")
                     and (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                 )
               )
             | .metadata.name
           ' | sort -u
           ```
           For any remaining ClusterRoles, confirm via `kubectl get clusterrole <name> -o yaml` that only the minimal, justified permissions and bindings remain.
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) List all ClusterRoles that can access validating or mutating webhook configurations
        # Run on: any machine with kubectl access

        kubectl get clusterrole -o json \
          | jq -r '
            .items[]
            | select(
                [
                  .rules[]
                  | select(
                      (.apiGroups[]? == "admissionregistration.k8s.io")
                      and (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                    )
                ] | length > 0
              )
            | .metadata.name
          ' | sort | uniq
        ```

        **Problem indication:** Any non-system or broad-privilege role (e.g., custom app roles) listed here may be over-privileged and must be reviewed.

        ***

        ```bash theme={null}
        # 2) Show full details of ClusterRoles identified above
        # Replace <CLUSTERROLE_NAME> with each name from step 1

        kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml
        ```

        **Problem indication:** In the `rules:` section, look for:

        * `apiGroups: ["admissionregistration.k8s.io"]`
        * `resources: ["validatingwebhookconfigurations"]` and/or `["mutatingwebhookconfigurations"]`
        * `verbs` including `create`, `update`, `patch`, `delete`, or `*`

        Roles that are:

        * bound to many subjects,
        * used by application service accounts,
        * or grant write/delete (`create`, `update`, `patch`, `delete`, `*`)
          are high risk and should be questioned.

        ***

        ```bash theme={null}
        # 3) Find which subjects (users/groups/serviceaccounts) are bound to those ClusterRoles
        # Run for each ClusterRole of interest

        kubectl get clusterrolebindings -o json \
          | jq -r '
            .items[]
            | select(.roleRef.kind == "ClusterRole" and .roleRef.name == "<CLUSTERROLE_NAME>")
            | .metadata.name as $crb
            | .subjects[]
            | "\($crb)\t\(.kind)\t\(.namespace // "-")\t\(.name)"
          ' | column -t
        ```

        **Problem indication:** Any binding that grants these roles to:

        * generic groups (e.g., `system:authenticated`, `system:unauthenticated`),
        * broad human user groups (e.g., `devs`, `admins` not specifically for cluster ops),
        * application service accounts in non-system namespaces

        suggests webhook configuration access is too widely granted.

        ***

        ```bash theme={null}
        # 4) Quick summary: show only roles with WRITE access to webhooks
        kubectl get clusterrole -o json \
          | jq -r '
            .items[]
            | select(
                [
                  .rules[]
                  | select(
                      (.apiGroups[]? == "admissionregistration.k8s.io")
                      and (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                      and (.verbs[]? | IN("create","update","patch","delete","*"))
                    )
                ] | length > 0
              )
            | .metadata.name
          ' | sort | uniq
        ```

        **Problem indication:** Any role in this list must have a clear, justified operational need; otherwise it is a candidate for permissions reduction.

        ***

        ```bash theme={null}
        # 5) Inspect direct access on the webhook objects themselves (cluster-wide)
        # Read access is also sensitive (can expose configuration patterns).

        kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations
        ```

        **Problem indication:** Large numbers of webhook configs, or unfamiliar names owned by third parties, warrant closer scrutiny of who manages them using steps 1–4.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        set -euo pipefail

        # This script inspects which subjects can access validating/mutating webhook configurations.
        # Run on any machine with kubectl access and cluster-wide RBAC permissions.

        echo "=== 1) ClusterRoles with access to validatingwebhookconfigurations or mutatingwebhookconfigurations ==="
        kubectl get clusterrole -o json \
        | jq -r '
          .items[]
          | {
              name: .metadata.name,
              rules: (
                .rules // [] 
                | map(
                    select(
                      (.apiGroups[]? == "admissionregistration.k8s.io")
                      and (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                    )
                  )
              )
            }
          | select((.rules | length) > 0)
          | . as $r
          | "ClusterRole: \($r.name)\nRules:",
            (
              $r.rules[]
              | "  apiGroups: \(.apiGroups // [])",
                "  resources: \(.resources // [])",
                "  verbs: \(.verbs // [])",
                ""
            )' \
        | sed 's/"//g' || echo "Failed to query ClusterRoles"

        echo
        echo "=== 2) ClusterRoleBindings mapping those ClusterRoles to subjects ==="
        problematic_crs=$(kubectl get clusterrole -o json \
          | jq -r '
            .items[]
            | select(
                (.rules // [])
                | map(
                    select(
                      (.apiGroups[]? == "admissionregistration.k8s.io")
                      and (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                    )
                  )
                | length > 0
              )
            | .metadata.name
          ')

        if [ -z "$problematic_crs" ]; then
          echo "No ClusterRoles found with direct access to webhook configuration objects."
        else
          for cr in $problematic_crs; do
            echo "ClusterRole: ${cr}"
            kubectl get clusterrolebinding -o json \
            | jq -r --arg CR "$cr" '
                .items[]
                | select(.roleRef.kind == "ClusterRole" and .roleRef.name == $CR)
                | "  ClusterRoleBinding: \(.metadata.name)",
                  "    Subjects:",
                  (
                    .subjects // []
                    | .[]
                    | "      kind=\(.kind) name=\(.name) namespace=\(.namespace // "-")"
                  ),
                  ""
              '
            echo
          done
        fi

        echo "=== 3) (Optional) Namespaced Roles with access (less common) ==="
        kubectl get role -A -o json \
        | jq -r '
          .items[]
          | {
              ns: .metadata.namespace,
              name: .metadata.name,
              rules: (
                .rules // []
                | map(
                    select(
                      (.apiGroups[]? == "admissionregistration.k8s.io")
                      and (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                    )
                  )
              )
            }
          | select((.rules | length) > 0)
          | . as $r
          | "Role: \($r.ns)/\($r.name)\nRules:",
            (
              $r.rules[]
              | "  apiGroups: \(.apiGroups // [])",
                "  resources: \(.resources // [])",
                "  verbs: \(.verbs // [])",
                ""
            )' \
        | sed 's/"//g' || echo "Failed to query Roles"

        cat <<'EOF'

        How to interpret this output:

        - Any ClusterRole or Role listed above that grants non-read-only verbs on:
            resources: ["validatingwebhookconfigurations","mutatingwebhookconfigurations"]
            apiGroups: ["admissionregistration.k8s.io"]
          is potentially problematic.

        - Pay particular attention to:
            - verbs including: ["create","update","patch","delete","deletecollection","*"]
            - broad roles like "cluster-admin" or custom admin/devops roles
            - ClusterRoleBindings/RoleBindings that attach these roles to:
                * system:authenticated or system:serviceaccounts
                * groups or service accounts not strictly required to manage admission webhooks

        - Safer patterns:
            - No roles with these resources at all, except a very small number of tightly controlled
              admin/SRE roles.
            - If access is needed, limit verbs to ["get","list","watch"] where possible, and bind
              only to specific trusted subjects.

        Use this report to review and decide which roles/bindings should be tightened or removed.
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
