How Do You Create an RBAC Role and Bind It to a User?

intermediate|rbacdevopssrecloud architectCKACKS
TL;DR

You create a Role by defining rules that specify API groups, resources, and allowed verbs in a YAML manifest. You then create a RoleBinding that associates the Role with a subject (user, group, or ServiceAccount). Both can be created declaratively with YAML or imperatively with kubectl.

Detailed Answer

Creating RBAC permissions in Kubernetes is a two-step process: define the permissions (Role or ClusterRole) and assign them (RoleBinding or ClusterRoleBinding). Here is a complete walkthrough.

Step 1: Identify Required Permissions

Before writing YAML, determine exactly what API actions are needed. Use kubectl api-resources to find the correct API groups and resource names:

# Find the API group for Deployments
kubectl api-resources | grep deployments
# deployments    deploy   apps/v1    true    Deployment

# Find all resources in the core API group
kubectl api-resources --api-group=""

# Find all resources in the apps group
kubectl api-resources --api-group=apps

Key API groups and their common resources:

| API Group | Resources | |---|---| | "" (core) | pods, services, configmaps, secrets, persistentvolumeclaims | | apps | deployments, statefulsets, daemonsets, replicasets | | batch | jobs, cronjobs | | networking.k8s.io | networkpolicies, ingresses | | rbac.authorization.k8s.io | roles, clusterroles, rolebindings, clusterrolebindings |

Step 2: Create the Role (Declarative)

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: development
  name: app-developer
rules:
  # Core resources
  - apiGroups: [""]
    resources: ["pods", "pods/log", "pods/portforward"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["services", "configmaps"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  # Deployments
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  # Events (read-only for debugging)
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["get", "list", "watch"]

Apply it:

kubectl apply -f role.yaml

Step 2 (Alternative): Create the Role Imperatively

kubectl create role app-developer \
  --verb=get,list,watch,create,delete \
  --resource=pods,pods/log,pods/portforward \
  -n development

# Note: imperative creation supports only one rule at a time.
# For multiple rules, use YAML or run the command multiple times
# and edit the resulting role.

Step 3: Create the RoleBinding (Declarative)

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: development
  name: alice-app-developer
subjects:
  - kind: User
    name: alice
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: app-developer
  apiGroup: rbac.authorization.k8s.io

Apply it:

kubectl apply -f rolebinding.yaml

Step 3 (Alternative): Create the RoleBinding Imperatively

kubectl create rolebinding alice-app-developer \
  --role=app-developer \
  --user=alice \
  -n development

For a ServiceAccount:

kubectl create rolebinding deployer-binding \
  --role=app-developer \
  --serviceaccount=development:deployer-sa \
  -n development

For a group:

kubectl create rolebinding team-binding \
  --clusterrole=edit \
  --group=dev-team \
  -n development

Step 4: Verify the Permissions

# Check a specific permission
kubectl auth can-i create pods --as=alice -n development
# yes

kubectl auth can-i delete deployments --as=alice -n development
# no (we only granted get/list/watch/create/update/patch)

# List all permissions
kubectl auth can-i --list --as=alice -n development

Complete End-to-End Example

Here is a real-world scenario: creating a CI/CD ServiceAccount that can deploy applications to a staging namespace.

# 1. ServiceAccount for the CI pipeline
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-deployer
  namespace: staging
---
# 2. Role with deployment permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: staging
  name: ci-deploy-role
rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "create", "update", "patch"]
  - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "list", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
---
# 3. Bind the Role to the ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: staging
  name: ci-deployer-binding
subjects:
  - kind: ServiceAccount
    name: ci-deployer
    namespace: staging
roleRef:
  kind: Role
  name: ci-deploy-role
  apiGroup: rbac.authorization.k8s.io
# Apply all resources
kubectl apply -f ci-rbac.yaml

# Verify
kubectl auth can-i create deployments \
  --as=system:serviceaccount:staging:ci-deployer \
  -n staging
# yes

kubectl auth can-i delete namespaces \
  --as=system:serviceaccount:staging:ci-deployer
# no (good — least privilege)

Understanding apiGroups

The apiGroups field is often confusing. Here is the rule:

  • Core resources (pods, services, configmaps, secrets, etc.) use an empty string: [""]
  • Everything else uses the group from the API path. For apps/v1, the group is "apps". For batch/v1, the group is "batch".
# Quick reference: find the group for any resource
kubectl api-resources -o wide | grep -i deployment
# NAME          SHORTNAMES   APIVERSION   NAMESPACED   KIND          VERBS
# deployments   deploy       apps/v1      true         Deployment    [create delete ...]

The APIVERSION column shows apps/v1, so the apiGroup is apps.

Common Mistakes

  1. Forgetting the empty string for core resources — writing apiGroups: ["core"] or apiGroups: ["v1"] instead of apiGroups: [""].
  2. Missing subresourcespods/log and pods/exec are separate resources that need their own rules.
  3. Not testing after creation — always run kubectl auth can-i to verify.

Why Interviewers Ask This

This is a hands-on question that tests whether you can actually set up RBAC in a cluster. Interviewers expect you to walk through the process confidently, including the YAML structure and imperative commands.

Common Follow-Up Questions

How do you know which apiGroup a resource belongs to?
Use kubectl api-resources to list all resources and their API groups. The core group (pods, services, configmaps) is represented as an empty string ''.
Can you create a Role with kubectl without writing YAML?
Yes. kubectl create role my-role --verb=get,list --resource=pods -n dev creates a Role imperatively. Similarly, kubectl create rolebinding binds it.
How do you grant access to custom resources?
Specify the CRD's API group and resource name in the Role rules. For example, apiGroups: ['stable.example.com'], resources: ['crontabs'], verbs: ['get', 'list'].

Key Takeaways

  • Role rules require three fields: apiGroups, resources, and verbs.
  • The core API group (pods, services, etc.) is represented as an empty string.
  • kubectl create role and kubectl create rolebinding provide quick imperative creation.
  • Always verify the result with kubectl auth can-i.

Related Questions