> ## 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 configuration objects can be abused to intercept or alter admission decisions. Restrict this access to trusted administrators only.

### Risk Level

High

### Address

Security

### Compliance Standards

* CIS EKS

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **List all cluster roles and role bindings that reference webhook configuration resources**
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get clusterrole -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5
           kubectl get role -A -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5
           kubectl get clusterrolebinding -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5
           kubectl get rolebinding -A -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5
           ```
           Focus on rules where `.resources` includes `validatingwebhookconfigurations` or `mutatingwebhookconfigurations`.

        2. **Identify subjects (users, groups, service accounts) with access to these resources**
           * Run on: any machine with kubectl access\
             For each ClusterRole or Role found in step 1, get the full object and its bindings:
           ```bash theme={null}
           kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml
           kubectl get clusterrolebinding -o yaml | grep -n "<CLUSTERROLE_NAME>" -A10 -B2

           kubectl get role -A | grep "<ROLE_NAME>"
           kubectl get role -n <NAMESPACE> <ROLE_NAME> -o yaml
           kubectl get rolebinding -n <NAMESPACE> -o yaml | grep -n "<ROLE_NAME>" -A10 -B2
           ```
           List all `.subjects` and categorize them as trusted administrators or non-admin workloads.

        3. **Decide which access is justified and which should be removed or constrained**
           * Review each subject that has `get`, `list`, `watch`, `create`, `update`, `patch`, or `delete` on the webhook configuration resources.
           * Treat write access (`create/update/patch/delete`) as highly sensitive; normally, only a very small admin group should retain this.
           * For non-admin service accounts or broad groups (e.g., system:authenticated), plan to remove webhook configuration permissions or replace them with narrower roles that do not include these resources.

        4. **Remove or restrict webhook configuration access from inappropriate roles**
           * Run on: any machine with kubectl access\
             Edit each role/clusterrole that should no longer grant access:
           ```bash theme={null}
           kubectl edit clusterrole <CLUSTERROLE_NAME>
           kubectl edit role -n <NAMESPACE> <ROLE_NAME>
           ```
           In the opened editor, either:
           * Remove `validatingwebhookconfigurations` and `mutatingwebhookconfigurations` from the `resources` list, or
           * Delete the entire rule block that references them, if it is not needed.\
             Save and exit to apply changes.

        5. **If necessary, rebind subjects to safer roles**
           * Run on: any machine with kubectl access\
             For workloads that legitimately needed some permissions but not webhook configuration access, bind them to an alternative role without those resources:
           ```bash theme={null}
           kubectl create rolebinding <NEW_BINDING_NAME> \
             --clusterrole=<SAFER_CLUSTERROLE_NAME> \
             --serviceaccount=<NAMESPACE>:<SERVICEACCOUNT_NAME> \
             --namespace <NAMESPACE>
           ```
           Then remove the old binding:
           ```bash theme={null}
           kubectl delete clusterrolebinding <OLD_BINDING_NAME>
           # or
           kubectl delete rolebinding -n <NAMESPACE> <OLD_BINDING_NAME>
           ```

        6. **Verify that only trusted administrators retain webhook configuration access**
           * Run on: any machine with kubectl access\
             Re-run a focused RBAC review:
           ```bash theme={null}
           kubectl get clusterrole -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5
           kubectl get role -A -o yaml | egrep -n 'name: (validatingwebhookconfigurations|mutatingwebhookconfigurations)' -A5 -B5
           ```
           For each remaining role, confirm via its bindings that only trusted admin users/groups/service accounts are subjects. If any non-admin subjects remain, repeat steps 4–5.
      </Accordion>

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

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

        **Problem indication:** Any ClusterRole in this list is capable of accessing/altering webhook configurations. Non-admin or generic roles here (e.g., roles used by apps, CI/CD, or controllers) are suspect.

        ```bash theme={null}
        # 2) Show detailed rules for those ClusterRoles
        # Replace <clusterrole-name> with a name from the previous command

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

        **Problem indication:**\
        Rules that include:

        * `apiGroups: ["admissionregistration.k8s.io"]`
        * `resources: ["validatingwebhookconfigurations"]` and/or `["mutatingwebhookconfigurations"]`
        * `verbs` such as `create`, `update`, `patch`, `delete`, or `list`/`get`/`watch` beyond what trusted admins require.

        ```bash theme={null}
        # 3) Identify who is bound to those ClusterRoles (ClusterRoleBindings)
        # Run once to see all relevant ClusterRoleBindings

        kubectl get clusterrolebindings -o json \
          | jq -r '
            .items[]
            | select(.roleRef.kind == "ClusterRole")
            | select(
                .roleRef.name as $r
                | $r | IN(
                    # Paste the ClusterRole names from step 1 inside this IN() list, e.g.:
                    "cluster-admin","admission-webhook-admin"
                )
              )
            | .metadata.name + " -> " + .roleRef.name + " :: " +
              ( .subjects // [] | map(.kind + "/" + .name) | join(",") )
          ' | sort
        ```

        **Problem indication:**\
        Bindings where the `subjects` include:

        * Broad groups (e.g., `system:authenticated`, `system:serviceaccounts`, `system:serviceaccounts:<namespace>`)
        * Application service accounts or CI/CD identities
        * External user groups not intended as cluster administrators

        ```bash theme={null}
        # 4) Inspect specific ClusterRoleBindings in detail
        kubectl get clusterrolebinding <binding-name> -o yaml
        ```

        **Problem indication:**\
        `subjects` that are not clearly limited to a small, trusted admin group or break-glass admin accounts.

        ```bash theme={null}
        # 5) Also check namespace-scoped Roles (less common but possible)
        kubectl get roles --all-namespaces -o json \
          | jq -r '
            .items[]
            | select(
                .rules[]
                | select(
                    (.apiGroups[]? == "admissionregistration.k8s.io")
                    and
                    (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                )
              )
            | .metadata.namespace + "/" + .metadata.name
          ' | sort -u
        ```

        ```bash theme={null}
        # 6) For any Role found in step 5, see who is bound to it
        kubectl get rolebinding -A -o json \
          | jq -r '
            .items[]
            | .metadata.namespace as $ns
            | .roleRef.kind as $k
            | .roleRef.name as $r
            | select($k == "Role")
            | .metadata.name as $rb
            | .subjects // [] as $subs
            | "\($ns)/\($rb) -> \($k)/\($r) :: " + ($subs | map(.kind + "/" + .name) | join(","))
          ' | sort
        ```

        **Problem indication:**\
        Any Role/RoleBinding that grants access to webhook configuration resources to non-admin service accounts or broad groups.

        ```bash theme={null}
        # 7) Quick verification after manual adjustments (re-run scoping checks)
        # ClusterRoles
        kubectl get clusterroles -o json \
          | jq -r '
            .items[]
            | select(
                .rules[]
                | select(
                    (.apiGroups[]? == "admissionregistration.k8s.io")
                    and
                    (.resources[]? | IN("validatingwebhookconfigurations","mutatingwebhookconfigurations"))
                )
              )
            | .metadata.name
          ' | sort -u
        ```

        **Desired state:**\
        Only a very small, clearly administrative set of ClusterRoles (and corresponding bindings) remain with access to `validatingwebhookconfigurations` and `mutatingwebhookconfigurations`.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Report RBAC subjects with access to validating/mutating webhook configurations.
        # Run on: any machine with kubectl access and current context pointing to target cluster.

        set -euo pipefail

        echo "=== Discovering verbs that grant access to webhook configuration resources ==="

        # Collect all ClusterRoles and Roles that reference validating/mutating webhook configs
        kubectl get clusterroles,roles -A -o json \
          | jq -r '
            .items[]
            | {
                kind: .kind,
                namespace: (.metadata.namespace // ""),
                name: .metadata.name,
                rules: (.rules // [])
              }
            | . as $obj
            | $obj.rules[]
            | select(
                (.resources // []) 
                | map(
                    (split("/") | .[0])  # strip possible apiGroup/resource forms
                  )
                | flatten
                | any(. == "validatingwebhookconfigurations" or . == "mutatingwebhookconfigurations")
              )
            | {
                kind: $obj.kind,
                namespace: $obj.namespace,
                name: $obj.name,
                apiGroups: (.apiGroups // []),
                resources: (.resources // []),
                verbs: (.verbs // [])
              }
            | @json' | sort | uniq > /tmp/webhook-access-roles.json

        if [[ ! -s /tmp/webhook-access-roles.json ]]; then
          echo "No Roles or ClusterRoles directly reference validating or mutating webhook configurations."
          exit 0
        fi

        echo
        echo "=== Roles/ClusterRoles with webhook configuration access ==="
        cat /tmp/webhook-access-roles.json | jq -r '
          . as $r
          | "\($r.kind)\t\($r.namespace)\t\($r.name)\tverbs=\($r.verbs|join(","))\tresources=\($r.resources|join(","))"
        ' | column -t

        echo
        echo "=== Finding RoleBindings/ClusterRoleBindings that grant these roles ==="

        # Build a list of role references of interest
        role_filters=$(cat /tmp/webhook-access-roles.json | jq -r '
          . as $r
          | if $r.kind == "ClusterRole" then
              "(.kind==\"ClusterRoleBinding\" and .roleRef.kind==\"ClusterRole\" and .roleRef.name==\"\($r.name)\")"
            else
              "(.kind==\"RoleBinding\" and .roleRef.kind==\"Role\" and .roleRef.name==\"\($r.name)\" and .metadata.namespace==\"\($r.namespace)\")"
            end
        ' | sort -u | paste -sd " or " -)

        # If there are no role filters, exit
        if [[ -z "${role_filters}" ]]; then
          echo "No bindings found for the identified Roles/ClusterRoles."
          exit 0
        fi

        kubectl get clusterrolebindings,rolebindings -A -o json \
          | jq -r "
            .items[]
            | select(${role_filters})
            | {
                kind: .kind,
                namespace: (.metadata.namespace // \"\"),
                name: .metadata.name,
                roleKind: .roleRef.kind,
                roleName: .roleRef.name,
                subjects: (.subjects // [])
              }
            | @json" > /tmp/webhook-access-bindings.json

        if [[ ! -s /tmp/webhook-access-bindings.json ]]; then
          echo "No RoleBindings or ClusterRoleBindings grant the webhook-accessing roles."
          exit 0
        fi

        echo
        echo "=== Bindings granting access to webhook configuration objects ==="
        cat /tmp/webhook-access-bindings.json | jq -r '
          . as $b
          | if ($b.subjects | length) == 0 then
              "\($b.kind)\t\($b.namespace)\t\($b.name)\trole=\($b.roleKind)/\($b.roleName)\tsubject=<none>"
            else
              $b.subjects[]
              | "\($b.kind)\t\($b.namespace)\t\($b.name)\trole=\($b.roleKind)/\($b.roleName)\tsubject=\(.kind):\(.namespace // "-"):\(.name)"
            end
        ' | column -t

        echo
        echo "=== Interpretation guidance ==="
        cat <<'EOF'
        Any subject listed above (User, Group, or ServiceAccount) has RBAC-granted access
        to validatingwebhookconfigurations or mutatingwebhookconfigurations.

        Potential problems to investigate:
        - Non-admin users or broad groups (e.g., "system:authenticated", "system:serviceaccounts")
        - Application-specific service accounts that should not control cluster-wide admission
        - Wildcard verbs (*) or powerful verbs (create, update, patch, delete) on these resources
        - ClusterRoleBindings that expose admin-like ClusterRoles to many subjects

        Review each binding and decide whether the subject truly needs this level of access.
        If not, update or remove the corresponding Role/ClusterRole or Binding.
        EOF
        ```

        Problematic output indicators:

        * Roles/ClusterRoles with `verbs` including `create`, `update`, `patch`, `delete`, or `*` on `validatingwebhookconfigurations` or `mutatingwebhookconfigurations`.
        * Bindings where:
          * `subjects` include broad groups like `system:authenticated`, `system:unauthenticated`, or generic team groups.
          * Application/service-specific `ServiceAccount` subjects appear, rather than trusted admin identities.
            These require manual review and possible RBAC tightening; they cannot be safely auto-fixed.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
