Kubernetes Core Concepts: Service Account

adil
4 min readOct 10, 2023

Part 2: How to use TokenRequest API and TokenVolume Projection in Kubernetes?

Kubernetes has an API that lets you do CRUD tasks on the cluster.

Photo by Bernard Hermant on Unsplash

What can I do with the API?

You may delete a pod. You may list pods in a namespace. You may list the namespaces in the cluster and so on.

Let’s deploy a pod:

00-pod.yaml

---
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
containers:
- image: nginx
name: web-container

Apply and get into the shell:

➜  ~ kubectl apply -f 00-pod.yaml
pod/web-pod created
➜ ~ kubectl exec -it web-pod -- /bin/bash
root@web-pod:/#

I want to use the Kubernetes API to get a list of all the pods in the default namespace. (Here’s the reference).

You can use the kubernetes.default.svc domain to access the Kubernetes API.

Let’s try:

root@web-pod:/# curl -k https://kubernetes.default.svc/api/v1/namespaces/default/pods
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "pods is forbidden: User \"system:anonymous\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",
"reason": "Forbidden",
"details": {
"kind": "pods"
},
"code": 403
}

To access the API, we must have a token. Kubernetes API assumed the request came from an Anonymous System User because we didn’t send a token.

To make calls to the Kubernetes API, we need to have a Service Account

If you want to access the Kubernetes API via kubectl, you don’t need to create a service account.

Get all pods in the default namespace:

Get the web-pod in the default namespace:

Default Service Account

A service account is generated automatically whenever a Kubernetes cluster is created. It’s called default service account:

➜  ~ kubectl get serviceaccounts
NAME SECRETS AGE
default 0 28m

The default service account is automatically attached to the pod.

A token for the default service account is automatically created. The token is added to the pod automatically as a file:

The file token is there. Let’s use the token to call the Kubernetes API:

curl -k --header "Authorization: Bearer ${token}" https://kubernetes.default.svc/api/v1/namespaces/default/pods

Similar to the above error. However, now, Kubernetes API can recognize our user: default

This is because the default service account does not have the right permissions to list the pods in the default namespace.

Let’s not change anything and instead set up a new service account.

01-service-account.yaml

---
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-account-blog
namespace: default

Apply:

➜  ~ kubectl apply -f 01-service-account.yaml
serviceaccount/sa-account-blog created
➜ ~ kubectl get serviceaccounts
NAME SECRETS AGE
default 0 43m
sa-account-blog 0 10s

In Kubernetes, a Service account is used for authentication, and a Role is used for authorization.

We have to create a role.

02-role.yaml

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: sa-role-blog
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]

Apply:

➜  ~ kubectl apply -f 02-role.yaml
role.rbac.authorization.k8s.io/sa-role-blog created

There are two kinds of roles in Kubernetes: Role and ClusterRole

Role is valid for a particular namespace (in our case, default )
ClusterRole is valid across all namespaces.

The role sa-role-blog needs to be attached to the user sa-account-blog . We need a RoleBinding for this:

03-role-binding.yaml

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-binding-blog
namespace: default
subjects:
- kind: ServiceAccount
name: sa-account-blog
namespace: default
roleRef:
kind: Role
name: sa-role-blog
apiGroup: rbac.authorization.k8s.io

Apply:

➜  ~ kubectl apply -f 03-role-binding.yaml
rolebinding.rbac.authorization.k8s.io/role-binding-blog created
➜ ~ kubectl get rolebindings -o wide
NAME ROLE AGE USERS GROUPS SERVICEACCOUNTS
role-binding-blog Role/sa-role-blog 33s default/sa-account-blog

We can now use the service account sa-account-blog. Let’s deploy another pod with the new service account:

04-pod.yaml

---
apiVersion: v1
kind: Pod
metadata:
name: web-pod-with-sa
spec:
serviceAccountName: sa-account-blog
containers:
- image: nginx
name: web-container

Apply:

➜  ~ kubectl apply -f 04-pod.yaml
pod/web-pod-with-sa created

The service account is attached to the pod:

Let’s get into the shell and test the token:

We can call the Kubernetes API with the token.

--

--