基本原理
Pod 中的进程在访问 API Server 的时候,通常使用 ServiceAccount 来表明自己的身份。ServiceAccount 为运行在 Pod 中的进程提供一个安全、自动化的身份验证机制,使得这些进程可以与 Kubernetes API Server 进行交互。
1.24 及之后的版本,ServiceAccount 的大致原理是:
-
当创建 Pod 的时候,在 admission 阶段,serviceaccount admission 会通过 projected 方式为 Pod 挂载一个 token,挂载的路径是
/var/run/secrets/kubernetes.io/serviceaccount
。如下所示,
apiVersion: v1 kind: Pod metadata: name: calico-node-ffkx6 namespace: kube-system spec: containers: - #... volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-mnxtg readOnly: true volumes: #... - name: kube-api-access-mnxtg 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
-
Pod 被调度到 Node 上后,kubelet 中的 projected volume mounter 会根据 volumesMount 中的 volume 类型,为 Pod 挂载对应的文件。当发现存在 ServiceAccountToken 类型的 projected source 时,就会调用 apiserver 的 TokenRequest 接口,为当前 Pod 请求临时的 Token,之后将 token 挂载到相应的目录中。生成的 token 的有效期只有 3607s,同时 kubelet 会自动刷新这个 token 来保证它不会过期。
我们可以通过下面命令获取到 Pod 中使用的 token,得到一个 JWT 结构的字符串,
$ kubectl exec -it ${pod} -- cat /run/secrets/kubernetes.io/serviceaccount/token eyJhbGciOiJSUzI1NiIsImtpZCI6InFwbWtnTnM3Nk9HTjZwSU1PdVp2c3ZGY2VqX0JjQXE5bkcyRUZHd3NTZ00ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjg4NzMxMzA1LCJpYXQiOjE2NTcxOTUzMDUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJuczEiLCJwb2QiOnsibmFtZSI6InBvZDEiLCJ1aWQiOiIyNmFhNzQ4Ny02ODA5LTQwNmItODcyZi0wYjExZjFjZTg4YzQifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6InNhMSIsInVpZCI6ImIzMDI0YWQzLWU2ZjItNGE1MC1iMjE4LWJlM2I5YjhkMGNiOSJ9LCJ3YXJuYWZ0ZXIiOjE2NTcxOTg5MTJ9LCJuYmYiOjE2NTcxOTUzMDUsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpuczE6c2ExIn0.bOQeFK-U4tvigUsbj_o7xfxrVBDyvdwQdxnd4ZGPb_m_P1EuffwJB80mOwIYPTijNpj6OzWdaGMedFZdv6TKcWFZ4DZWLB6UXtOS0rXIsl9OyKEU2RyCIssR0LKmx6L4mDznEk6jLv7tOY3eLRfX7FgjmCK_Y1Hipju9d2v92_poPb1oHHMP00OZs0XUfb-V0arIGcYm52kiJ_E4PuWpH9AzpAG916wmdnoIpnUxYsFhX020I5Pw8jLsxaPUAVc1AlOYr8IKgyvilqJljjivbZA9Sru_LjlsboEfUFqFQscu57kSR58WJos3gYJteAyM94pIdb-5RU0U75p3Ih6eLA
之后将这个 token 复制到 https://jwt.io/ 进行解码,可以看到 token 中包含了三部分:Header、Payload 和 Signature,也就是 JWT 由这三部分组成。其中,
-
Payload 中包含了使用的 ServiceAccount、Pod 信息、有效时间和过期时间等。可以看到以下信息:
- token 的过期时间(exp)为 1 年。
- token 的有效时间为 3607,即在 1 个小时后会由 kubelet 重新申领一个新的 token(warnafter)。
如果 pod 重新创建的话,则会重新申领 token,被删除的 pod 里的 token 立即过期。综上来说,token 的有效性由 pod 的生命周期(比如 Pod 删除了,token 就会失效了)、有效时间和过期时间决定。
-
其中 Signature 相当于 Header + Signature 通过私钥加密后的内容(跟数字签名有点类似)。这里的私钥,其实就是之前提到的 kube-controller-manager 会配置的私钥,这个私钥的作用就是用于生成 Service Account Token 的 Signature。
containers: - command: - kube-controller-manager # 对 service account token 进行加密的私钥文件的路径。kube-controller-manager 使用这个私钥生成相应 service account token,Pod 访问 kube-apiserver 的时候会使用这些 token,然后这些 token 会被 kube-apiserver 验证,由于 kube-apiserver 启动的时候指定了相应的公钥,因此它可以进行验证。 - --service-account-private-key-file=/etc/kubernetes/pki/sa.key
-
-
Pod 中的程序在请求 API Server 的时候,也就是通过 client-go 库在请求 APIServer 的时候,client-go 会默认搜索
/var/run/secrets/kubernetes.io/serviceaccount
这个路径,然后使用这个 token 向 API Server 发起请求。 -
API Server 在收到这个 token 后,根据 JWT 的规范对内容进行完整性校验,这里的校验就包括使用 kube-apiserver 启动时配置的 service account token 的公钥对 token 的 signature 进行解密,验证内容是否篡改。同时校验有效性等。校验通过后根据 token 中的 service account 进行认证鉴权。
containers: - command: - kube-apiserver # 用于验证 service account token 的公钥 - --service-account-key-file=/etc/kubernetes/pki/sa.pub
版本变化
1.20 版本及之前
- 创建 serviceaccount 的时候,会自动创建一个 secret。这个 secret 会包含一个 token,这个 token 是永远不过期的。
- 如果 Pod 中使用了相应的 serviceaccount,那么 Pod 中被挂载的 token 跟上述自动创建的 secret 中的 token 内容是一模一样的。
1.21-1.23
- 创建 serviceaccount 的时候,会自动创建一个 secret。这个 secret 会包含一个 token,这个 token 是永远不过期的。
- 如果 Pod 中使用了相应的 serviceaccount,那么 Pod 中被挂载的 token 并不会使用上述自动创建的 secret 中的token,而是由 kubelet 发送一个 tokenRequest 请求创建的。
1.24
- 创建 serviceaccount 的时候,不会再自动创建一个 secret。
- 如果 Pod 中使用了相应的 serviceaccount,那么 Pod 中被挂载的 token 是由 kubelet 发送一个 tokenRequest 请求创建的。
相关链接
服务账户service account在kubernetes1.24中的变化:https://blog.csdn.net/lduan_001/article/details/125667733