当创建pod时需要在pod里的.spec.serviceAccount指定pod以哪个service account运行,如果没有指定的话则默认使用default这个sa。
然后通过投射卷,在pod的目录/run/secrets/kubernetes.io/serviceaccount/会有一个文件token,里面包括了容器所能用到令牌。我们通过RBAC对sa授了什么权限,那么容器里的app拿着这个token,就具备了什么权限。
那么pod里的这个token是从哪里来的呢?我们分几个版本的kubernetes分别讨论。
1.20版本之前(含1.20)
本实验环境是1.20.2版本
首先创建一个名字为sa1的sa。
[root@vms81 ~]# kubectl create sa sa1
serviceaccount/sa1 created
[root@vms81 ~]# kubectl get sa
NAME SECRETS AGE
default 1 9h
sa1 1 4s
[root@vms81 ~]# kubectl get secrets
NAME TYPE DATA AGE
default-token-slrd4 kubernetes.io/service-account-token 3 9h
sa1-token-wnd8m kubernetes.io/service-account-token 3 7s
[root@vms81 ~]#
此时会为此sa生成一个token,格式为 saname-token-xxxx,比如我们创建了一个名字为sa1的sa之后,系统自动创建了一个名字为sa1-token-wnd8m的secret。这个secret里包含了一个token。
[root@vms81 ~]# kubectl describe secrets sa1-token-wnd8m
Name: sa1-token-wnd8m
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: sa1
kubernetes.io/service-account.uid: 5adb2436-725f-4f56-996a-a2e3369b88a6
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1066 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImVNbXg4eVc3NFdwSnFCOU53aGo4SjNJYnlHaUwyMklwNXF0RDB5c3RlaVUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhMS10b2tlbi13bmQ4bSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJzYTEiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI1YWRiMjQzNi03MjVmLTRmNTYtOTk2YS1hMmUzMzY5Yjg4YTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpzYTEifQ.HmeNAIeHIdKP7NRlh0fpywuSJJzvBWZa5i11z6E3EMa9CRIEaNFkA-Jg805GyZEGk-22PybvkHRiyIqlXF7V_Xy5bkBqqZrYMZVHoUZboZLJG_VsYVcu9cdArn7ZNidFAtqArpIr33soVmAjZ8GFMINEvzDMBEj-rn07eIz9HCk0gQ_EKMWdrjh8Eg9BdtodP8vRnx4VizPfkjjLOmwmdrr4dYiGvcWrtsjLFi5jkUQrpT6PezFOlU4WhslCFOLSojahl7uExz-LWbzbJaWhHvw7Y8CVw9HlG_BedMGiZq7Sll7w7PkMwOSvgXjl2cs0veiMbR1HbwjqvKao1dy-yQ
[root@vms81 ~]#
从这里我们能获取到对应的token,这个token也可以通过
kubectl get secrets sa1-token-wnd8m -o jsonpath='{.data.token}' | base64 -d
来获取,记住这种获取方式需要使用base64 -d进行解码。
这个token是JWT结构的,我们把这个token复制到jwt.io网站进行解码。
右侧部分显示了token被解码之后的内容,其中data部分是token里包含的sa1的信息。这里并没有任何的过期时间,也说明了我们创建sa1之后,所对应的token是永不过期的。
我们创建pod,写pod1的yaml文件内容如下。
[root@vms81 ~]# cat pod1.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
serviceAccount: sa1
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@vms81 ~]#
创建pod1
[root@vms81 ~]# kubectl apply -f pod1.yaml
pod/pod1 created
[root@vms81 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod1 1/1 Running 0 3s
[root@vms81 ~]#
查看pod里的token值。
[root@vms81 ~]# kubectl exec -it pod1 -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImVNbXg4eVc3NFdwSnFCOU53aGo4SjNJYnlHaUwyMklwNXF0RDB5c3RlaVUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhMS10b2tlbi13bmQ4bSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJzYTEiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI1YWRiMjQzNi03MjVmLTRmNTYtOTk2YS1hMmUzMzY5Yjg4YTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpzYTEifQ.HmeNAIeHIdKP7NRlh0fpywuSJJzvBWZa5i11z6E3EMa9CRIEaNFkA-Jg805GyZEGk-22PybvkHRiyIqlXF7V_Xy5bkBqqZrYMZVHoUZboZLJG_VsYVcu9cdArn7ZNidFAtqArpIr33soVmAjZ8GFMINEvzDMBEj-rn07eIz9HCk0gQ_EKMWdrjh8Eg9BdtodP8vRnx4VizPfkjjLOmwmdrr4dYiGvcWrtsjLFi5jkUQrpT6PezFOlU4WhslCFOLSojahl7uExz-LWbzbJaWhHvw7Y8CVw9HlG_BedMGiZq7Sll7w7PkMwOSvgXjl2cs0veiMbR1HbwjqvKao1dy-yQ[root@vms81 ~]#
可以看到pod1里通过投射卷所挂载的token跟sa1对应的secret sa1-token-wnd8m里的token是一模一样的,这个token是永不过期的。
即使删除了pod之后重新创建pod,pod里的token仍然是不变的。
此时如果删除sa1-token-wnd8m这个secret,也会重新创建一个secret sa1-token-xxx,此时secret会生成一个新的token。
1.21版本到1.23版
下面的实验是基于kubernetes版本1.21.2。
首先创建一个名字为sa1的sa。
[root@vms91 ~]# kubectl create sa sa1
serviceaccount/sa1 created
[root@vms91 ~]# kubectl get sa
NAME SECRETS AGE
default 1 17h
sa1 1 9s
[root@vms91 ~]# kubectl get secrets
NAME TYPE DATA AGE
default-token-xj4vd kubernetes.io/service-account-token 3 17h
sa1-token-sbjzv kubernetes.io/service-account-token 3 39s
[root@vms91 ~]#
我们创建了一个名字为sa1的sa之后,系统自动创建了一个名字为sa1-token-sbjzv的secret。这个secret里包含了一个token。
[root@vms91 ~]# kubectl describe secrets sa1-token-sbjzv
Name: sa1-token-sbjzv
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: sa1
kubernetes.io/service-account.uid: e6e37668-7307-4d42-93fa-e453c27a51c3
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1066 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImxYZE5WOW5RcWVYRVlJN3NGOFhPakZJVlV1cFpjNGV6MHdHeko1XzBraW8ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhMS10b2tlbi1zYmp6diIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJzYTEiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJlNmUzNzY2OC03MzA3LTRkNDItOTNmYS1lNDUzYzI3YTUxYzMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpzYTEifQ.aW44kSJNAEm_A96-HMq-IIbQo8UM6nnvFlwZoRGLiOb0XvrSPr3DN84ox4yY64SCa-Z8Snig-UPdiDABre9X7P7vt44kg_NgMk7cy1gOAD8dm-1Q4pebvzGqqqOp-UUWWE43g7Wikr1cGHU63VRefH484nZoj5bIyRcidbyHhM60-UpYbu3beqBa0DSJ6DBZewZ52CG2Ojl7aCKJXcYp2bf_2XujVb91XdM3ildK2LQsNXzs4h-D-KFJZZ2YCSkayAriGI0bwPOeHNKeIo9rA4Cow9_KrpIT3nNVL8P8NESJUNlU08ALkkTCwbglTcg_tp_r4hqW6V0bPGSBDC957A
[root@vms91 ~]#
从这里我们能获取到对应的token,这个token也可以通过
kubectl get secrets sa1-token-sbjzv -o jsonpath='{.data.token}' | base64 -d
来获取,记住这种获取方式需要使用base64 -d进行解码。
这里的token是JWT结构的,我们把这里获取的token复制到jwt.io网站进行解码。
右侧部分显示了token被解码之后的内容,其中data部分是token里包含的sa1的信息。这里并没有任何的过期时间,也说明了我们创建sa1之后,所对应的token是永不过期的。
我们创建pod,写pod1的yaml文件内容如下。
[root@vms91 ~]# cat pod1.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
serviceAccount: sa1
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@vms91 ~]#
创建pod1
[root@vms91 ~]# kubectl apply -f pod1.yaml
pod/pod1 created
[root@vms91 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod1 1/1 Running 0 6s
[root@vms91 ~]#
查看pod1里的token值。
[root@vms91 ~]# kubectl exec -it pod1 -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImxYZE5WOW5RcWVYRVlJN3NGOFhPakZJVlV1cFpjNGV6MHdHeko1XzBraW8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjg4NzI2MTMzLCJpYXQiOjE2NTcxOTAxMzMsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJwb2QxIiwidWlkIjoiNjc5OGZmYjQtYzZiMi00MWEzLWE2YjAtZTI5ODM4N2ViZmY1In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYTEiLCJ1aWQiOiJlNmUzNzY2OC03MzA3LTRkNDItOTNmYS1lNDUzYzI3YTUxYzMifSwid2FybmFmdGVyIjoxNjU3MTkzNzQwfSwibmJmIjoxNjU3MTkwMTMzLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpzYTEifQ.k02zrswPNoa1n6imYPHmLIgDr4Sjz6PkBRZ23xGtnBPlMRiq720I0nml5LNFUHW-D1X2-GT_QN58CkSaRnv6PPHD38PUa1gGE6ILGBcnzECHHF9ZSVYTSMgi1ncY6gXmxLSdi83AVUp9xO3i4-M4GkfIYHAEBRoQpcRZ-MKUvSM1lHT3Gx3T_eGCvuv5g41H5sUoEp9OgYLPJKUQ3R3NlYRuMcDO7tgR8_vy-L8r3pVPyU29LbhbvPDCDxoeI9n-zyUcgwxqiYav3FrzgpI2Gb8B4FRo73AbTKAPt2xGikhdBCkuuUVtFpQdtgcArY7JBA1meNNp3yCJelgFhhhBqA[root@vms91 ~]#
可以看到pod1里通过投射卷所挂载的token跟sa1所生成的secret sa1-token-sbjzv里包含的token不一样。我们复制pod1里的那个token到jwt.io里进行解码查看。
可以看到这里的token多了一个过期时间,这个过期时间是1年(把鼠标放在exp的值上面会自动显示过期时间)。
在1.21到1.23的版本里,当我们创建了一个service account之后,系统也会自动创建一个secret。这个secret里会有一个永不过期的token。但是在pod里并不会使用secret的这个token。
当我们创建pod时,会kubelet会向tokenRequest api发送一个请求,申请一个新的token放在pod的/run/secrets/kubernetes.io/serviceaccount/token里。
如果手动申领一个token的语法如下。
curl -k -X "POST" "https://192.168.26.91:6443/api/v1/namespaces/default/serviceaccounts/sa1/token" \
-H "Authorization: Bearer TOKEN值 \
-H "Content-Type: application/json; charset=utf-8" \
-d $'{}'
这里TOKEN值需要具备相关的权限才行。
这个pod里的这个token会在1个小时后由kubelet重新重新去申领一个新的token,所以1小时之后再次查看这个token的话会发现token的内容是变化的。
[root@vms91 ~]# kubectl exec -it pod1 -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImxYZE5WOW5RcWVYRVlJN3NGOFhPakZJVlV1cFpjNGV6MHdHeko1XzBraW8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjg4NzI5MzM5LCJpYXQiOjE2NTcxOTMzMzksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJwb2QxIiwidWlkIjoiNjUwNWUzOWUtNzA2Mi00Y2M3LWI3YjMtNTA3ZjU1MDMzMzY3In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYTEiLCJ1aWQiOiJlNmUzNzY2OC03MzA3LTRkNDItOTNmYS1lNDUzYzI3YTUxYzMifSwid2FybmFmdGVyIjoxNjU3MTk2OTQ2fSwibmJmIjoxNjU3MTkzMzM5LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpzYTEifQ.BvI9X7OnNVD5ZEHqMKOJH3JpfztTvJ7Nx8Q0PFdaOwSPrYfskkoRe_Sqx7q_0WB7fugUkv42FJW75huCrpHAOtjejLhtJ4bWiIe0k8WHNW788k2tjvD_sSJyXcaME2Vc3JxUT5PJFJ0LJt1Kdc2Y8kpMCf3nph0zBrSv4oCYhNCEvSn8dNLO9Al7QZ-UNC1Zwc4H5r3Jdg-0JwTaY3Utn_nL8H6EtITp6tNck441tXCM92ph-swF2_Ayiyz8xi09c2LpNKBUWbyrMxFx-lWpzcW32u85nj6YryJPjK4ZRqrTjrgD_jspY6mJJONFH267Q4c1dCBsxwMd5aWq3zsaiQ[root@vms91 ~]#
如果删除此pod重新创建的话,则会重新申领token,被删除pod里的token立即过期。
1.24版本
下面的实验是基于kubernetes版本1.24.2。
[root@vms71 ~]# kubectl create sa sa1
serviceaccount/sa1 created
[root@vms71 ~]# kubectl get sa
NAME SECRETS AGE
default 0 46h
sa1 0 5s
[root@vms71 ~]# kubectl get secrets
No resources found in ns1 namespace.
[root@vms71 ~]#
从这里看到并没有为sa1创建一个secret。
创建pod1用的yaml文件pod1.yaml,内容如下。
[root@vms71 ~]# cat pod1.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
serviceAccount: sa1
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@vms71 ~]#
创建pod1并查看pod1里的token值。
[root@vms71 ~]# kubectl apply -f pod1.yaml
pod/pod1 created
[root@vms71 ~]#
[root@vms71 ~]# kubectl exec -it pod1 -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6InFwbWtnTnM3Nk9HTjZwSU1PdVp2c3ZGY2VqX0JjQXE5bkcyRUZHd3NTZ00ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjg4NzMxMzA1LCJpYXQiOjE2NTcxOTUzMDUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJuczEiLCJwb2QiOnsibmFtZSI6InBvZDEiLCJ1aWQiOiIyNmFhNzQ4Ny02ODA5LTQwNmItODcyZi0wYjExZjFjZTg4YzQifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6InNhMSIsInVpZCI6ImIzMDI0YWQzLWU2ZjItNGE1MC1iMjE4LWJlM2I5YjhkMGNiOSJ9LCJ3YXJuYWZ0ZXIiOjE2NTcxOTg5MTJ9LCJuYmYiOjE2NTcxOTUzMDUsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpuczE6c2ExIn0.bOQeFK-U4tvigUsbj_o7xfxrVBDyvdwQdxnd4ZGPb_m_P1EuffwJB80mOwIYPTijNpj6OzWdaGMedFZdv6TKcWFZ4DZWLB6UXtOS0rXIsl9OyKEU2RyCIssR0LKmx6L4mDznEk6jLv7tOY3eLRfX7FgjmCK_Y1Hipju9d2v92_poPb1oHHMP00OZs0XUfb-V0arIGcYm52kiJ_E4PuWpH9AzpAG916wmdnoIpnUxYsFhX020I5Pw8jLsxaPUAVc1AlOYr8IKgyvilqJljjivbZA9Sru_LjlsboEfUFqFQscu57kSR58WJos3gYJteAyM94pIdb-5RU0U75p3Ih6eLA[root@vms71 ~]#
我们把pod1里的那个token复制到jwt.io里进行解码查看。
可以看到这里的token的有效期也为1年,和1.21版本一样,这个token在pod里也是每1小时会更新一次。如果pod被删除重建,那么会重新申领一个新的token,被删除pod里的token立即过期。
总结
1.20(含1.20)之前的版本,在创建sa时会自动创建一个secret,然后这个会把这个secret通过投射卷挂载到pod里
1.21~1.23这几个版本,在创建sa时也会自动创建secret,但是在pod里并不会使用secret里的token,而是由kubelet到tokenRequest api去申请一个token。
1.24版本,在创建sa时不再自动创建secret了,是由kubelet到tokenRequest api去申请token。