Skip to content

Least Privilege RBAC: Scoping & Risk Reduction

Vì Sao Quantum Trọng

Least privilege là core security principle: grant minimum permissions necessary để task. Di Kubernetes:

  • Over-privileged service accounts là common misconfiguration. ví dụ: pod run dưới SA có "*": ["*"] (cluster-admin-equivalent)
  • Blast radius khi compromised — nếu SA token leaked, attacker punya truy cập huge. Least privilege minimize damage scope
  • Compliance & audit — security teams expect principle of least privilege. Over-broad permissions fail audits
  • Operational cost — over-privileged accounts mask underlying permission needs, making refactoring harder

Understanding RBAC scoping mechanisms fundamental để implement effective least privilege.

Privilege Scoping Dimensions

Kubernetes authorization có thể scoped along multiple dimensions:

1. Namespace Scoping

Highest impact dimension. RoleBinding restrict permissions đến single namespace:

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: team-a        # SCOPED: only team-a
  name: deployer
roleRef:
  kind: ClusterRole
  name: deployer
subjects:
- kind: ServiceAccount
  namespace: team-a
  name: my-app

my-app SA trong team-a có thể deploy, không trong team-b hoặc kube-system.

Worst practice:

yaml
kind: ClusterRoleBinding    # Cluster-wide!

Cluster-wide binding = permission everywhere.

2. Verb Scoping

Don't grant ["*"]; grant specific verbs:

yaml
# BAD: Too broad
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["*"]        # Includes watch, create, delete, exec, etc.

# GOOD: Specific verbs
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list"]  # Read-only

Verb minimization principle:

  • create → minimize; requires careful validation
  • delete / deletecollection → minimize; destructive
  • watch → if not needed, don't grant
  • * → never (except để very few roles like cluster-admin)

3. Resource Scoping

Minimize resources per rule:

yaml
# BAD: Too broad
rules:
- apiGroups: [""]
  resources: ["*"]        # All resources
  verbs: ["get", "list"]

# GOOD: Specific resources
rules:
- apiGroups: [""]
  resources: ["pods", "services", "configmaps"]
  verbs: ["get", "list"]

Resource minimization:

  • ["*"] tempting nhưng dangerous
  • Always enumerate specific resources
  • For core API group, be explicit nếu include RBAC objects (roles, rolebindings) — often unintended

4. Resource Names Scoping

Limit đến specific resource instances:

yaml
# Moderately specific
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]

# More specific: only database credentials secret
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["db-credentials"]
  verbs: ["get"]

resourceNames reduce blast radius nếu SA compromised — attacker có thể only read one secret, not all.

5. Subresources Scoping

Fine-grained control via subresources:

yaml
# Pod read-only
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

# Pod logs access (separate permission)
rules:
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]

# Pod exec (most dangerous; grant sparingly)
rules:
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create"]

Each subresource separate permission. Pod access ≠ pod/log access ≠ pod/exec access.

Role Reduction Heuristics

Heuristic 1: Identify Core Task

Start với mà minimal diperlukan để task:

Task: "Deploy application"

Core permissions:

  • apps/deployments: [get, list, create, update, patch]
  • apps/replicasets: [get, list] (owned by deployment)
  • core/pods: [get, list] (for checking pod status)

NOT needed:

  • delete deployments (destroy production)
  • exec pods (debugging; admin task)
  • configmaps (might need for config, but separate permission)

Heuristic 2: Use Namespace Boundaries

Different SAs per namespace = separate RBAC chains:

yaml
# SA trong dev-team namespace
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: dev-team
  name: builder
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev-team
  name: builder
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["create", "update", "delete"]  # OK để dev

---
# SA trong prod-team namespace (separate RBAC)
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: prod-team
  name: builder
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: prod-team
  name: builder
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list"]  # Read-only trong prod

Separate SAs enforces boundary — dev SA cannot touch prod.

Heuristic 3: Separate "Read" từ "Write"

Create distinct roles để authorization separation:

yaml
# Read-only access
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-viewer
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

---
# Write access (separate role)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-editor
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create", "update", "patch", "delete"]

Bind read/write tergantung vào team needs. Some teams chỉ read.

Heuristic 4: Restrict Dangerous Verbs

Verbs mà phải grant sparingly:

VerbDangerWhen Needed
*Cluster-adminBootstrap only
delete, deletecollectionData lossOnly explicit cleanup roles
create (on RBAC objects)Privilege escalationAdmins only
impersonatePrivilege escalationAdmins, delegators only
execDirect pod accessDebugging roles only
logsInformation disclosureDebugging roles only
portforwardNetwork accessDebugging roles only

Multi-Stage Deployment Pattern

Least privilege để multi-stage deployment (dev → staging → prod):

Stage 1: Development (Permissive)

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev
  name: developer
rules:
- apiGroups: ["", "apps", "batch"]
  resources: ["*"]
  verbs: ["*"]  # Full access trong dev

Developers iterate quickly, don't need scoping overhead.

Stage 2: Staging (Moderate)

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: staging
  name: deployer
rules:
# Can deploy, but not delete
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "create", "update", "patch"]

# Can view logs for debugging
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

# Cannot delete
# Cannot exec pods
# Cannot access secrets

Staging deployer limited; tests changes without production-risk exposure.

Stage 3: Production (Restrictive)

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: prod
  name: deployer
rules:
# Can only update specific deployments
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["update", "patch"]
  resourceNames: ["api-server", "web-frontend"]

# Can view, not delete
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

# Cannot delete
# Cannot create
# Cannot exec
# Cannot access secrets

Prod deployer minimal — update existing, view status, no destructive actions.

Blast Radius Analysis

Scenario: SA Token Leaked

Nếu SA token compromise, damage scope limited bởi RBAC permissions.

Over-privileged SA:

yaml
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]

Attacker có thể:

  • Delete entire cluster
  • Extract all secrets
  • Escalate tới other namespaces
  • Disable audit logging

Blast radius: ENTIRE CLUSTER


Least privilege SA:

yaml
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
  # Only trong own namespace (RoleBinding)

Attacker có thể:

  • List pods trong one namespace
  • Infer deployment names, configs

Blast radius: ONE NAMESPACE, READ-ONLY


Quantifying Risk

Risk = (Blast Radius) × (Likelihood of Compromise) × (Damage per Access)

Over-privileged: 100% of cluster × higher likelihood × devastating damage
Least privilege: 5% of cluster × same likelihood × limited damage

Risk reduction: ~95%

Anti-Patterns

Anti-Pattern 1: "Just Use Cluster-Admin"

yaml
# ANTI-PATTERN
kind: ClusterRoleBinding
roleRef:
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: my-app

Problem:

  • Unnecessary blast radius
  • Fails security audits
  • No granularity để debugging
  • Compliance violation

Anti-Pattern 2: Wildcards for "convenience"

yaml
# ANTI-PATTERN
rules:
- resources: ["*"]
  verbs: ["*"]

"Just in case we need it later" → security debt.

Anti-Pattern 3: No Namespace Scoping

yaml
# ANTI-PATTERN: Cluster-wide binding for namespace task
kind: ClusterRoleBinding
subjects:
- kind: ServiceAccount
  namespace: team-a
  name: app

Grants team-a:app access cluster-wide; should be RoleBinding in team-a namespace.

Audit để Least Privilege Violations

Find Over-Broad Permissions

bash
# Find all roles với resource: "*"
kubectl get roles,clusterroles -A -o json | \
  jq '.items[] | select(.rules[]? | .resources[]? == "*")'

# Find all roles với verb: "*"
kubectl get roles,clusterroles -A -o json | \
  jq '.items[] | select(.rules[]? | .verbs[]? == "*")'

# Find ClusterRoleBindings (usually too broad)
kubectl get clusterrolebindings -o wide | grep -v "kube-" | grep -v "system:"

Regular Permission Review

bash
# Extract all permissions từ RBAC
kubectl get roles,clusterroles -A -o json | \
  jq '.items[] | {name: .metadata.name, rules}' | \
  tee rbac-audit.json

# Compare với previous audit để changes
diff <(jq '.[].name' rbac-audit-old.json) \
     <(jq '.[].name' rbac-audit-new.json)

Least Privilege Checklist

☐ Service accounts scoped đến namespace (RoleBinding, not ClusterRoleBinding)?
☐ Verbs minimized (no ["*"])?
☐ Resources enumerated (no ["*"])?
☐ Dangerous verbs (delete, exec, create RBAC) granted minimally?
☐ Subresources separated (pods vs pods/exec vs pods/log)?
☐ ResourceNames used để single-resource restrictions?
☐ Read permissions separate từ write permissions?
☐ Cluster-admin không used except admins?
☐ Impersonate permissions explicitly granted?
☐ Regular audit từ permissions?

Summary

Least privilege RBAC core principles:

  1. Scope namespace — RoleBinding to namespace, not ClusterRoleBinding
  2. Minimize verbs — enumerate specific actions
  3. Enumerate resources — no ["*"]
  4. Separate read/write — different roles
  5. Use subresources — pod ≠ pod/exec
  6. Audit regularly — detect drift

Implementing least privilege:

  • Identify core task
  • Grant minimum permissions
  • Test with actual workload
  • Regular permission review
  • Enforce via policy (e.g., fail without scoped bindings)

Tham Khảo