> ## 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 The Proxy Sub-Resource Of Nodes

### More Info:

Access to the Node proxy sub-resource exposes the kubelet API and can bypass audit logging and admission control. It should be limited to trusted administrators only.

### Risk Level

High

### Address

Security

### Compliance Standards

* CIS AKS

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **List all roles/clusterroles that can access the Node `proxy` sub-resource**
           * Run on: any machine with `kubectl` access
           ```bash theme={null}
           kubectl get clusterrole -o yaml | grep -En 'kind: ClusterRole|(^[[:space:]]*- apiGroups:)|(^[[:space:]]*resources:)|(^[[:space:]]*verbs:)|(^[[:space:]]*resourceNames:)|proxy' 
           ```
           ```bash theme={null}
           kubectl get role --all-namespaces -o yaml | grep -En 'kind: Role|(^[[:space:]]*- apiGroups:)|(^[[:space:]]*resources:)|(^[[:space:]]*verbs:)|(^[[:space:]]*resourceNames:)|proxy'
           ```
           Focus on entries where `resources:` includes `nodes/proxy` or `nodes` and `verbs:` includes `get`, `list`, `create`, or `*`.

        2. **Extract detailed definitions of suspicious roles/clusterroles**
           * From the names found in step 1, get the full YAML for review.
           * Run on: any machine with `kubectl` access
           ```bash theme={null}
           kubectl get clusterrole <CLUSTERROLE_NAME> -o yaml
           ```
           ```bash theme={null}
           kubectl get role -n <NAMESPACE> <ROLE_NAME> -o yaml
           ```
           Review whether `nodes/proxy` (or broad `nodes` with `*` verbs) is really needed for the intended purpose of that role.

        3. **Identify who is actually getting this proxy access (bindings)**
           * Run on: any machine with `kubectl` access
           ```bash theme={null}
           kubectl get clusterrolebinding -o yaml | grep -En 'kind: ClusterRoleBinding|roleRef:|name:|subjects:'
           ```
           ```bash theme={null}
           kubectl get rolebinding --all-namespaces -o yaml | grep -En 'kind: RoleBinding|roleRef:|name:|subjects:'
           ```
           For each role/clusterrole from step 2 that has `nodes/proxy` access, find the corresponding `roleRef` in these bindings and note which `users`, `groups`, or `serviceAccounts` receive that access.

        4. **Decide which subjects truly need Node `proxy` access**
           * For each bound subject identified in step 3, validate with your operational/incident-response requirements:
             * Trusted cluster administrators: generally may retain this access.
             * Automation/service accounts, application accounts, and regular users: usually should not need direct kubelet API access via Node proxy.
           * Document a list of subjects and whether `nodes/proxy` is required, optional, or not justified.

        5. **Tighten or remove excessive `nodes/proxy` permissions**
           * Run on: any machine with `kubectl` access
           * Option A – Remove `nodes/proxy` from the role/clusterrole (preferred):
           ```bash theme={null}
           kubectl edit clusterrole <CLUSTERROLE_NAME>
           # or for namespace-scoped:
           kubectl edit role -n <NAMESPACE> <ROLE_NAME>
           ```
           In the editor, under `rules:`, remove `nodes/proxy` (or overly broad `nodes` rules) from `resources:` for subjects that should not have it; narrow `verbs:` if needed. Save and exit.
           * Option B – If a shared role is too broad, create a new, restricted role and rebind:
           ```bash theme={null}
           kubectl create role <NEW_ROLE_NAME> -n <NAMESPACE> --verb=<VERBS_WITHOUT_PROXY> --resource=<RESOURCES_WITHOUT_NODES/PROXY>
           kubectl edit rolebinding -n <NAMESPACE> <ROLEBINDING_NAME>
           ```
           Update `roleRef` to point to the new restricted role for non-admin subjects.

        6. **Re-verify that only trusted admins retain Node `proxy` access**
           * Run on: any machine with `kubectl` access
           ```bash theme={null}
           # Re-audit roles/clusterroles for nodes/proxy
           kubectl get clusterrole -o yaml | grep -En 'nodes/proxy'
           kubectl get role --all-namespaces -o yaml | grep -En 'nodes/proxy'
           ```
           For any remaining `nodes/proxy` entries, repeat the binding review:
           ```bash theme={null}
           kubectl get clusterrolebinding -o yaml | grep -En 'roleRef:|name:|subjects:'
           kubectl get rolebinding --all-namespaces -o yaml | grep -En 'roleRef:|name:|subjects:'
           ```
           Confirm that all roles with `nodes/proxy` are bound only to explicitly approved, trusted administrator identities.
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1. List all Roles/ClusterRoles that reference 'nodes' with 'proxy' access
        # Run on: any machine with kubectl access

        kubectl get clusterroles,roles -A -o json \
          | jq -r '
            .items[]
            | . as $role
            | (.rules[]? // [])
            | select(
                (.resources[]? == "nodes" or .resources[]? == "nodes/proxy") and
                (.verbs[]? == "proxy" or .verbs[]? == "*")
              )
            | "\($role.kind)/\($role.metadata.namespace // "cluster")/\($role.metadata.name)"
          ' | sort -u
        ```

        **What to look for:**\
        Each line is a `ClusterRole` or `Role` that can reach the `nodes` `proxy` sub-resource (either explicitly via `proxy` or implicitly via `*` on verbs). Any entry here must be reviewed; these are the only candidates that can expose the kubelet API through the Node proxy.

        ***

        ```bash theme={null}
        # 2. For each suspicious role, show full rule details
        # Example for a ClusterRole named 'system:node-proxy-access'

        kubectl get clusterrole system:node-proxy-access -o yaml
        ```

        **What to look for in the output:**

        In `rules:`:

        * Problematic resource/verb combinations (need human judgement):

          ```yaml theme={null}
          rules:
          - apiGroups: [""]
            resources:
              - nodes
              - nodes/proxy
            verbs:
              - get
              - list
              - watch
              - proxy        # explicit proxy access
              # or:
              - '*'          # implicitly includes proxy
          ```

        * Higher risk patterns:
          * `resources: ["*"]` with `verbs: ["*"]` or including `proxy`
          * `resources: ["nodes"]` + `verbs: ["*"]`
          * Any role intended for non-admins (developers, CI, apps) that has `nodes` with `proxy` or `*` verbs.

        Roles with such rules and bound to non-admin users/groups/service accounts are likely too permissive.

        ***

        ```bash theme={null}
        # 3. Show which subjects are bound to each identified ClusterRole/Role
        # Replace NAME and NAMESPACE based on step 1 output

        # For ClusterRole bindings:
        kubectl get clusterrolebindings -o json \
          | jq -r '
            .items[]
            | select(.roleRef.kind == "ClusterRole" and .roleRef.name == "system:node-proxy-access")
            | .metadata.name as $crb
            | .subjects[]?
            | "\($crb): \(.kind)/\(.namespace // "cluster")/\(.name)"
          '

        # For Role bindings (namespace-scoped):
        kubectl get rolebindings -A -o json \
          | jq -r '
            .items[]
            | select(.roleRef.kind == "Role" and .roleRef.name == "node-proxy-role")
            | .metadata.namespace as $ns
            | .metadata.name as $rb
            | .subjects[]?
            | "\($ns)/\($rb): \(.kind)/\(.namespace // "cluster")/\(.name)"
          '
        ```

        **What to look for:**

        * Non-admin or broad subjects, for example:
          * `system:authenticated` or `system:unauthenticated`
          * Wildcard groups (e.g., `system:serviceaccounts`, `system:serviceaccounts:<ns>`)
          * Application service accounts in app namespaces
          * External user groups used by regular developers

        Access to `nodes` `proxy` should normally be restricted to a small, trusted administrator group or break-glass account.

        ***

        ```bash theme={null}
        # 4. Cluster-wide view: all roles that can proxy nodes with their bindings
        # (This gives a review worksheet; still needs human judgement)

        kubectl get clusterroles,roles -A -o json \
          | jq -r '
            def has_node_proxy:
              any(.rules[]?; (
                any(.resources[]?; . == "nodes" or . == "nodes/proxy") and
                any(.verbs[]?; . == "proxy" or . == "*")
              ));
            .items[]
            | select(has_node_proxy)
            | {
                kind: .kind,
                name: .metadata.name,
                namespace: (.metadata.namespace // "cluster")
              }
          ' | while read -r line; do
            kind=$(echo "$line" | jq -r '.kind')
            name=$(echo "$line" | jq -r '.name')
            ns=$(echo "$line" | jq -r '.namespace')

            echo "=== $kind $ns/$name ==="

            if [ "$kind" = "ClusterRole" ]; then
              kubectl get clusterrole "$name" -o json \
                | jq '.rules[] | select(
                      ( .resources? // [] | index("nodes") or index("nodes/proxy") ) and
                      ( .verbs? // [] | index("proxy") or index("*") )
                    )'
              echo "-- Bindings --"
              kubectl get clusterrolebindings -o json \
                | jq -r "
                    .items[]
                    | select(.roleRef.kind == \"ClusterRole\" and .roleRef.name == \"$name\")
                    | .metadata.name as \$crb
                    | .subjects[]?
                    | \"\(\$crb): \(.kind)/\(.namespace // \"cluster\")/\(.name)\"
                  "
            else
              kubectl get role -n "$ns" "$name" -o json \
                | jq '.rules[] | select(
                      ( .resources? // [] | index("nodes") or index("nodes/proxy") ) and
                      ( .verbs? // [] | index("proxy") or index("*") )
                    )'
              echo "-- Bindings --"
              kubectl get rolebindings -n "$ns" -o json \
                | jq -r "
                    .items[]
                    | select(.roleRef.kind == \"Role\" and .roleRef.name == \"$name\")
                    | .metadata.name as \$rb
                    | .subjects[]?
                    | \"${ns}/\(\$rb): \(.kind)/\(.namespace // \"cluster\")/\(.name)\"
                  "
            fi
            echo
          done
        ```

        **What to look for:**

        * Any role where:
          * The rule snippet shows `nodes`/`nodes/proxy` with `proxy` or `*` verbs, and
          * Bindings list subjects that are not clearly trusted administrators.

        Those combinations are candidates to tighten or remove based on your access policy.

        ***

        ```bash theme={null}
        # 5. Verification after you adjust RBAC (manual policy decision required)

        kubectl get clusterroles,roles -A -o json \
          | jq -r '
            .items[]
            | . as $role
            | (.rules[]? // [])
            | select(
                (.resources[]? == "nodes" or .resources[]? == "nodes/proxy") and
                (.verbs[]? == "proxy" or .verbs[]? == "*")
              )
            | "\($role.kind)/\($role.metadata.namespace // "cluster")/\($role.metadata.name)"
          ' | sort -u
        ```

        **Success indication:**

        * The output is empty, or
        * Any remaining roles are intentionally approved for trusted administrators only, with bindings you have reviewed and accepted.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Report Roles/ClusterRoles that can access the Node proxy sub‑resource
        # Run from any machine with kubectl access and cluster‑wide RBAC read permissions.

        set -euo pipefail

        echo "=== ClusterRoles with Node proxy access ==="
        kubectl get clusterroles -o json \
          | jq -r '
            .items[]
            | {
                name: .metadata.name,
                rules: .rules[]
              }
            | select(
                (
                  (.rules.resources? // []) | index("nodes/proxy")
                )
                or
                (
                  (.rules.resources? // []) | any(. == "nodes" or . == "nodes/*")
                  and ((.rules.verbs? // []) | any(. == "proxy" or . == "*" ))
                )
              )
            | [
                "ClusterRole: " + .name,
                "  Resources: " + ((.rules.resources // []) | join(", ")),
                "  Verbs:     " + ((.rules.verbs // []) | join(", ")),
                "  API grps:  " + ((.rules.apiGroups // []) | join(", ")),
                ""
              ]
              | .[]
          '

        echo
        echo "=== Roles with Node proxy access (all namespaces) ==="
        kubectl get roles --all-namespaces -o json \
          | jq -r '
            .items[]
            | {
                ns: .metadata.namespace,
                name: .metadata.name,
                rules: .rules[]
              }
            | select(
                (
                  (.rules.resources? // []) | index("nodes/proxy")
                )
                or
                (
                  (.rules.resources? // []) | any(. == "nodes" or . == "nodes/*")
                  and ((.rules.verbs? // []) | any(. == "proxy" or . == "*" ))
                )
              )
            | [
                "Role: " + .ns + "/" + .name,
                "  Resources: " + ((.rules.resources // []) | join(", ")),
                "  Verbs:     " + ((.rules.verbs // []) | join(", ")),
                "  API grps:  " + ((.rules.apiGroups // []) | join(", ")),
                ""
              ]
              | .[]
          '

        echo
        echo "=== Note ==="
        echo "Any entry above represents a candidate for review."
        echo "- High risk: rules that include 'nodes/proxy', 'nodes/*' or verb 'proxy' or '*' on 'nodes'."
        echo "- Ensure only tightly controlled admin roles retain such access; others should be removed or scoped down."
        ```

        Explanation of problematic output:

        * Any listed `ClusterRole` or `Role` is granting some form of Node proxy access.
        * Especially concerning:
          * `resources` contains `nodes/proxy` or `nodes/*`.
          * `resources` contains `nodes` and `verbs` contains `proxy` or `*`.
        * For each such role, review who is bound to it (via `RoleBinding`/`ClusterRoleBinding`) and decide whether they are trusted administrators. If not, adjust or remove the permission manually.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
