User or group does not have permission to create PersistentVolumes
Kubernetes RBAC mapping of users or groups with the create verb on persistentvolumes through ClusterRoleBindings.
Shows which principals can provision PersistentVolumes cluster-wide.
Risk
With create on persistentvolumes, a principal can define hostPath PVs and mount them via claims, exposing node files and bypassing pod safeguards.
Results include secret disclosure and filesystem tampering (confidentiality/integrity), and potential node compromise affecting availability.
Run this check with Prowler
Run in Prowler Cloudprowler kubernetes --checks rbac_minimize_pv_creation_accessRecommendation
Enforce least privilege: permit create on persistentvolumes only for trusted storage controllers and select administrators.
Adopt dynamic provisioning, maintain separation of duties, and use defense in depth (Pod Security and admission policies) to prevent unsafe volumes like hostPath.
Remediation
CLI
kubectl delete clusterrolebinding <example_resource_name>Terraform
resource "kubernetes_cluster_role" "<example_resource_name>" {
metadata { name = "<example_resource_name>" }
rule {
api_groups = [""]
resources = ["persistentvolumes"]
verbs = ["get", "list", "watch"]
# Critical: exclude "create" on persistentvolumes to prevent PV creation
# This removes the permission that caused the FAIL finding.
}
}Other
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 = ["create"]
resources = ["persistentvolumes"]
class rbac_minimize_pv_creation_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 create PersistentVolumes."
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 create PersistentVolumes."
break
findings.append(report)
return findings