RBAC: roles, bindings and service accounts

Roles, ClusterRoles and their bindings, the three kinds of subject, service account tokens, and how to prove access with kubectl auth can-i.

Architecture · 25%40 min3 tasks

After this lesson

  • 01You can create a ServiceAccount, Role and RoleBinding imperatively in under two minutes and choose correctly between Role and ClusterRole, RoleBinding and ClusterRoleBinding.
  • 02You can write rules with the right apiGroups, plural resources, subresources and resourceNames.
  • 03You can prove what a user, group or service account may and may not do with kubectl auth can-i --as and --as-group.
  • 04You can issue a time-limited service account token and remove access that was granted too broadly.

Manage role based access control (RBAC) is the first competency in the architecture domain and one of the most dependable sources of points: a service account, a role and a binding are three short commands. The grader does not read your YAML. It asks the API server whether a subject may perform a verb on a resource, exactly as kubectl auth can-i does, so a rule with the wrong API group fails even when it looks right. RBAC has no deny rules either; a subject's access is the union of every binding that names it, which means taking access away always means removing or narrowing a binding.

Four objects, and where their rules apply

BindingPoints atRules apply to
RoleBindinga Role in the same namespaceThat namespace only
RoleBindinga ClusterRoleThat namespace only. The ClusterRole acts as a reusable template; any cluster-scoped resources in it grant nothing
ClusterRoleBindinga ClusterRoleEvery namespace, plus cluster-scoped resources (nodes, PersistentVolumes, namespaces, CRDs)
ClusterRoleBindinga RoleNot possible: roleRef of a ClusterRoleBinding must be a ClusterRole
A Role and a RoleBinding with all three subject kindsyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: config-editor
  namespace: arx-rbac-demo
rules:
  - apiGroups: [""]                   # "" is the core group: pods, services, configmaps, secrets
    resources: ["configmaps"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["app-settings"]   # this one object only
    verbs: ["update", "patch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "deployments/scale"]
    verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: config-editor
  namespace: arx-rbac-demo
roleRef:                              # immutable: to point elsewhere, delete and recreate the binding
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: config-editor
subjects:
  - kind: ServiceAccount              # no apiGroup; namespace is required
    name: settings-sync
    namespace: arx-rbac-demo
  - kind: User                        # the name the authenticator reports (certificate CN, OIDC claim)
    apiGroup: rbac.authorization.k8s.io
    name: maya
  - kind: Group                       # certificate O field, OIDC groups claim, or a system: group
    apiGroup: rbac.authorization.k8s.io
    name: platform-oncall
  • apiGroups is the part of apiVersion before the slash. Core objects use ""; Deployments, StatefulSets, DaemonSets and ReplicaSets are apps; Jobs and CronJobs are batch; Ingresses and NetworkPolicies are networking.k8s.io. kubectl api-resources -o wide prints the group and supported verbs of every resource.
  • resources are lowercase plurals. Subresources are separate entries: pods/log, pods/exec, deployments/scale, nodes/status. Access to pods grants nothing on pods/exec.
  • verbs: get, list, watch, create, update, patch, delete, deletecollection, plus special verbs such as impersonate, bind and escalate. "*" matches every verb, including ones added later.
  • resourceNames narrows a rule to named objects. It cannot restrict create or deletecollection (the name is not known when the request is authorized), and a list or watch only matches if it selects that exact name, so pair it with get, update, patch and delete.
  • nonResourceURLs such as /healthz or /metrics only work in a ClusterRole bound with a ClusterRoleBinding.

Create it imperatively, prove it with auth can-i

The commands you will type on the exambash
kubectl create namespace arx-rbac-demo
kubectl -n arx-rbac-demo create serviceaccount settings-sync

# Role: comma-separated verbs, any number of resources; non-core resources as <resource>.<group>
kubectl -n arx-rbac-demo create role app-reader \
  --verb=get,list,watch --resource=pods,services,deployments.apps
kubectl -n arx-rbac-demo create role settings-writer \
  --verb=update,patch --resource=configmaps --resource-name=app-settings

# RoleBinding: --role or --clusterrole, then any mix of subjects (SA is <namespace>:<name>)
kubectl -n arx-rbac-demo create rolebinding app-reader --role=app-reader \
  --serviceaccount=arx-rbac-demo:settings-sync --user=maya --group=platform-oncall
kubectl -n arx-rbac-demo create rolebinding settings-writer --role=settings-writer --user=maya

# Cluster-wide rules and binding
kubectl create clusterrole pv-viewer --verb=get,list,watch \
  --resource=persistentvolumes,storageclasses.storage.k8s.io
kubectl create clusterrolebinding pv-viewer --clusterrole=pv-viewer --group=platform-oncall

# Ask the API server as the subject. "yes" exits 0, "no" exits 1.
kubectl auth can-i list deployments.apps -n arx-rbac-demo \
  --as=system:serviceaccount:arx-rbac-demo:settings-sync
kubectl auth can-i update configmaps/app-settings -n arx-rbac-demo --as=maya   # TYPE/NAME form
kubectl auth can-i get pods --subresource=log -n arx-rbac-demo --as=maya       # subresources need the flag
kubectl auth can-i list persistentvolumes --as=anyone --as-group=platform-oncall  # --as-group needs --as
kubectl auth can-i --list -n arx-rbac-demo --as=system:serviceaccount:arx-rbac-demo:settings-sync
kubectl auth whoami                                                            # who you are right now

# Generate YAML instead when you need to add something the flags cannot express
kubectl -n arx-rbac-demo create role app-reader --verb=get --resource=pods --dry-run=client -o yaml

Service accounts, tokens and the built-in ClusterRoles

A ServiceAccount is a namespaced identity for software. Its username is system:serviceaccount:<namespace>:<name>, and it is a member of system:serviceaccounts and system:serviceaccounts:<namespace>, the group to bind when a rule should cover every account in a namespace. A Pod runs as the account in spec.serviceAccountName (default when unset), and the kubelet mounts a short-lived, automatically rotated token at /var/run/secrets/kubernetes.io/serviceaccount/token. Token Secrets are no longer created for you: kubectl create token asks the TokenRequest API for a token on demand. Set automountServiceAccountToken: false on the Pod or the ServiceAccount when the workload never calls the API.

Tokens, a Pod that uses an account, and an aggregated ClusterRolebash
kubectl -n arx-rbac-demo create serviceaccount report-bot
kubectl -n arx-rbac-demo create token report-bot --duration=2h > /tmp/cka/report-bot.token
# Printed once and never stored. A long-lived token (avoid unless asked) is a Secret the control plane fills in:
kubectl -n arx-rbac-demo apply -f - <<'YAML'
apiVersion: v1
kind: Secret
metadata:
  name: report-bot-token
  annotations:
    kubernetes.io/service-account.name: report-bot
type: kubernetes.io/service-account-token
YAML

# Run as the account: kubectl run has no SA flag, so override the spec (or set it in YAML)
kubectl -n arx-rbac-demo run reporter --image=busybox:1.36 --restart=Never \
  --overrides='{"spec":{"serviceAccountName":"report-bot"}}' -- sleep 3600
kubectl -n arx-rbac-demo set serviceaccount deployment/web report-bot   # an existing Deployment

# Aggregation: this ClusterRole's rules are merged into the built-in "view" role automatically
kubectl apply -f - <<'YAML'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: backup-viewer
  labels:
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
  - apiGroups: ["ops.example.com"]
    resources: ["backuppolicies"]
    verbs: ["get", "list", "watch"]
YAML
Built-in ClusterRoleGrantsDoes not grant
cluster-adminEvery verb on every resource. Cluster-wide through a ClusterRoleBinding, one namespace through a RoleBinding. kubeadm binds it to the group kubeadm:cluster-admins, which is why kubectl auth whoami shows that group for the admin kubeconfigNothing is withheld
adminRead and write on most namespaced objects, including Roles and RoleBindings in the namespaceWrites to the Namespace itself or its ResourceQuotas
editRead and write on most namespaced objects, including Secrets and PodsViewing or changing Roles and RoleBindings
viewRead-only on most namespaced objectsSecrets (reading them would expose credentials), Roles and RoleBindings

In the docs · the tab you get on exam day

Taskar-rbac-1
Architecture6%~7 min

context · kubectl config use-context kind-cka

A service account that can deploy, and a token for it

In namespace arx-rbac, create a ServiceAccount named deployer and a Role named deployer that allows get, list, watch, create, update and patch on Deployments, and get, list, watch on Pods, and nothing else. Bind the Role to the ServiceAccount with a RoleBinding named deployer-binding. Finally, create a token for deployer that is valid for 2 hours and write only the token to /tmp/cka/rbac/deployer.token.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-1/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-1/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-1/cleanup | bash
Taskar-rbac-2
Architecture4%~5 min

context · kubectl config use-context kind-cka

Read-only node access for a group

Members of the group arx-ops need to inspect nodes and PersistentVolumes across the cluster without changing anything. Create a ClusterRole named arx-node-reader that allows get, list and watch on nodes and persistentvolumes, and grant it to the group arx-ops with a ClusterRoleBinding named arx-node-reader-ops. The group must not gain any other access.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-2/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-2/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-2/cleanup | bash
Taskar-rbac-3
Architecture6%~7 min

context · kubectl config use-context kind-cka

Take back cluster-admin from a service account

The ServiceAccount log-shipper in namespace arx-rbac-fix was given cluster-admin to get a log collector working. It only needs to read Pods and their logs in its own namespace. Remove the excess access, then grant exactly get, list, watch on Pods and get on Pod logs in arx-rbac-fix, using a Role named log-reader and a RoleBinding named log-shipper-read. Do not delete the ServiceAccount.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-3/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-3/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-rbac-3/cleanup | bash

Finished reading?

0 of 3 tasks passed. Readiness counts tasks, so run them before you move on.