什么是服务账号?
服务账号是在 Kubernetes 中一种用于非人类用户的账号,在 Kubernetes 集群中提供不同的身份标识。 应用 Pod、系统组件以及集群内外的实体可以使用特定 ServiceAccount 的凭据来将自己标识为该 ServiceAccount。 这种身份可用于许多场景,包括向 API 服务器进行身份认证或实现基于身份的安全策略。
如何使用服务账号
要使用 Kubernetes 服务账号,你需要执行以下步骤:
- 使用像
kubectl
这样的 Kubernetes 客户端或定义对象的清单(manifest)创建 ServiceAccount 对象。 - 使用鉴权机制(如 RBAC)为 ServiceAccount 对象授权。
- 在创建 Pod 期间将 ServiceAccount 对象指派给 Pod。
如果你所使用的是来自外部服务的身份,可以获取 ServiceAccount token,并在该服务中使用这一令牌。
ServiceAccount token 变化
- 1.20(含 1.20)之前的版本,在创建 sa 时会自动创建一个 secret,然后这个会把这个 secret 通过投射卷挂载到 pod 里,该 secret 里面包含的 token 是永久有效的。
- 1.21~1.23 版本,在创建 sa 时也会自动创建 secret,但是在 pod 里并不会使用 secret 里的 token,而是由 kubelet 到 TokenRequest API 去申请一个 token,该 token 默认有效期为一年,但是 pod 每一个小时会更新一次 token。
- 1.24 版本及以上,在创建 sa 时不再自动创建 secret 了,只保留由 kubelet 到 TokenRequest API 去申请 token。
创建 ServiceAccount token
创建一个 ServiceAccount
创建一个 sa-demo 的 ServiceAccount,文件名为 build-robot.yaml:
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-robot
automountServiceAccountToken: false
创建并查看资源:
# kubectl create -f build-robot.yaml
serviceaccount/build-robot created
# kubectl get serviceaccounts build-robot
NAME SECRETS AGE
build-robot 0 11s
使用 RBAC 为 ServiceAccount 对象授权
这里为做演示,直接使用 cluster-admin 这个角色:
kubectl create clusterrolebinding build-robot-clusterrolebinding --clusterrole=cluster-admin --serviceaccount=default:build-robot
如果需要绑定其他权限,则可以自定义 Role 或 ClusterRole,再绑定到这个 ServiceAccount 上。
创建 token
token 分为临时请求 token 和永久 token。相对于永久 token,临时请求token 更为安全。临时请求 token 挂载与 Pod 内,kubelet 组件会替 Pod 请求 token 并将其保存起来;通过将 token 存储到一个可配置的路径以使之在 Pod 内可用;kubelet 会在 token 存在期达到其 TTL 的 80% 的时候或者令牌生命期超过 24 小时的时候主动请求将其轮换掉,并且能够在挂载它们的 Pod 被删除时自动被废弃。
创建临时 token
要启用和使用 request token 映射,必须向kube-apiserver指定以下每个命令行参数:
- --service-account-issuer:定义 ServiceAccount token 发放者是谁。
- --service-account-key-file:指定公钥文件路径。
- --service-account-signing-key-file:指定私钥文件路径。
- --api-audiences(可以省略):为 ServiceAccount token 定义其受众。
使用 nginx 镜像来创建一个名为 vault-token 的 token,挂载名为 build-robot 的 ServiceAccount,文件名为 pod-projected-svc-token.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx-1
spec:
containers:
- image: nginx:1.22.0
name: nginx-1
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: vault-token
serviceAccountName: build-robot
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken:
path: vault-token
expirationSeconds: 7200
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
创建并查看:
# kubectl create -f pod-projected-svc-token.yaml
pod/nginx-1 created
# kubectl get -f pod-projected-svc-token.yaml
NAME READY STATUS RESTARTS AGE
nginx-1 1/1 Running 0 12s
创建资源后,会在 /var/run/secrets/tokens/ 下创建一个 vault-token 文件:
# kubectl exec -it nginx-1 -- ls /var/run/secrets/tokens/
ca.crt namespace vault-token
# kubectl exec -it nginx-1 -- cat /var/run/secrets/tokens/vault-token
eyJhbGciOiJSUzI1NiIsImtpZCI6Impib1RRNHIzMGpfMmpWXzBXS3FNT0FtdFlBNTRSbTdSdEJfbFNEQ25jdVkifQ.eyJhdWQiOlsidmF1bHQiXSwiZXhwIjoxNzI1MDM5NjQxLCJpYXQiOjE3MjUwMzI0NDEsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiZDQ4MjJhNGUtZGRlMS00NDQxLTllY2UtZjNiNjg1MDNmYjdlIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoiazhzIiwidWlkIjoiMmU4NWMwZWMtZmEyMi00NjRkLWI2ZGUtMDRkMGFlMzA5YTU5In0sInBvZCI6eyJuYW1lIjoibmdpbngiLCJ1aWQiOiI3OGM0ZDA5Ny1hYTRiLTQ0MmYtYjk5OS01YjJmZjYzNmMwNWYifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6ImJ1aWxkLXJvYm90IiwidWlkIjoiMzQxNTIxYjQtYzA0Ny00ZGRmLTgyNGYtOTMxZjEzZGJhNmVhIn19LCJuYmYiOjE3MjUwMzI0NDEsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmJ1aWxkLXJvYm90In0.NXigSqMdBzPowK2Zoqc9bsbS3NgPgq7IUUw9ohpSBjz9ies3ydxvnRkdIqYSOA_pAn4QNORybHWOEIXq69ic7osXjuY6xVN54Ct6_KJyIF2gp24VKLEV7B_iLbh575k5ZjhPa00HHgppCe_cnXn9IoBP5V8oX2YLm1Jo4rld6QGoKNFm54VGL-46m6w559_zMLsKBorSY5k1F7B9NeZ9rAgcu_Aa3KiTej_lnAyWbWQPAMgx0t7LO5IW55EU6FhhZcicRwq8fOfUTM9lutHqUH5D8_gONMzaS4sbhkgyHMZeki0Po4i6oJe_seSwP968gbhcCPfm0Q40pE6U6jn6hA
把这个 token 复制到 https://jwt.io/ 上解析这个 token:
其中里面有个 exp 字段和 iax 字段,分别指过期时间和生效时间,刚好相差 7200 秒,就是 2 个小时。符合上面 yaml 指定的 expirationSeconds 值。
使用 token 去访问 api Server
进入 pod 中:
# kubectl exec -it nginx-1 -- bash
root@nginx-1:/# ls /var/run/secrets/tokens/
ca.crt namespace vault-token
root@nginx-1:/# export CURL_CA_BUNDLE=/var/run/secrets/tokens/ca.crt
root@nginx-1:/# TOKEN=$(cat /var/run/secrets/tokens/vault-token)
root@nginx-1:/# curl -H "Authorization: Bearer $TOKEN" https://kubernetes.default/api/
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "192.168.0.2:6443"
}
]
}
能正常访问到 api Server。
创建长期 token
如果需要为 ServiceAccount 获得一个 API 令牌,你可以创建一个新的、带有特殊注解 kubernetes.io/service-account.name
的 Secret 对象。
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: build-robot-secret
annotations:
kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
EOF
然后通过kubectl describe
即可获取一个长期的 token:
# kubectl describe secrets build-robot-secret
Name: build-robot-secret
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: build-robot
kubernetes.io/service-account.uid: 341521b4-c047-4ddf-824f-931f13dba6ea
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1107 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6Impib1RRNHIzMGpfMmpWXzBXS3FNT0FtdFlBNTRSbTdSdEJfbFNEQ25jdVkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImJ1aWxkLXJvYm90LXNlY3JldCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJidWlsZC1yb2JvdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjM0MTUyMWI0LWMwNDctNGRkZi04MjRmLTkzMWYxM2RiYTZlYSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmJ1aWxkLXJvYm90In0.ZEd-xx8khQmuAKY7gryuHMgbqrvAQwctJA7ztXyCd9eWweUOj99wi2ILHUckt7-MWCu47ad9pZ1pSsUBVnXBdWpjeUzOgX_s7FrMNz8M8m8EfWWv1xxp9cdBQWdwqDNBxa4X6isCS-OBdYMSwcHgvAKFVOkZ1rMjXXgMnYXs_DAoqSNlFnSSJLGUpa6zowvpzNjCn5chfqS1rUwBDpBvB_vg-WU5qnaXRAhNRY22shjtY230fQO2Vc53244mM_1mlBwlWzIjLoFKXZv-v5xhPPDAbIsINe8YmC6pBkSIXwqCHRo1Y1Chh2Are6U6ltWl2sLLAHSdoNiJtYgpos5Aug
把这个 token 拿到 https://jwt.io/ 上解析这个 token:
已经没了exp 字段。
使用 token 去访问 api Server
创建一个 pod 把 build-robot-secret 给 nginx 使用,文件名为pod-token-forever-valid.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx-2
spec:
containers:
- image: nginx:1.22.0
name: nginx-2
volumeMounts:
- name: secret-volume
mountPath: /etc/secret
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: build-robot-secret
由于在创建 ServiceAccount 的时候,指定了automountServiceAccountToken: false
,所以不会自动挂载 secret,需要手动挂载上去。
如果没指定 automountServiceAccountToken,默认为 true,则不用手动挂载。具体挂载目录可通过创建 pod 后,通过 describe pod 进行查看。
创建并查看资源:
# kubectl create -f pod-token-forever-valid.yaml
pod/nginx-2 created
# kubectl get -f pod-token-forever-valid.yaml
NAME READY STATUS RESTARTS AGE
nginx-2 1/1 Running 0 12s
进入到 pod 中使用 token 去访问 api server:
# kubectl exec -it nginx-2 -- bash
root@nginx-2:/# ls /etc/secret/
ca.crt namespace token
root@nginx-2:/# export CURL_CA_BUNDLE=/etc/secret/ca.crt
root@nginx-2:/# TOKEN=$(cat /etc/secret/token)
root@nginx-2:/# curl -H "Authorization: Bearer $TOKEN" https://kubernetes.default/api/
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "192.168.0.2:6443"
}
]
}
经测试能成功访问。
参考:
https://mp.weixin.qq.com/s/F0V8nyo3LtATFmS7pHuxXw
https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/service-accounts-admin/
https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-service-account/