K8s Security Pro
#04 RBAC free

Least Privilege RBAC

A namespace-scoped Role and RoleBinding implementing least-privilege access control with read-only permissions for pods.

CIS Benchmark
5.1.15.1.3
MITRE ATT&CK
T1078T1078.004

Overview

This template creates a namespace-scoped Role with minimal read-only permissions (get, watch, list on pods) and a RoleBinding that assigns it to a specific ServiceAccount. It demonstrates the principle of least privilege: grant only the exact permissions an application needs.

Security threat addressed: Overly permissive RBAC roles (especially cluster-admin or wildcard permissions) allow attackers to escalate privileges, read secrets, create privileged pods, and take full control of the cluster.

When to use: Use this as a starting point for every application that needs Kubernetes API access. Customize the resources and verbs to match your application’s actual requirements.

Threat Model

  • Blast radius containment: If a pod’s service account token is stolen, the attacker can only perform the specific actions defined in the Role, not cluster-wide operations.
  • Privilege escalation prevention: Namespace-scoped Roles prevent lateral movement to other namespaces. No wildcard permissions mean no unintended access to Secrets or other sensitive resources.
  • Credential theft limitation: Even if an attacker obtains the service account token, read-only access to pods provides minimal value for further attacks.

MITRE ATT&CK:

  • T1078 — Valid Accounts: Wildcard permissions allow reading Secrets, creating privileged pods, and other dangerous operations.
  • T1078.004 — Valid Accounts: Cloud Accounts: Cluster-admin grants full control, equivalent to root on every node.

Real-world scenario: An attacker compromises a web application and steals the mounted service account token. With least-privilege RBAC, they can only list pods in one namespace, rather than reading secrets or creating backdoor deployments cluster-wide.

YAML Source

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
  labels:
    app.kubernetes.io/name: k8s-security
    app.kubernetes.io/part-of: k8s-security-pro
    app.kubernetes.io/managed-by: k8s-security-pro
rules:
# LEAST PRIVILEGE PRINCIPLE
# Only grant the specific verbs and resources needed.
# NEVER use wildcards ('*') in production roles.
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods", "pods/log"] # Specific resources only
  verbs: ["get", "watch", "list"] # Read-only access. No 'create', 'delete', or 'update'.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
  labels:
    app.kubernetes.io/name: k8s-security
    app.kubernetes.io/part-of: k8s-security-pro
    app.kubernetes.io/managed-by: k8s-security-pro
subjects:
- kind: ServiceAccount
  name: my-app-sa # The ServiceAccount your app uses
  namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
# If an attacker compromises a pod with a 'cluster-admin' or broad role,
# they effectively own your entire cluster. By limiting permissions, you contain the breach.

Installation

kubectl:

kubectl apply -f 04_least_privilege_rbac.yaml

Helm:

helm install k8s-security ./charts/k8s-security -f values-prod.yaml

Kustomize:

kubectl apply -k kustomize/overlays/prod

Verification

# Verify the Role exists and check its permissions
kubectl get role pod-reader -n <namespace> -o yaml

# Verify the RoleBinding
kubectl get rolebinding read-pods -n <namespace> -o yaml

# Test permissions with the ServiceAccount (should succeed)
kubectl auth can-i list pods -n <namespace> --as=system:serviceaccount:<namespace>:my-app-sa

# Test that write access is denied (should return "no")
kubectl auth can-i create pods -n <namespace> --as=system:serviceaccount:<namespace>:my-app-sa

# Test that secrets access is denied (should return "no")
kubectl auth can-i get secrets -n <namespace> --as=system:serviceaccount:<namespace>:my-app-sa

# Audit all ClusterRoleBindings with cluster-admin
kubectl get clusterrolebindings -o jsonpath='{range .items[?(@.roleRef.name=="cluster-admin")]}{.metadata.name}{"\t"}{.subjects}{"\n"}{end}'

CIS Benchmark References

  • 5.1.1 — Ensure that the cluster-admin role is only used where required. This template demonstrates proper scoped roles as an alternative to cluster-admin.
  • 5.1.3 — Minimize wildcard use in Roles and ClusterRoles. This template uses explicit verb and resource lists, never wildcards.

MITRE ATT&CK References

  • T1078 — Valid Accounts: Wildcard RBAC permissions allow reading Secrets, creating privileged pods, and escalating to cluster-admin. Least-privilege roles prevent this.
  • T1078.004 — Valid Accounts: Cloud Accounts: Cluster-admin grants full control equivalent to root on every node. Namespace-scoped roles contain the blast radius.

Further Reading