Roles and RoleBindings
You want to give a developer read-only access to Pods in the dev namespace. They should not see Secrets or modify anything. RBAC does this with two objects: a Role that defines what is allowed, and a RoleBinding that connects the Role to a subject. Neither object on its own does anything; you need both.
Start by creating the dev namespace in the simulated cluster:
kubectl create namespace devThe Role object
A Role is a namespaced resource. It holds a list of rules, where each rule combines three elements: apiGroups, resources, and verbs. The Role grants only the listed verbs on the listed resources, and only within the namespace where the Role lives. A Role in dev has no effect in production.
Build the manifest one field at a time. Create the file:
nano dev-role.yamlThe kind field tells the API server this is a Role, not a ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1kind: RoleThe metadata section anchors the Role in the right namespace:
metadata: name: pod-reader namespace: devThe rules section is where the permissions live. Each rule lists an apiGroups array (use "" for core resources like Pods), a resources array, and a verbs array:
rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"]Full file to write:
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: pod-reader namespace: devrules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"]kubectl apply -f dev-role.yamlkubectl get roles -n devYou should see pod-reader listed. The Role exists, but nothing uses it yet. A Role without a RoleBinding grants access to nobody.
What is the minimum Role definition to allow a subject to list and get ConfigMaps in a namespace?
Try it: Write a Role manifest with resources: ["configmaps"] and verbs: ["get", "list"], apply it, and verify with kubectl get roles -n dev.
Reveal answer
The apiGroups must be [""] (ConfigMaps are a core resource), resources must include "configmaps", and verbs must include at least "get" and "list". Watch is optional but commonly added for controllers that need to observe changes.
The RoleBinding object
A RoleBinding connects a Role to one or more subjects. The binding lives in the same namespace as the Role it references. A RoleBinding cannot reference a Role in a different namespace.
nano dev-rolebinding.yamlThe subjects array lists who receives the permissions. Each entry has a kind (User, Group, or ServiceAccount), a name, and an apiGroup:
subjects: - kind: User name: developer apiGroup: rbac.authorization.k8s.ioThe roleRef points at the Role being bound. Once a RoleBinding is created, roleRef is immutable. To change it, delete the binding and recreate it:
roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.ioFull manifest:
apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: pod-reader-binding namespace: devsubjects: - kind: User name: developer apiGroup: rbac.authorization.k8s.ioroleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.iokubectl apply -f dev-rolebinding.yamlkubectl get rolebindings -n devNow developer can get, list, and watch Pods in dev. They still cannot touch Secrets, ConfigMaps, or resources in any other namespace.
Binding a Role to a ServiceAccount
The subjects entry changes slightly when the subject is a ServiceAccount. A ServiceAccount belongs to a namespace, so you must specify it:
subjects: - kind: ServiceAccount name: my-app namespace: devA common mistake is omitting the namespace field on a ServiceAccount subject. If namespace is missing, the API server looks for the ServiceAccount in the namespace of the RoleBinding itself. If the ServiceAccount lives in a different namespace, the binding silently fails to grant access. The binding is created successfully, but the permissions never take effect for that subject. Always specify namespace explicitly when binding to a ServiceAccount.
You created a RoleBinding in namespace dev that binds to ServiceAccount reporter with namespace: staging. The Role grants get pods. When reporter in staging tries to get pods in dev, access is denied. Why?
Reveal answer
The API server resolves the ServiceAccount identity as system:serviceaccount:staging:reporter. The RoleBinding subjects entry has namespace: staging, which means the binding correctly identifies that ServiceAccount. The issue is the opposite: if you wrote the wrong namespace, it would look for a ServiceAccount in the wrong place. In the correct scenario, the binding should work. Double-check that the name matches exactly, and use kubectl auth can-i with --as system:serviceaccount:staging:reporter in the dev namespace to debug.
With Roles and RoleBindings, you can grant precise permissions within a single namespace. The next lesson covers ClusterRoles and ClusterRoleBindings, which extend this model to resources that span the entire cluster or live outside any namespace.