---
title: "RBAC Patterns: Practical Access Control for Kubernetes"
description: "Kubernetes RBAC fundamentals and real-world patterns for read-only access, CI/CD service accounts, monitoring, and least-privilege pod identities."
url: https://agent-zone.ai/knowledge/kubernetes/rbac-patterns/
section: knowledge
date: 2026-02-22
categories: ["kubernetes"]
tags: ["rbac","security","service-accounts","access-control"]
skills: ["rbac-configuration","service-account-management","security-hardening"]
tools: ["kubectl"]
levels: ["intermediate"]
word_count: 791
formats:
  json: https://agent-zone.ai/knowledge/kubernetes/rbac-patterns/index.json
  html: https://agent-zone.ai/knowledge/kubernetes/rbac-patterns/?format=html
  api: https://api.agent-zone.ai/api/v1/knowledge/search?q=RBAC+Patterns%3A+Practical+Access+Control+for+Kubernetes
---


# RBAC Patterns

Kubernetes RBAC controls who can do what to which resources. It is built on four objects: Roles, ClusterRoles, RoleBindings, and ClusterRoleBindings. Getting RBAC right means understanding how these four pieces compose and knowing the common patterns that cover 90% of real-world needs.

## The Four RBAC Objects

**Role** -- Defines permissions within a single namespace. Lists API groups, resources, and allowed verbs.

**ClusterRole** -- Defines permissions cluster-wide or for non-namespaced resources (nodes, persistent volumes, namespaces themselves).

**RoleBinding** -- Grants a Role or ClusterRole to a user, group, or ServiceAccount within a specific namespace.

**ClusterRoleBinding** -- Grants a ClusterRole to a subject across all namespaces.

The key insight: a ClusterRole combined with a RoleBinding limits that ClusterRole's permissions to the binding's namespace. This is the most useful pattern because you define permissions once (ClusterRole) and grant them selectively per namespace (RoleBinding).

## Pattern 1: Read-Only Namespace Access

Give a developer or team read-only visibility into a production namespace.

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-reader
rules:
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
  resources: ["pods", "services", "deployments", "replicasets",
              "statefulsets", "daemonsets", "jobs", "cronjobs",
              "configmaps", "ingresses", "events"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-team-readonly
  namespace: payments-prod
subjects:
- kind: Group
  name: dev-team
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: namespace-reader
  apiGroup: rbac.authorization.k8s.io
```

Note `pods/log` as a sub-resource: without it, users can see pods but cannot read logs. Secrets are deliberately excluded -- read access to Secrets is effectively the same as having the values.

## Pattern 2: CI/CD Deploy Service Account

A service account for your CI/CD pipeline that can deploy applications but cannot read Secrets or modify RBAC.

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-deploy
  namespace: payments-prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: payments-prod
rules:
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["services", "configmaps"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["batch"]
  resources: ["jobs"]
  verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: ["networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-deploy-binding
  namespace: payments-prod
subjects:
- kind: ServiceAccount
  name: ci-deploy
  namespace: payments-prod
roleRef:
  kind: Role
  name: deployer
  apiGroup: rbac.authorization.k8s.io
```

Generate a short-lived token for CI:

```bash
kubectl create token ci-deploy -n payments-prod --duration=1h
```

## Pattern 3: Monitoring Access

Prometheus and similar tools need to scrape metrics across all namespaces but should not be able to modify anything.

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-reader
rules:
- apiGroups: [""]
  resources: ["pods", "nodes", "services", "endpoints", "namespaces"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods/proxy"]
  verbs: ["get"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: monitoring-reader-binding
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: monitoring
roleRef:
  kind: ClusterRole
  name: monitoring-reader
  apiGroup: rbac.authorization.k8s.io
```

This uses a ClusterRoleBinding because monitoring needs cluster-wide access. The `nonResourceURLs` rule grants access to the `/metrics` endpoint on the API server itself.

## The Default Service Account Problem

Every namespace has a `default` ServiceAccount, and every pod that does not specify one runs as `default`. This account may have more permissions than you expect. Disable its token mounting and create dedicated service accounts instead:

```bash
kubectl patch serviceaccount default -n payments-prod \
  -p '{"automountServiceAccountToken": false}'
```

For pods that do not need API access (most application pods), disable token mounting:

```yaml
spec:
  automountServiceAccountToken: false
  containers:
  - name: app
    image: my-app:latest
```

For pods that need API access, create a dedicated service account with minimal permissions:

```yaml
spec:
  serviceAccountName: my-app-sa
  containers:
  - name: app
    image: my-app:latest
```

## Debugging RBAC with kubectl auth can-i

The `can-i` subcommand tests whether a subject has a specific permission. This is the fastest way to debug access issues.

**Check your own permissions:**

```bash
kubectl auth can-i create deployments -n payments-prod
# yes

kubectl auth can-i delete secrets -n payments-prod
# no
```

**Check another user or service account:**

```bash
kubectl auth can-i get pods -n payments-prod \
  --as=system:serviceaccount:payments-prod:ci-deploy
# yes

kubectl auth can-i delete namespaces \
  --as=system:serviceaccount:payments-prod:ci-deploy
# no
```

**List all permissions for a subject:**

```bash
kubectl auth can-i --list -n payments-prod \
  --as=system:serviceaccount:payments-prod:ci-deploy
```

This outputs a table of every resource and verb the subject has access to in that namespace.

**Check what ClusterRoles and RoleBindings apply to a subject:**

```bash
# Find all bindings that reference a specific service account
kubectl get rolebindings,clusterrolebindings -A -o json | \
  jq '.items[] | select(.subjects[]? | .name == "ci-deploy") | .metadata.name'
```

## Least Privilege Checklist

1. Never grant `*` (wildcard) verbs or resources unless you are building a cluster admin role.
2. Exclude Secrets from read-only roles. Secret access should be explicit and limited.
3. Disable automatic service account token mounting on the default service account.
4. Use short-lived tokens (`kubectl create token`) instead of long-lived secrets.
5. Prefer namespaced RoleBindings over ClusterRoleBindings. Cluster-wide access should be the exception.
6. Audit with `kubectl auth can-i --list` after making changes to verify the effective permissions match your intent.

