Skip to main content
K8sCalc
kubernetes6 May 2026

Kubernetes RBAC Explained with Real Examples

A practical walkthrough of Kubernetes RBAC — covering Roles, ClusterRoles, bindings, and real-world patterns for read-only users, CI service accounts, and namespace admins.

Kubernetes RBAC is one of those topics that looks simple in the docs and bites you in production. The concepts are straightforward — but the defaults are permissive, the pitfalls are common, and most teams only discover their gaps after an incident.

This guide covers RBAC from the ground up with real manifests you can use today. Use the Kubernetes RBAC Generator to scaffold bindings for your specific use case.

Core Concepts

RBAC in Kubernetes has four building blocks:

Subjects — who gets permissions. A subject is one of:

  • User — a human identity (often from OIDC/LDAP, not created as a K8s object)
  • Group — a group of users
  • ServiceAccount — an in-cluster identity for pods and automation

Roles and ClusterRoles — what is allowed. These define a set of rules: which API groups, resources, and verbs are permitted.

RoleBindings and ClusterRoleBindings — the glue between subjects and roles.

The key namespace distinction:

ObjectScope
RoleSingle namespace
ClusterRoleCluster-wide
RoleBindingBinds any Role or ClusterRole to subjects within one namespace
ClusterRoleBindingBinds a ClusterRole to subjects cluster-wide

A ClusterRole bound via a RoleBinding only grants permissions within that namespace. This is a useful pattern: define a reusable ClusterRole (e.g., namespace-admin), then bind it per-namespace with RoleBinding.

Verbs and Resources

RBAC rules control access by API group, resource type, and verb:

rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Common verbs:

VerbWhat it allows
getRead a single resource by name
listList all resources of a type
watchStream changes (used by controllers)
createCreate new resources
updateReplace an existing resource
patchPartially update a resource
deleteDelete a resource
deletecollectionDelete all resources matching a selector

To find the API group for a resource:

kubectl api-resources --sort-by=name | grep deployment
# NAME          SHORTNAMES   APIVERSION    NAMESPACED   KIND
# deployments   deploy       apps/v1       true         Deployment

The APIVERSION column gives you the API group (apps). Core resources like pods, services, and configmaps use "" (empty string) as the API group.

Pattern 1: Read-Only User

A common need: give a developer read-only access to a specific namespace so they can inspect running workloads without being able to change anything.

# ClusterRole that can be reused across namespaces
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-readonly
rules:
  - apiGroups: ["", "apps", "batch", "autoscaling"]
    resources:
      - pods
      - pods/log
      - deployments
      - replicasets
      - statefulsets
      - daemonsets
      - jobs
      - cronjobs
      - horizontalpodautoscalers
      - services
      - endpoints
      - configmaps
      - persistentvolumeclaims
      - events
    verbs: ["get", "list", "watch"]
---
# Bind it to a user in one namespace only
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: alice-readonly
  namespace: production
subjects:
  - kind: User
    name: alice@example.com   # Must match the identity from your OIDC provider
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: namespace-readonly
  apiGroup: rbac.authorization.k8s.io

Alice can now run kubectl get pods -n production and kubectl logs but cannot delete, scale, or modify anything.

Pattern 2: CI/CD Service Account

A CI pipeline (GitHub Actions, GitLab CI, ArgoCD) needs a service account with just enough permission to deploy. Scope it to the namespace it deploys into.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: github-actions
  namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deploy-role
  namespace: production
rules:
  - apiGroups: ["apps"]
    resources: ["deployments", "statefulsets"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "configmaps", "secrets"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: github-actions-deploy
  namespace: production
subjects:
  - kind: ServiceAccount
    name: github-actions
    namespace: production
roleRef:
  kind: Role
  name: deploy-role
  apiGroup: rbac.authorization.k8s.io

Generate a token for this service account and base64-encode it for your CI secret:

kubectl create token github-actions -n production --duration=87600h

This service account cannot read from other namespaces, cannot modify RBAC, and cannot touch cluster-scoped resources.

Pattern 3: Namespace Admin

A team that owns a namespace needs full control within it — but nothing outside it.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-alpha-admin
  namespace: team-alpha
subjects:
  - kind: Group
    name: team-alpha    # From your OIDC provider's group claims
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: admin           # Built-in ClusterRole — full namespace access
  apiGroup: rbac.authorization.k8s.io

Kubernetes ships with four built-in ClusterRoles worth knowing:

Built-in RoleWhat it grants
cluster-adminFull cluster access, including RBAC modification
adminFull namespace access (most resources, including RBAC within namespace)
editRead/write on most namespace resources, no RBAC
viewRead-only on most namespace resources, no secrets

Security Pitfalls to Avoid

1. Wildcard Verbs and Resources

# Never do this
rules:
  - apiGroups: ["*"]
    resources: ["*"]
    verbs: ["*"]

This is functionally equivalent to cluster-admin. Wildcards bypass the entire purpose of RBAC. Always enumerate the specific resources and verbs you need.

2. cluster-admin Abuse

cluster-admin is regularly granted because it "just works" — but it means the subject can modify RBAC itself, read all secrets cluster-wide, and delete any resource. Reserve it for break-glass emergency users and the human running cluster upgrades. Never bind it to a service account used by automation.

Check who has it:

kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'

3. Secrets Access as an Afterthought

Secrets are not covered by the view ClusterRole intentionally — but the edit and admin ClusterRoles do include them. If you give a service account edit access to a namespace, it can read all secrets in that namespace, including other services' credentials. Use a purpose-specific Role with explicit resources instead of built-in ClusterRoles for service accounts.

4. Forgetting pods/exec and pods/log

These are sub-resources and must be explicitly listed:

- apiGroups: [""]
  resources: ["pods/exec", "pods/log", "pods/portforward"]
  verbs: ["create", "get"]

pods/exec lets a user exec into any pod in the namespace — which is equivalent to shell access on the node if the pod has host mounts or privileged containers. Grant it only to operators who genuinely need it.

5. Not Auditing Bindings

RBAC permissions accumulate over time. Run periodic audits:

# See all subjects bound to cluster-admin
kubectl get clusterrolebindings -o wide | grep cluster-admin

# See all role bindings in a namespace
kubectl get rolebindings -n production -o wide

# Check what permissions a service account has
kubectl auth can-i --list --as=system:serviceaccount:production:github-actions -n production

The kubectl auth can-i --list approach is the fastest way to verify a service account's actual effective permissions — use it after creating bindings to confirm they're correct.

For generating production-ready RBAC manifests for common patterns, the Kubernetes RBAC Generator covers read-only users, CI accounts, namespace admins, and custom roles.