Kubernetes’ Service Account is a type of account managed by Kubernetes, which is particularly convenient to manage, but it is not easy to understand the application context when you are new to this type of account. This article is a complete overview that I have read after reading many documents, and I believe it can provide a certain level of understanding of service accounts.
Account Types
There are two types of Kubernetes accounts. 1.
-
User accounts (Normal Users)
Anyone who wants to connect to and access the Kubernetes cluster needs to create a “User Account” and provide credential information to the client (e.g.,
kubectl
) for authentication through the Kubernetes API server.I think the name should be better understood as
User Accounts
, but the official Kubernetes website calls it Normal Users. -
Service Accounts
Any container running inside a Pod that wants to access the Kubernetes API server (
kube-apiserver
) needs to have a “Service Account” bound to the Pod first in order to pass the authentication of the Kubernetes API server.
Experience the namespace default Service Account
When you create namespace
, a service account called default
is created for you by default.
-
Create
dev
namespace -
Get the
serviceaccounts
under thedev
namespace1
kubectl get serviceaccounts --namespace=dev
-
Get the contents of the
default
service account under thedev
namespace1
kubectl get serviceaccounts default -n=dev -o yaml
1 2 3 4 5 6 7 8 9 10 11
apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: "2022-08-23T14:55:51Z" name: default namespace: dev resourceVersion: "1434536" selfLink: /api/v1/namespaces/dev/serviceaccounts/default uid: 3b750bc5-fd6c-43b0-9c64-4a4700f522ae secrets: - name: default-token-xpqc7
It will automatically bind a
secrets
to keep the Token information of the service account. -
Get the
secrets
content bound by thedefault
service account under thedev
namespace1
kubectl get secrets default-token-xpqc7 -n=dev -o yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
apiVersion: v1 data: ca.crt: DATA+OMITTED namespace: ZGV2 token: TOKEN+OMITTED kind: Secret metadata: annotations: kubernetes.io/service-account.name: default kubernetes.io/service-account.uid: 3b750bc5-fd6c-43b0-9c64-4a4700f522ae creationTimestamp: "2022-08-23T14:55:51Z" name: default-token-xpqc7 namespace: dev resourceVersion: "1434535" selfLink: /api/v1/namespaces/dev/secrets/default-token-xpqc7 uid: 868a7d4f-74b8-4be4-8c0e-b9d5a3e678b2 type: kubernetes.io/service-account-token
Experience how Pods use Service Account
-
We start by creating a Pod in the
dev
namespace without specifying adefault
service account.1
kubectl run microbot --image=dontrebootme/microbot:v1 -n dev
-
In fact, all Pods are added to
default
service accounts by defaultIf you don’t specify
spec.serviceAccountName
when you create a Pod innamespace
, Kubernetes will also add thedefault
service account in the same namespace by default. Therefore, every Pod must have a service account bound to it.1
kubectl get microbot -n dev -o yaml
At this point you will see the following YAML file, with a
serviceAccountName: default
that has been automatically set in.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
apiVersion: v1 kind: Pod metadata: labels: run: microbot name: microbot namespace: dev spec: containers: - image: dontrebootme/microbot:v1 imagePullPolicy: IfNotPresent name: microbot resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-dfs8b readOnly: true dnsPolicy: ClusterFirst enableServiceLinks: true nodeName: microk8s-vm preemptionPolicy: PreemptLowerPriority priority: 0 restartPolicy: Always schedulerName: default-scheduler securityContext: {} serviceAccount: default serviceAccountName: default ...
And by default, a
/var/run/secrets/kubernetes.io/serviceaccount
directory is mounted in the Pod! -
View the
/var/run/secrets/kubernetes.io/serviceaccount
directory inside the Pod1
kubectl exec microbot -it -n dev -- sh
1
ls -laF /var/run/secrets/kubernetes.io/serviceaccount
It looks like it automatically mounts all the
secrets
contained inserviceaccounts
to this directory! -
Send HTTP requests to
kube-apiserver
in the Pod’s containerIf TOKEN is not brought in, it will be accessed as
system:anonymous
for anonymous users. -
Bring in the TOKEN of the
default
service account to access thekube-apiserver
At this point you will see that the API request has been validated.
This is the standard way to use Service Account in Pod! 👍
Adding Role Permissions to a Service Account
In fact, although this “authenticated” TOKEN can call part of the kube-apiserver
API, the default service account does not actually have access to any resources in the K8s cluster. We have to create a Role
and assign privileges to it through the RBAC mechanism, and then bind the account through RoleBinding
to give it sufficient privileges to access resources.
-
Try to get the
PodList
list first1
curl --cacert $CACERT -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc.cluster.local:443/api/v1/namespaces/dev/pods/
You will get the following error message.
1 2 3 4 5 6 7 8 9 10 11 12
{ "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "pods is forbidden: User \"system:serviceaccount:dev:default\" cannot list resource \"pods\" in API group \"\" in the namespace \"dev\"", "reason": "Forbidden", "details": { "kind": "pods" }, "code": 403 }
At this point we will know that our User is
system:serviceaccount:dev:default
and our API group is""
,namespace
isdev
and resource type (kind
) ispods
.In addition, you can also use the
kubectl auth can-i
command to quickly find out if a specific user has privileges to specific resources, which is quite useful!1
kubectl auth can-i get pods -n=dev --as=system:serviceaccount:dev:default
He will simply reply to you
yes
orno
. In its current state, it should replyno
! 👍 -
Create
Role
objects1
kubectl create role read-pods -n=dev --verb='get,list' --resource=pods
or generate the corresponding YAML file content via the
-dry-run=client -o yaml
parameter.1
kubectl create role read-pods -n=dev --verb='get,list' --resource=pods --dry-run=client -o yaml
-
Create
RoleBinding
objects1
kubectl create rolebinding read-pods -n dev --user=system:serviceaccount:dev:default --role=read-pods
or generate the corresponding YAML file content via the
-dry-run=client -o yaml
parameter.1
kubectl create rolebinding read-pods -n dev --user=system:serviceaccount:dev:default --role=read-pods --dry-run=client -o yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: creationTimestamp: null name: read-pods namespace: dev roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: read-pods subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: system:serviceaccount:dev:default
The above syntax can also be written in the
subjects:
field, it is actually the same, ServiceAccount (kind: ServiceAccount
) (name: default
) is GeneralAccount (kind: User
) (name: system: serviceaccount:dev:default
), but the names are expressed in different ways.First check if the
system:serviceaccount:dev:default
service account has been grantedget
permissions forpods
. -
Get the
PodList
list again1
curl --cacert $CACERT -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc.cluster.local:443/api/v1/namespaces/dev/pods/
At this point, you should be able to get the complete
PodList
list with detailed information.
Experience creating a new service account
With the above understanding of default
service account, I believe it is not difficult to understand the usage of custom service account. Here are the steps to experience it.
-
Create a custom service account
monitor
in thedev
namespace1
kubectl create serviceaccount monitor -n dev
-
Set
Role
andRoleBinding
-
Create the Pod using YAML and specify the
serviceAccountName: monitor
service accountA quick way to apply it with PowerShell.
-
Calling the API server from a container in Pods
1
kubectl exec microbot -it -n dev -- sh
Summary
Now you should know how to access the resources in the Kubernetes cluster from your application in the Pod.
I used to think that to read ConfigMaps
or Secrets
from a Pod you had to mount them as volumeMounts
or env
, but in fact there is no way to apply RBAC licenses via volumeMounts
or env
. When you want to restrict application access to cluster resources, you have to change the YAML and apply updates, which is not reliable.
Furthermore, it is not very convenient to have to redeploy or restart Deployment every time you change configuration settings.
Now you can access Kubernetes resources directly from your application through a service account, and you can limit the scope of access through the RBAC mechanism, which is both flexible and secure, which I think is great! 👍
Reference
https://blog.miniasp.com/post/2022/08/24/Understanding-Service-Account-in-Kubernetes-through-MicroK8s