25

Service Accounts

Video: Day 25/40 — Kubernetes Service Account (RBAC Continued) • https://www.youtube.com/watch?v=k2iCq7IlMKM • Duration: ~16 min

Key terms

TermMeaning
ServiceAccount (SA)Identity for pods/workloads
default SAAuto-assigned account per namespace
TokenCredential mounted into a pod
Projected tokenShort-lived, audience-bound token
automountServiceAccountTokenToggle for mounting the token
RBACBinds permissions to a ServiceAccount

Problem & solution

Pods, controllers, and CI bots also need an identity to call the API, but human user accounts aren't Kubernetes objects and don't fit automated workloads. Service accounts give non-human workloads a managed in-cluster identity.

Solution: Give in-cluster apps a ServiceAccount identity with an auto-mounted token, then grant it permissions through RBAC.

The analogy

Most badges at the port go to people, but the automated cranes and loader robots have to pass through gates too, and you obviously cannot hand a robot a human ID. So the port issues machine badges built specifically for equipment, each carrying a chip the gates can read on the robot's behalf. Kubernetes does the same: a ServiceAccount is the machine badge for a pod or workload, the auto-mounted token is its chip, and the api-server reads that token to authenticate the workload, never treating it as a human user.

Where this fits in the cluster

The same cluster entities appear in every day's notes; the <== marks what this day touches.

Two kinds of accounts

Kubernetes splits identities into user accounts for humans and service accounts for workloads — only the latter is a real cluster object.

   USER account      -> humans: admins, developers, operators
                        (certs/OIDC, NOT a Kubernetes object)
   SERVICE account   -> non-humans: pods, controllers, CI bots, apps
                        (a real Kubernetes object you can create)

When code inside a pod needs to call the Kubernetes API, it uses a ServiceAccount, not a human user cert.

Default service account

Every namespace has a default SA, and every pod that doesn't name one gets it mounted automatically.

kubectl get sa                       # SAs in current namespace
kubectl get sa -A                    # every namespace has its own 'default'

Create a service account

A ServiceAccount is a namespaced object you create like any other resource.

kubectl create sa build-bot
kubectl get sa build-bot -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-bot
  namespace: default

Give it permissions (RBAC, same as users)

A SA is just another subject in a RoleBinding/ClusterRoleBinding.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: build-bot-read
  namespace: default
subjects:
  - kind: ServiceAccount
    name: build-bot
    namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Verify with impersonation:

kubectl auth can-i list pods \
  --as=system:serviceaccount:default:build-bot     # -> yes

Use it from a pod

Set serviceAccountName on the pod spec so the container runs as that SA and gets its token mounted.

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  serviceAccountName: build-bot         # <- pod runs as this SA
  containers:
    - name: app
      image: nginx

The token is mounted at:

   /var/run/secrets/kubernetes.io/serviceaccount/
     token       -> JWT bearer token (auto-rotated, projected)
     ca.crt      -> cluster CA to trust the API server
     namespace   -> the pod's namespace

Tokens

You can mint a bearer token for a SA on demand, useful for testing API calls or external clients.

kubectl create token build-bot                 # short-lived token (on demand)
kubectl create token build-bot --duration=1h

Modern Kubernetes uses short-lived, auto-rotated projected tokens instead of the old permanent Secret-based tokens.

End-to-end example: a pod calls the API as its ServiceAccount

The app inside a pod authenticates with its auto-mounted SA token.

End-to-end flow

An in-cluster app authenticates to the API using its mounted ServiceAccount token.

Key takeaways

  • User accounts = humans, ServiceAccounts = workloads/bots.
  • SAs are real namespaced objects; each namespace has a default SA.
  • Grant a SA access with the same RBAC (Role/ClusterRole + binding).
  • Subject form: system:serviceaccount:<namespace>:<name>.
  • Pods authenticate using the projected token mounted into the container.

Checklist

  • [ ] Listed default SAs across namespaces
  • [ ] Created a build-bot SA and bound it to a Role
  • [ ] Verified with --as=system:serviceaccount:default:build-bot
  • [ ] Ran a pod with serviceAccountName and found the mounted token