Back to Checks
kubernetes

User or group has no get, list, or watch permissions on the nodes/proxy sub-resource

rbac_minimize_node_proxy_subresource_access
Service: rbacSeverity: high
OfficialBuilt-inVerified
Categories:identity-accesscluster-security
Resource Type:ClusterRoleBinding

RBAC permissions to the nodes/proxy subresource are analyzed. Any user or group granted get, list, or watch via cluster-wide bindings is reported.

Risk

Access to nodes/proxy exposes the Kubelet API through the apiserver proxy, which can bypass audit and admission controls. An adversary could execute commands in pods, read sensitive data, and pivot across nodes, impacting confidentiality and integrity; broad access also risks node-wide service disruption.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler kubernetes --checks rbac_minimize_node_proxy_subresource_access

Recommendation

Apply least privilege: avoid granting any verbs on nodes/proxy to users or groups. If access is unavoidable, limit it to trusted admins, scope narrowly, make it time-bound, and enforce separation of duties. Prefer namespace roles over cluster-wide bindings and review RBAC regularly as part of defense in depth.

Remediation

CLI

kubectl delete clusterrolebinding <example_resource_name>

Terraform

# Remove nodes/proxy access by defining the ClusterRole without that permission
resource "kubernetes_cluster_role" "<example_resource_name>" {
  metadata {
    name = "<example_resource_name>"
  }
  # Critical fix: no rule granting get/list/watch on "nodes/proxy" so subjects won't have that access
}

Other

1Identify the ClusterRoleBinding that grants the user/group access: in your RBAC list, locate the binding referencing a ClusterRole that includes resources: nodes/proxy with verbs get, list, or watch
2Edit the ClusterRole: run `kubectl edit clusterrole <example_resource_name>`
3In `rules`, remove any entry where `resources` includes `nodes/proxy` and `verbs` includes `get`, `list`, or `watch`; save and exit
4If you cannot modify the role (shared role), delete the specific ClusterRoleBinding that attaches it to the user/group: `kubectl delete clusterrolebinding <example_resource_name>`

Check Code

from prowler.lib.check.models import Check, Check_Report_Kubernetes
from prowler.providers.kubernetes.services.rbac.lib.role_permissions import (
    is_rule_allowing_permissions,
)
from prowler.providers.kubernetes.services.rbac.rbac_client import rbac_client

verbs = ["get", "list", "watch"]
resources = ["nodes/proxy"]


class rbac_minimize_node_proxy_subresource_access(Check):
    def execute(self) -> Check_Report_Kubernetes:
        findings = []
        # Collect unique subjects and the ClusterRole names bound to them
        subjects_bound_roles = {}
        for crb in rbac_client.cluster_role_bindings.values():
            for subject in crb.subjects:
                # CIS benchmarks scope these checks to human identities only
                if subject.kind in ["User", "Group"]:
                    key = (subject.kind, subject.name, subject.namespace)
                    if key not in subjects_bound_roles:
                        subjects_bound_roles[key] = (subject, set())
                    subjects_bound_roles[key][1].add(crb.roleRef.name)

        cluster_roles_by_name = {
            cr.metadata.name: cr for cr in rbac_client.cluster_roles.values()
        }
        for _, (subject, role_names) in subjects_bound_roles.items():
            report = Check_Report_Kubernetes(metadata=self.metadata(), resource=subject)
            report.resource_name = f"{subject.kind}:{subject.name}"
            report.resource_id = f"{subject.kind}/{subject.name}"
            report.status = "PASS"
            report.status_extended = f"User or group '{subject.name}' does not have access to the node proxy sub-resource."
            for role_name in role_names:
                cr = cluster_roles_by_name.get(role_name)
                if cr and is_rule_allowing_permissions(cr.rules, resources, verbs):
                    report.status = "FAIL"
                    report.status_extended = f"User or group '{subject.name}' has access to the node proxy sub-resource."
                    break
            findings.append(report)

        return findings