Skip to main content
Navigation
HomeTechnical ReferenceJournalGitHubGitHub
Sidebar — toggle document categories via the logo
Categories

Kubernetes RBAC & User Management

Overview

Kubernetes controls access through Role-Based Access Control (RBAC). Identities can be regular users (managed outside the cluster via x509 certificates or OIDC) or ServiceAccounts (managed inside the cluster). This reference covers creating users, ServiceAccounts, Roles, ClusterRoles, and binding them with the appropriate permissions.

ServiceAccounts

ServiceAccounts are Kubernetes-managed identities for pods and CI/CD tooling.

Creating and using ServiceAccounts

ServiceAccounts identify pods and automation when they talk to the API server. Create one and reference it in a pod's serviceAccountName so the pod authenticates with that identity instead of the default one.

# Create a ServiceAccount
kubectl create sa my-app

# View details
kubectl get sa my-app -o yaml

# Reference it from a pod via serviceAccountName:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: default
---
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
serviceAccountName: my-app
containers:
- name: app
image: nginx

Token access

ServiceAccounts authenticate with tokens. In Kubernetes 1.24+ you mint short-lived, expiring tokens on demand with kubectl create token, or you create a long-lived token Secret manually for tools that need stable credentials.

# Create a long-lived token (Kubernetes 1.24+)
kubectl create token my-app --duration=720h

# Or via a Secret (older method, still works if manually created)
apiVersion: v1
kind: Secret
metadata:
name: my-app-token
annotations:
kubernetes.io/service-account.name: my-app
type: kubernetes.io/service-account-token

Roles and ClusterRoles

Role (namespace-scoped)

A Role grants permissions within a single namespace. This example gives read access to pods and logs, plus configmaps and secrets, but only in the default namespace — nothing outside it.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get"]

ClusterRole (cluster-scoped or cross-namespace)

A ClusterRole grants permissions cluster-wide or across all namespaces. Use it for cluster-scoped resources like nodes, or as a reusable set of rules that namespace-scoped RoleBindings can reference.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes", "nodes/metrics"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list"]

Aggregated ClusterRoles

Aggregation lets you compose one ClusterRole from rules contributed by other ClusterRoles that carry a matching aggregate-to-* label. The built-in view, edit, and admin roles are built this way, and your custom rules merge into them automatically.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: app-monitoring
labels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["apps"]
resources: ["deployments/status"]
verbs: ["get"]

Bindings

RoleBinding (namespace-scoped)

A RoleBinding attaches a Role to subjects — users, groups, or ServiceAccounts — within one namespace. It's the final step that actually gives an identity permissions; without a binding, a Role is just a definition.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: ServiceAccount
name: my-app
namespace: default
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding (cluster-wide)

A ClusterRoleBinding attaches a ClusterRole to subjects across the whole cluster. Use it sparingly — it grants the bound permissions in every namespace, not just one.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-nodes
subjects:
- kind: Group
name: ops-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-reader
apiGroup: rbac.authorization.k8s.io

User certificates

Since Kubernetes doesn't manage user objects, users authenticate with x509 certificates signed by the cluster CA.

Creating a user certificate

Because Kubernetes has no user objects, human users authenticate with x509 certificates signed by the cluster CA. Generate a key and CSR, sign it, then add a kubeconfig context so the user can access the cluster.

# Generate a private key
openssl genrsa -out jane.key 2048

# Create a Certificate Signing Request (CSR)
openssl req -new -key jane.key -out jane.csr -subj "/CN=jane/O=developers"

# Sign the certificate with the cluster CA
openssl x509 -req -in jane.csr \
-CA /etc/kubernetes/pki/ca.crt \
-CAkey /etc/kubernetes/pki/ca.key \
-CAcreateserial \
-out jane.crt -days 365

# Set up kubectl context
kubectl config set-credentials jane --client-certificate=jane.crt --client-key=jane.key
kubectl config set-context jane-context --cluster=my-cluster --user=jane
kubectl config use-context jane-context

Check access

kubectl auth can-i answers "can this identity do this?" without actually performing the request. It's the quickest way to verify RBAC changes and to debug forbidden errors, and it supports --as to impersonate any user or group.

kubectl auth can-i create pods
kubectl auth can-i delete nodes --as jane
kubectl auth can-i list deployments --as-group developers

Common RBAC patterns

Read-only across all namespaces

This is the standard read-only pattern for auditors, dashboards, and support staff: a ClusterRole allowing get/list/watch on everything, bound to a group cluster-wide. It provides exactly what the built-in view ClusterRole gives you.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: view-only
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: view-only-binding
subjects:
- kind: Group
name: auditors
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view-only
apiGroup: rbac.authorization.k8s.io

CI/CD deployer (namespace-scoped)

CI/CD systems deploy on your behalf and should be limited to one namespace. This pattern gives a ServiceAccount from the ci namespace full manage access to workloads in production, without any cluster-wide privileges.

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

Namespace admin

Give a team full control of its own namespace — everything except cluster-scoped objects and resource quotas. This lets each team self-service within team-a without a cluster-admin role.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: namespace-admin
namespace: team-a
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
- apiGroups: [""]
resources: ["resourcequotas", "limitranges"]
verbs: ["get", "list"]

Troubleshooting

When permissions don't behave as expected, inspect the bindings that exist and check the effective permissions with kubectl auth can-i --list. For requests that are denied, the API server's audit log shows exactly which rule (or missing rule) was responsible.

# List all bindings in a namespace
kubectl get rolebindings,clusterrolebindings -n default

# Describe a binding
kubectl describe rolebinding read-pods -n default

# Check effective permissions
kubectl auth can-i --list -n default
kubectl auth can-i --list --as jane

# Audit RBAC (requires audit logging configured)
# Check /var/log/kube-apiserver-audit.log for denied requests

See also