Files
rootnode-academy/content/root/root-09-kubernetes-security/m02-serviceaccounts-and-rbac/01-lesson-serviceaccounts-rbac.md
T
ced4568andClaude Sonnet 5 960a638ec9 feat(root): add Kubernetes security curriculum
Four new modules, moving ROOT 09 off PLANNED and building the
22-scenario CKS-style bank into a coherent course instead of
disconnected exam tasks — see docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md
for the CORE/GUIDED/INDEPENDENT/ADVANCED/ASSESSMENT-BANK classification
behind every placement.

root-09-m01-security-model-and-securitycontext — READY, full
gold-standard pattern: defense in depth, least privilege, hardening a
Pod with SecurityContext. Lesson, demonstration (a Pod before/after
hardening), knowledge check, guided exercise (a hardening checklist),
real Labs (CORE: non-root/drop-capabilities/read-only-fs/
pod-security-context; GUIDED: seccomp; ADVANCED: AppArmor; INDEPENDENT:
the generic pod-security-context duplicate), and a reflection.

root-09-m02-serviceaccounts-and-rbac — PARTIAL: workload identity and
least-privilege permissions. Lesson + knowledge check + real Labs.

root-09-m03-network-and-supplychain-security — PARTIAL: NetworkPolicy
as a security control (default-deny), image/registry trust, TLS
ingress. Lesson + knowledge check + real Labs.

root-09-m04-cluster-hardening-and-runtime — PARTIAL, deliberately
survey-depth: Pod Security Admission and a lighter treatment of
audit-policy/runtime-sandboxing topics that don't belong in the core
daily-driver path.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 09:07:07 -05:00

4.1 KiB

Identity for Workloads: ServiceAccounts & RBAC

Workloads need an identity too, separate from yours

When YOU run kubectl, you're authenticated as yourself. But a Pod running INSIDE the cluster that needs to call the Kubernetes API (to list other Pods, read a ConfigMap, whatever) needs its own identity — it can't and shouldn't use your personal credentials. That identity is a ServiceAccount.

Every Pod runs as SOME ServiceAccount — if you don't specify one, it uses the default ServiceAccount in its Namespace, which by default has essentially no permissions. A Pod's ServiceAccount token is automatically mounted into the container unless you explicitly disable that (automountServiceAccountToken: false) — worth doing for any Pod that never actually needs to call the Kubernetes API at all, since an unused, mounted credential is still something an attacker could steal and use.

RBAC: what that identity is actually allowed to do

Having an identity (a ServiceAccount) doesn't grant any permissions by itself — RBAC (Role-Based Access Control) is the separate system that defines what an identity can DO.

  • A Role (namespace-scoped) or ClusterRole (cluster-wide) defines a set of permissions: which API resources, which verbs (get/list/watch/create/update/delete).
  • A RoleBinding (or ClusterRoleBinding) connects a Role/ClusterRole to a specific identity (a ServiceAccount, a user, or a group) — the Role alone grants nothing to anyone until it's bound.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: ServiceAccount
  name: my-app
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

This grants the my-app ServiceAccount permission to get/list Pods in the default Namespace — and nothing else. It cannot delete Pods, cannot touch Secrets, cannot do anything not explicitly listed.

Role vs. ClusterRole: scope matters

A Role only grants permissions within the ONE Namespace it's defined in. A ClusterRole can grant permissions across the entire cluster (or, bound via a RoleBinding instead of a ClusterRoleBinding, can also be reused namespace-by-namespace) — reach for a ClusterRole only when the workload genuinely needs cluster-wide visibility, not by default.

Least privilege, applied to permissions specifically

The same principle from the previous module applies directly here: grant the narrowest set of verbs on the narrowest set of resources that the workload actually needs. A Role granting get/list on Pods is very different from one granting * on * — the latter is "root-equivalent" API access, and should be treated with exactly that level of caution.

Instructor Notes

Approximate timing: 15 minutes read; 10 minutes live.

Likely misconceptions to watch for:

  • Students often conflate "ServiceAccount" with "user account for logging in." A ServiceAccount is specifically an identity for WORKLOADS, not a human login mechanism.
  • The Role/RoleBinding two-step (define permissions, then separately grant them to someone) is sometimes seen as redundant. Emphasize the reuse benefit: the same Role can be bound to multiple identities without redefining the permission set each time.

Discussion question: "Why might you explicitly disable automounting a ServiceAccount token for a Pod that never calls the Kubernetes API?" (Answer: an unused but present credential is still something an attacker who compromises the container could steal and use elsewhere — removing it entirely removes that risk for a Pod that never needed it.)

Optional extension: ask what the practical difference is between granting a ClusterRole via a RoleBinding (scoped to one Namespace) versus a ClusterRoleBinding (cluster-wide) — a good check that students understand the Role/ClusterRole and Binding-type distinctions are independent axes, not the same choice.