Least Privilege

The principle of least privilege means granting only the minimum permissions necessary for a user or application to perform its function. In Kubernetes, this reduces the attack surface and limits the damage from compromised accounts or applications.

Why Least Privilege Matters

Imagine giving every employee a master key to your entire building. If one key is lost or stolen, the entire building is at risk. Least privilege is like giving each employee a key only to the rooms they actually need.

In Kubernetes terms:

  • A compromised application with excessive permissions can affect other namespaces or cluster components
  • Accidental mistakes have limited scope when permissions are restricted
  • Compliance requirements often mandate least privilege
  • Security audits are easier when permissions are minimal and documented

How to Implement Least Privilege

1. Start with No Permissions

Begin by creating users and ServiceAccounts with no permissions, then add only what’s needed:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app
  namespace: production

This ServiceAccount has zero permissions until you create a RoleBinding.

2. Grant Specific Permissions

Instead of using broad roles like admin or edit, create custom roles with exact permissions:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: app-specific
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["app-config"]  # Only this specific ConfigMap
  verbs: ["get"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  resourceNames: ["my-app"]  # Only this specific Deployment
  verbs: ["get", "patch"]

3. Use Namespace Isolation

Limit permissions to specific namespaces:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: developer-access
  namespace: development  # Only this namespace
subjects:
- kind: User
  name: developer
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: developer
  apiGroup: rbac.authorization.k8s.io

Auditing Permissions

Check Current Permissions

Use kubectl auth can-i to see what a user or ServiceAccount can do:

# List all permissions for a ServiceAccount
kubectl auth can-i --list \
  --as=system:serviceaccount:production:my-app \
  --namespace=production

# Test specific permission
kubectl auth can-i create pods \
  --as=system:serviceaccount:production:my-app \
  --namespace=production

Analyze Role Bindings

Find all bindings for a user or ServiceAccount:

# Find all RoleBindings for a user
kubectl get rolebindings,clusterrolebindings --all-namespaces \
  -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .subjects[*]}{.kind}{"/"}{.name}{"\n"}{end}{end}' \
  | grep "User/alice"

# Find all bindings for a ServiceAccount
kubectl get rolebindings --all-namespaces \
  -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .subjects[*]}{.kind}{"/"}{.name}{"\n"}{end}{end}' \
  | grep "ServiceAccount/my-app"

Tools for Permission Analysis

kubectl-who-can

A plugin that shows who can perform specific actions:

kubectl who-can create pods --namespace=production
kubectl who-can delete deployments --namespace=production

rbac-lookup

Lists all permissions for a user or ServiceAccount:

rbac-lookup alice
rbac-lookup system:serviceaccount:production:my-app

rakkess

Shows access matrix for resources:

rakkess --namespace production

Common Mistakes to Avoid

1. Using Cluster-admin Unnecessarily

Bad: Giving a developer cluster-admin to “make things easier”

Good: Create a namespace-scoped role with only needed permissions

2. Wildcard Verbs

Bad: Using verbs: ["*"] when only read access is needed

Good: Specify exact verbs: verbs: ["get", "list", "watch"]

3. ClusterRole for Namespace Resources

Bad: Using ClusterRoleBinding for namespace-scoped resources

Good: Use RoleBinding with a Role or ClusterRole

4. Overly Broad Resource Access

Bad: resources: ["*"] when only specific resources are needed

Good: List specific resources: resources: ["pods", "services"]

Permission Patterns

Read-Only Access

For developers who need to debug but not modify:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: read-only
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]

Application Permissions

For an application that only needs to read its own ConfigMap:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: app-reader
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["app-config"]
  verbs: ["get"]

CI/CD Permissions

For a CI/CD system that deploys to specific namespaces:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: cicd-deployer
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

Regular Audits

Schedule regular permission reviews:

  1. Monthly - Review new RoleBindings and ClusterRoleBindings
  2. Quarterly - Audit all permissions for unused or excessive access
  3. Annually - Comprehensive security review with compliance teams

Audit Checklist

  • Are there any ClusterRoleBindings that should be RoleBindings?
  • Do any roles use wildcard verbs or resources unnecessarily?
  • Are ServiceAccounts using minimal permissions?
  • Can any permissions be scoped to specific resource names?
  • Are there unused roles or bindings that can be removed?
  • Do developers have more permissions than needed?

Remediation Steps

When you find excessive permissions:

  1. Document the current state - Record what permissions exist and why
  2. Create a minimal role - Define the exact permissions needed
  3. Test in a non-production environment - Verify the new permissions work
  4. Update the binding - Switch to the new minimal role
  5. Monitor for issues - Watch for permission denied errors
  6. Remove old role - Once confirmed working, delete the old role

See Also