Service Accounts & Roles
Learn to configure Kubernetes service accounts and roles for pod-level identity and access control.
Focus: configure service accounts and roles
When a Pod runs in Kubernetes, it doesn't have a built-in identity to prove who it is when talking to the API server or external services. Without a proper identity, every Pod would need to use your personal user credentials or skip authentication entirely—both are security nightmares. That's where ServiceAccounts come in: they give each Pod a distinct, verifiable identity, and with Roles and RoleBindings, you can control exactly what that identity can do. This lesson teaches you to configure service accounts and roles so your Pods can authenticate and authorize themselves securely.
The problem this lesson solves
Imagine you deploy a microservice that needs to list all Pods in a namespace for internal monitoring. If you don't configure a service account and role, the Pod has one of two problems:
- No identity: The Pod uses the default service account, which usually has no RBAC permissions. The API call fails with a 403 Forbidden.
- Too much identity: The Pod inherits over-permissive credentials (like a cluster admin token) and accidentally deletes resources it shouldn't.
This is the classic DevOps tension: security vs. convenience. The solution is to configure a dedicated ServiceAccount, attach a Role with minimal permissions, and bind them with a RoleBinding (or ClusterRole/ClusterRoleBinding for cross-namespace access). Without this, your cluster is either broken or insecure.
Core concept / mental model
Think of a ServiceAccount as a badge that the Pod wears. The badge proves "this is the Pod metrics-collector, not an impostor." A Role is a list of permission rules — like a key card that says "you can read Pods and Services in the default namespace." A RoleBinding is the handshake that attaches the badge to the key card.
Important nuance: ServiceAccounts are namespace-scoped objects. If you create a ServiceAccount in
namespace-a, a Pod innamespace-bcannot use it unless you use a ClusterRole and ClusterRoleBinding (which are cluster-scoped).
Key terms
- ServiceAccount: An object that provides an identity for processes running in a Pod.
- Role: A set of rules that define allowed verbs (
get,list,create,delete, etc.) on specific resources (pods,services,deployments). - RoleBinding: Links a Role to a user, group, or ServiceAccount within a namespace.
- ClusterRole / ClusterRoleBinding: Same as Role/RoleBinding but cluster-scoped (no namespace restriction).
When a Pod uses a service account, the API server automatically mounts a token (mounted at /var/run/secrets/kubernetes.io/serviceaccount/token) that the Pod can present for authentication and authorization.
How it works step by step
- Create a ServiceAccount with
kubectl create serviceaccount <name>or a YAML manifest. - Define a Role with rules specifying
apiGroups,resources, andverbs. For example, a Role that allows getting and listing Pods looks like this:
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- Bind the Role to the ServiceAccount using a RoleBinding:
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: default
name: pod-reader-binding
subjects:
- kind: ServiceAccount
name: my-service-account
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
- Assign the ServiceAccount to a Pod by setting
spec.serviceAccountName:my-service-account. - The Pod's code uses the mounted token to authenticate against the Kubernetes API. The API server checks the RoleBinding and allows or denies the request.
Hands-on walkthrough
Let's make it real. You'll create a ServiceAccount, a Role that allows listing Pods, bind them, and deploy a Pod that uses the token to prove it works.
Step 1: Create the ServiceAccount
kubectl create serviceaccount my-sa
# OR with YAML:
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-sa
namespace: default
EOF
Verify it exists:
kubectl get sa
# Output:
# NAME SECRETS AGE
# default 1 30d
# my-sa 1 5s
Step 2: Create the Role
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
EOF
Step 3: Bind the Role to the ServiceAccount
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: default
name: pod-reader-binding
subjects:
- kind: ServiceAccount
name: my-sa
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
EOF
Step 4: Deploy a Pod that uses the ServiceAccount
Let's deploy a Pod that runs kubectl and uses curl to hit the Kubernetes API.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: sa-test-pod
spec:
serviceAccountName: my-sa
containers:
- name: test
image: bitnami/kubectl:latest
command: ["sleep", "3600"]
EOF
Wait for the Pod to be ready (kubectl get pod sa-test-pod) and then exec into it:
kubectl exec -it sa-test-pod -- /bin/sh
Inside the container, read the token and namespace:
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
# List Pods using the token
curl -s --header "Authorization: Bearer $TOKEN" --insecure \
https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/pods
You should see a JSON response with your Pods. If the token had no permissions, you'd get a 403 Forbidden.
Step 5: Clean up
kubectl delete pod sa-test-pod
kubectl delete rolebinding pod-reader-binding
kubectl delete role pod-reader
kubectl delete serviceaccount my-sa
Compare options / when to choose what
| Feature | Role + RoleBinding | ClusterRole + ClusterRoleBinding |
|---|---|---|
| Scope | Namespace-specific | Cluster-wide (all namespaces) |
| Resources | Namespaced resources (Pods, Services, ConfigMaps) | Namespaced + cluster-scoped (Nodes, PVs, StorageClasses) |
| Non-resource URLs | Not supported | Supports /healthz, /api, etc. |
| Use case | Scoped microservice permissions, per-team namespaces | Cluster admin tasks, monitoring across namespaces |
When to use what:
- Use Role + RoleBinding when your Pod only needs access to resources in its own namespace.
- Use ClusterRole + ClusterRoleBinding when a component needs to see everything (e.g., Prometheus scraping all namespaces, or an admission webhook).
- You can also bind a ClusterRole to a RoleBinding (namespace-scoped) — that's common when you want to reuse a ClusterRole definition but restrict it to a single namespace.
Troubleshooting & edge cases
Common errors and fixes
- 403 Forbidden: The ServiceAccount lacks permissions. Verify the Role rules and the RoleBinding subject. Use
kubectl auth can-i --as=system:serviceaccount:default:my-sa list pods -n defaultto test. - Token not mounted: You forgot
spec.serviceAccountNamein the Pod. The default ServiceAccount is always mounted, but if your Pod can't authenticate, check the mount path manually or inspect the Pod's container spec. - Cross-namespace access denied: You tried to use a RoleBinding to give access to a namespace other than where the binding lives. Use a ClusterRoleBinding instead.
- Token expiration: In Kubernetes 1.24+, the service account token is a bound token with expiration (typically 1 year). If your token stops working after a long time, you need to refresh the Pod or use a custom token controller.
Edge case: Default ServiceAccount automation
If you never explicitly set a service account, Kubernetes uses the default ServiceAccount in the Pod's namespace. By default, the default ServiceAccount has no RBAC permissions. This is secure but can be confusing: a Pod that tries to list objects silently fails unless you bind a Role to the default ServiceAccount.
What you learned & what's next
You now understand how to configure service accounts and roles to give Pods a secure, scoped identity. You can:
- Explain why service accounts exist (separation of identities for workloads).
- Create a ServiceAccount, Role, and RoleBinding from YAML.
- Test permissions with
kubectl auth can-iand by exec-ing into a container. - Decide between namespaced (Role/RoleBinding) and cluster-scoped (ClusterRole/ClusterRoleBinding) access.
Next up, you'll explore NetworkPolicies — how to control traffic between Pods, not just API access. You'll take that fine-grained identity you just configured and apply it to network segmentation.
Practice recap
Practice: Create a ServiceAccount named deployer, then write a Role that allows * (all verbs) on deployments and pods in the default namespace, and bind them. Deploy a Pod that runs kubectl run nginx-test --image=nginx to verify the permission works. If you get a 403, check the Role and RoleBinding namespaces.
Common mistakes
- Forgetting to set
spec.serviceAccountNameon the Pod — the default service account has no permissions, so API calls silently fail with 403. - Binding a Role to a ServiceAccount but in the wrong namespace — RoleBindings only apply to the namespace they're created in; use ClusterRoleBinding for cross-namespace access.
- Using
kubectl create rolewithout--verbor--resourceflags — you might create a Role with no rules, which effectively denies everything. - Assuming the mounted token never expires — bound tokens (Kubernetes 1.24+) have expiration; plan for token rotation or use projected volumes.
Variations
- Instead of in-line YAML, use
kubectl create role pod-reader --verb=get,list --resource=pods --dry-run=client -o yamlto generate the Role manifest. - For cluster-level monitoring tools like Prometheus, use a ClusterRole with read access across all namespaces and a ClusterRoleBinding.
- Use external secrets managers (e.g., Vault) to inject credentials instead of Kubernetes tokens, when interacting with external services.
Real-world use cases
- A CI/CD pipeline runs in a Pod and needs to create deployments in a dedicated namespace; a ServiceAccount with a Role allows it safely.
- A monitoring agent (like Prometheus) uses a ClusterRole to scrape metrics from Pods and Nodes across the entire cluster.
- A sidecar container in a Pod needs to read ConfigMaps from its own namespace to dynamically reconfigure the main app.
Key takeaways
- ServiceAccounts give Pods a unique identity separate from human users.
- Roles define allowed actions on resources; RoleBindings attach them to ServiceAccounts.
- Use ClusterRole + ClusterRoleBinding for cluster-wide access, not for namespace-scoped controls.
- Always test permissions with
kubectl auth can-ibefore deploying production workloads. - Bound tokens automatically expire in modern Kubernetes — plan for rotation.
- The default service account has no permissions; always create dedicated accounts for each workload.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.