ServiceAccount provides an identity for processes running in a Pod, and processes within the Pod can use the identity of their associated service account to authenticate to the APIServer in the cluster.
When the Pod is created, there is a spec.serviceAccount
property under the specification that specifies which ServiceAccount the Pod uses, or the default sa if it is not specified, and then by projecting the volume, there is a token
token file under the Pod’s directory /run/secrets/ kubernetes.io/serviceaccount/
, there is a token
token file under the Pod’s directory /run/secrets/
. If we grant any privileges to the sa via RBAC, then the application in the container will have the corresponding privileges when it takes the token.
However, it is important to note that different versions of K8s use the token
file differently, so we will briefly explain each of them here.
<= version 1.20
Use kind to quickly create a cluster with a version less than or equal to v1.20
.
1
2
3
4
|
☸ ➜ kind create cluster --name kind120 --image kindest/node:v1.20.15
☸ ➜ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind120-control-plane Ready control-plane,master 33s v1.20.15
|
We first create a ServiceAccount object with the word sa-demo
.
1
2
3
4
5
6
7
8
9
|
☸ ➜ kubectl create sa sa-demo
☸ ➜ kubectl get sa
NAME SECRETS AGE
default 1 43s
sa-demo 1 6s
☸ ➜ kubectl get secret
NAME TYPE DATA AGE
default-token-dv78w kubernetes.io/service-account-token 3 46s
sa-demo-token-4gvbw kubernetes.io/service-account-token 3 8s
|
We can see that after creating a sa, a secret is automatically generated in the format <saname>-token-xxxx
, for example, if we create a sa with the name sa-demo
, a secret with the name sa-demo-token-4gvbw
is automatically created. This secret contains a token.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
☸ ➜ kubectl describe secrets sa-demo-token-4gvbw
Name: sa-demo-token-4gvbw
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: sa-demo
kubernetes.io/service-account.uid: 1ae8eea9-acc6-4e3d-b378-07feb9146ac4
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1066 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImhQNmFMNjAyaDZ5OElyMmtTNGdPUWxRdHVDU1A4aGFfVkJiNHdHMkZjQlUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tNGd2YnciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjFhZThlZWE5LWFjYzYtNGUzZC1iMzc4LTA3ZmViOTE0NmFjNCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.j0DQmzTeSfagKYGc2dMUuzhYqVQh2puJAoQS0EMKeiAKD6rC4bUHWXWBrCu5Ttvpch6ZTEYwyCdRof1lDGWiLa3pJ1R1RwUNVQTCmVTZPs7tTuoGLRW0KGfEd0jyi4LU6uw4kA_6kwEsz4q2quWcB_fiH_Z3iKVfh1JolYTVAWTBMWnVn6gBvIrlXV5ny2oyvcPQeVfIek8aPQqhbsct_qOxrjqpZY8mpBz0ETR_EELjmcZxVVPLvomOdCqEqbV-FF5KRiFxizB3Xoh6NHz3EcsxpCZNRYdand-UFHaBQC9IPwJKzxhANGmuZuWJUCqCVGGRZTo9c6eoyVz831sZ0A
|
You can see that the automatically generated secret object contains a token, which we can also get with the following command.
1
|
☸ ➜ kubectl get secrets sa-demo-token-4gvbw -o jsonpath='{.data.token}' | base64 -d
|
This token is a JWT
structure, and we can copy this token to the jwt.io
website for decoding.
The right part shows the contents of the token after it is decoded, where the PAYLOAD part is the information of sa-demo
contained in the token, and you can see that there is no expiration time in it, which means the token will never expire.
Now let’s run a Pod using the sa we created above.
1
2
3
4
5
6
7
8
9
10
11
12
|
# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80
|
Just create the Pod directly.
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
|
☸ ➜ kubectl apply -f demo-pod.yaml
☸ ➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo 1/1 Running 0 81s
☸ ➜ kubectl get pod demo -oyaml
apiVersion: v1
kind: Pod
metadata:
name: demo
namespace: default
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
# ......
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: sa-demo-token-4gvbw
readOnly: true
# ......
volumes:
- name: sa-demo-token-4gvbw
secret:
defaultMode: 420
secretName: sa-demo-token-4gvbw
|
After the pod is created, we can see that the secret corresponding to the specified sa is automatically mounted to the /var/run/secrets/kubernetes.io/serviceaccount
directory of the container, so the directory must now contain the corresponding token file, which we can verify by looking at its contents.
1
2
|
☸ ➜ kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImhQNmFMNjAyaDZ5OElyMmtTNGdPUWxRdHVDU1A4aGFfVkJiNHdHMkZjQlUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tNGd2YnciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjFhZThlZWE5LWFjYzYtNGUzZC1iMzc4LTA3ZmViOTE0NmFjNCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.j0DQmzTeSfagKYGc2dMUuzhYqVQh2puJAoQS0EMKeiAKD6rC4bUHWXWBrCu5Ttvpch6ZTEYwyCdRof1lDGWiLa3pJ1R1RwUNVQTCmVTZPs7tTuoGLRW0KGfEd0jyi4LU6uw4kA_6kwEsz4q2quWcB_fiH_Z3iKVfh1JolYTVAWTBMWnVn6gBvIrlXV5ny2oyvcPQeVfIek8aPQqhbsct_qOxrjqpZY8mpBz0ETR_EELjmcZxVVPLvomOdCqEqbV-FF5KRiFxizB3Xoh6NHz3EcsxpCZNRYdand-UFHaBQC9IPwJKzxhANGmuZuWJUCqCVGGRZTo9c6eoyVz831sZ0A
|
You can see that the token mounted in the Pod by the projected volume is exactly the same as the token contained in the secret of sa-demo. This token never expires, so even if you delete the Pod and recreate it, the token in the Pod remains the same, because the token data in the secret object does not change.
If you need to access K8s cluster resource objects in the Pod, you can now bind the corresponding permissions to the sa used, and then use the corresponding token to communicate with the APIServer in the Pod application, and the token will be able to identify the corresponding permissions at this time.
>= version 1.21 && <= version 1.23
Next we test the K8s cluster based on >= 1.21 && <= 1.23
versions.
Here we use kind to quickly create a v1.22.15
version of the cluster.
1
2
3
4
|
☸ ➜ kind create cluster --name kind122 --image kindest/node:v1.22.15
☸ ➜ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind122-control-plane Ready control-plane,master 115s v1.22.15
|
Again, first create a ServiceAccount object named sa-demo
.
1
2
3
4
5
6
7
8
9
|
☸ ➜ kubectl create sa sa-demo
☸ ➜ kubectl get sa
NAME SECRETS AGE
default 1 43s
sa-demo 1 6s
☸ ➜ kubectl get secret
NAME TYPE DATA AGE
default-token-9w9bp kubernetes.io/service-account-token 3 116s
sa-demo-token-g7d2g kubernetes.io/service-account-token 3 8s
|
We can also see that the system automatically creates a corresponding secret object after creating sa, which is no different from the previous version, and we can also get the token value contained in the secret object with the following command.
1
2
|
☸ ➜ kubectl get secrets sa-demo-token-g7d2g -o jsonpath='{.data.token}' | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6Im1ERkhnQ3Y3b1oxUmNHbWVhN210SDEwNXY2dVNkc0QzdXJjTkhsY21FRVEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tZzdkMmciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjI3ZGI0M2FjLTdjYjItNDQ2Yi05N2Q1LWU0MGUzOWRjZTg4YyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.fnSaqrZKolTfz2pi9t32X38Er60WSzUoRHArte6qVmQ1NTaMis4F6rESWekeJvGW26szTJdll6vK8KtL_IRO2m6sp_fEAYfNMQMXL4CuaRByXeAavDqLgMHhodf4k4Yg-Mj4LCQ3aHOxojbAbPT1i_h17Ewivc39fmzp-dAXbHhhWhCW2Vl_CkM-F-UtzLyDwThvJedkeetrfyOOjE7K6HpzWfqIQyMUdCJog3WnFO_4kHXacFCgYg_gNPMYyViQAsTsxB8FplGdEzRuWKnQO9cDE55V4l55IxmE0er-dSSdG8085PzxaM_lMCtRI8YtjRjxcbxS5QkTm5R_ps0IsA
|
Also copy the token value to the jwt.io
website for decoding.
From the decoded value, we can see that the token value also does not contain any expiration time, which means the token will never expire after we create the sa.
Again, let’s use the above sa to create a Pod, as follows.
1
2
3
4
5
6
7
8
9
10
11
12
|
# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80
|
Create the Pod directly.
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
31
32
33
34
35
36
37
38
39
40
41
42
|
☸ ➜ kubectl apply -f demo-pod.yaml
☸ ➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo 1/1 Running 0 81s
☸ ➜ kubectl get pod demo -oyaml
apiVersion: v1
kind: Pod
metadata:
name: demo
namespace: default
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-6wmfb
readOnly: true
# ......
volumes:
- name: kube-api-access-6wmfb
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
|
When you look at the resource object after the Pod is created, you can see that there is a big difference from the previous version, instead of mounting the secret automatically created above to the container’s /var/run/secrets/kubernetes.io/serviceaccount
directory. We can look at the token value in the Pod to compare it with the token value contained in the secret.
1
2
|
☸ ➜ kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6Im1ERkhnQ3Y3b1oxUmNHbWVhN210SDEwNXY2dVNkc0QzdXJjTkhsY21FRVEifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzA1MDI1NDU4LCJpYXQiOjE2NzM0ODk0NTgsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJkZW1vIiwidWlkIjoiNzY1ODRmODAtZjU1My00Mzk2LWIxOTUtMDEwOTBhMzM4MWYyIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYS1kZW1vIiwidWlkIjoiMjdkYjQzYWMtN2NiMi00NDZiLTk3ZDUtZTQwZTM5ZGNlODhjIn0sIndhcm5hZnRlciI6MTY3MzQ5MzA2NX0sIm5iZiI6MTY3MzQ4OTQ1OCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2EtZGVtbyJ9.TAoe1eCHCXUoHh6oM4uySp8kzRaLQ44GZdU02Ir8m_dzYpdFSw4nwsNyqPggrZdDL3BMH4zceudBEdQuyxiSsrpVDeQKww2wTGhXAr2hWujrJq4ycmu6aMywyv2iRX9Vn-Las1giWK_bFuzCxiR10Lcgyd5N7VjB2WcT7K8rN7dAeUWgiH2s9lMOzoaIorUDXzlnSTcmxkhz1h7RXYKVGaqZBbd5wJsRnINZPGxqsS-wi21Aw2FFmIeeK8GGlnAqnS0f3VS1N2jm03gKPii-sMt0GARse4HsmhGAhyJnt9za6ZNpBgcybd7uEBjgIVrRFTkqBJOjPrAnMvRucVtwww
|
You can clearly see that the token value in the Pod is now different from the token value of the automatically created secret. Also decode the token value in jwt.io
.
You can see that the decoded PAYLOAD
data of the token contains many different data, and the exp
field indicates the expiration time of the token, and you can see that the expiration time is 1 year.
Here we can summarize that in v1.21 to v1.23 K8s clusters, when creating a ServiceAccount object, the system will still automatically create a secret object, the token contained in the secret object is still never expired, but the token value of the secret will not be used in the Pod.
As you can see from the Pod manifest above after creation, when a Pod is created, the Kubernetes control plane now automatically adds a projected volume to the Pod that includes the token to access the Kubernetes API, and the manifest fragment defines a projected volume consisting of three data sources, which are as follows.
serviceAccountToken datasource
: contains the token obtained by the kubelet from the kube-apiserver. kubelet uses the TokenRequest API to obtain a time-limited token. This token for the TokenRequest service expires after the Pod has been deleted or the defined lifecycle (default is 1 hour) has expired. The token is bound to a specific Pod and its audience is set to match the audience of the kube-apiserver. This mechanism replaces the previous mechanism of adding volumes based on Secret, which represented the ServiceAccount for the Pod but did not expire.
configMap data source
: ConfigMap contains a set of certificate authority data that Pods can use to ensure they connect to the cluster’s kube-apiserver (and not to a middleware or accidentally misconfigured peer).
downwardAPI Data Source
: Used to find the name of the namespace containing the Pod and make that name information available to the application code running within the Pod.
So instead of using the token in the secret object that is automatically associated with the ServiceAccount, we should specify that the Pod created by the current version of the K8s cluster contains a token that the kubelet will send a request to the TokenRequest API
for a new token to be placed in the Pod’s / run/secrets/kubernetes.io/serviceaccount/token
in the Pod. This token will be re-claimed by the kubelet after 1 hour, so if you check the token again after 1 hour, you will see that the contents of the token have changed. If you delete the pod and recreate it, the token will be re-claimed and the token in the deleted pod will expire immediately.
And we can also manually use the kubectl create token <sa>
command to request a ServiceAccount token, which can specify the expiration date, etc.
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
|
☸ ➜ kubectl create token -h
Request a service account token.
Examples:
# Request a token to authenticate to the kube-apiserver as the service account "myapp" in the current namespace
kubectl create token myapp
# Request a token for a service account in a custom namespace
kubectl create token myapp --namespace myns
# Request a token with a custom expiration
kubectl create token myapp --duration 10m
# Request a token with a custom audience
kubectl create token myapp --audience https://example.com
# Request a token bound to an instance of a Secret object
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret
# Request a token bound to an instance of a Secret object with a specific uid
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret --bound-object-uid
0d4691ed-659b-4935-a832-355f77ee47cc
Options:
# ......
|
>= version 1.24
Now let’s take a look at how the ServiceAccount token works in a K8s cluster from v1.24 onwards. Here we use kind to quickly create a v1.25.3
version of the cluster.
1
2
3
4
|
☸ ➜ kind create cluster --name kind125 --image kindest/node:v1.25.3
☸ ➜ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind125-control-plane Ready control-plane,master 115s v1.25.3
|
Also create a ServiceAccount named sa-demo
.
1
2
3
4
5
6
7
|
☸ ➜ kubectl create sa sa-demo
☸ ➜ kubectl get sa
NAME SECRETS AGE
default 0 39d
sa-demo 0 5s
☸ ➜ kubectl get secrets
No resources found in ns1 namespace
|
We can see that the ServiceAccount is not created with a corresponding Secret object. Also create a Pod as shown below.
1
2
3
4
5
6
7
8
9
10
11
12
|
# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80
|
Check out the details after creating the Pod above.
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
31
32
33
34
35
36
37
38
|
☸ ➜ kubectl apply -f demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
namespace: default
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-pftqd
readOnly: true
# ......
volumes:
- name: kube-api-access-pftqd
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
|
You can see that the creation of a Pod also automatically adds a projected volume to the Pod, which includes a token to access the Kubernetes API, consistent with >=1.21 release && <= 1.23 release
. Again, we can verify this by looking at the token values in the Pod.
1
2
|
☸ ➜ kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6IndnazJLZENQTktiZkxVejhnMnhmTHJYRTlkZ2ZnOHJGQmgwVW4td3BWd0kifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzA0ODg0MDg0LCJpYXQiOjE2NzMzNDgwODQsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJkZW1vIiwidWlkIjoiMTY0ZTIwZTYtYjNjMi00ZmQ5LWI3ZTUtMDZjYTExZWIyOWM4In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYS1kZW1vIiwidWlkIjoiYjJlNWM3ZmYtNjlhNy00NzYyLTkxMDctM2UxNzZhYmQ3NTdiIn0sIndhcm5hZnRlciI6MTY3MzM1MTY5MX0sIm5iZiI6MTY3MzM0ODA4NCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2EtZGVtbyJ9.lhYscyn_d9Y3GZSipSqGj4Jtsu8qsIyz34L18lv37HxjjGU_bQmUFCXYf_CRom8DfadHppmlaskZS18KmyTV1Z09BeujJd8viUnnYCWb9K6VJB5uPBYWLB0FETfgQy7Kqu8Gvk8qBKLjdCkl8U2vr2Oqd2qSEDyvqhNBQXnckQRH6wyypBUc7EXSGAJf6dPVE3c6XqnbXMJ7SRZb5svE-hv0lZKmJrouz9Ia4qxUXUtpzDlMPnHOym2x9d1TSSZ1Lp7BOsqTnxlUQVueh9w869jAajrP1G9e5zhZwZBfzRfARqCVqoLid_hOQP-mo4MLfHbn61SWItlCBd75nl2WLQ
|
We can copy the token value output above to jwt.io
for decoding.
As you can see from the data above, the token here is also valid for 1 year, and the token is updated every 1 hour in the Pod. If the Pod is deleted and rebuilt, then a new token will be claimed, and the token in the deleted Pod will expire immediately.
Note that there is no specific mechanism to invalidate tokens issued via TokenRequest
. If you no longer trust the ServiceAccount token bound to a Pod, you can delete the Pod, and deleting the Pod will cause the token bound to it to expire.
Summary
We can briefly summarize how the ServiceAccount Token works under different versions of K8s clusters.
- Versions before 1.20 (including 1.20), a secret is automatically created when creating a sa, and then this secret is mounted to the pod via a projected volume, and the token contained in this secret is persistent.
- In versions 1.21~1.23, a secret is also created automatically when creating a sa, but the token in the secret is not used in the pod, instead, the kubelet goes to the TokenRequest API to request a token, which is valid for one year by default, but the pod will update the token every hour. The token is valid for one year by default, but the pod will update the token every hour.
- In version 1.24 and above, the secret is no longer created automatically when creating a sa, only the token is requested from the kubelet to the TokenRequest API.
Of course we can still manually create a Secret to hold a ServiceAccount token, for example if you need a token that never expires. Once you manually create a Secret and associate it with a ServiceAccount, the Kubernetes control plane will automatically populate the token with that Secret.
Although there are mechanisms to manually create long-lasting ServiceAccount tokens, it is recommended to use TokenRequest to obtain short-term API access tokens.