深色模式
ServiceAccount 与 Token 管理
摘要:本文面向需要为工作负载配置身份认证、并希望消除长期静态 token 风险的 SRE / 平台工程师。覆盖 SA 身份模型、v1.22+ 的投影令牌、v1.24 起默认不再自动创建 Secret token、以及 token 不自动挂载与 node audience 限制。适用版本:Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+(投影令牌 v1.20+ GA;Legacy 自动创建默认关闭自 v1.24,GA 自 v1.27)
- 需要
kubectl且具有创建 ServiceAccount 的权限
核心概念:ServiceAccount 是什么
ServiceAccount 是 Kubernetes API 中的非人类身份对象,命名空间级。Workload、系统组件、集群内外实体都可以用它向 API server 认证。它与 User Account 的区别:
| 维度 | ServiceAccount | User Account |
|---|---|---|
| 位置 | Kubernetes API(ServiceAccount 对象) | API server 外部,通常不在 API 中 |
| 作用域 | 命名空间级,每命名空间有 default SA | 集群级 |
| 鉴权 | Kubernetes RBAC | RBAC 或外部 IAM |
| 用途 | workload、自动化 | 人 |
每个 ServiceAccount 的凭证是一个签名的 JWT。当客户端以该 SA 身份访问 API server 时,在 HTTP 请求里带 Authorization: Bearer <token>。API server 校验:签名 → 是否过期 → token claims 中的对象引用是否仍有效 → audience 是否匹配。
两种 Token:投影令牌 vs Legacy Secret
这是本文最关键的版本差异。
投影令牌(推荐,v1.22+ 默认)
v1.22 起,kubelet 通过 TokenRequest API 为每个 Pod 获取一个短生命周期、自动轮换的令牌,以 projected volume 挂载到 /var/run/secrets/kubernetes.io/serviceaccount/。令牌绑定到 Pod 生命周期:Pod 删除,令牌即失效。
yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
namespace: default
spec:
serviceAccountName: my-sa
containers:
- name: app
image: nginx:1.27
volumeMounts:
- name: sa-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
volumes:
- name: sa-token
projected:
sources:
- serviceAccountToken:
expirationSeconds: 3600
path: token1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Legacy Secret Token(不推荐,已淘汰自动创建)
v1.24 之前,Kubernetes 会为每个 SA 自动创建一个长期有效的 Secret token(LegacyServiceAccountTokenNoAutoGeneration feature gate 默认开启于 v1.24–v1.26)。这些 token 不过期、不轮换,泄漏风险高。该 feature gate 在 v1.27 提升为 GA 并移除(即自动创建永久 token 的能力被永久关闭,但你仍可手动创建)。
不要为集群外应用创建长期 Secret token
官方文档明确:为集群外应用长期存储 Secret 中的 SA token 是安全风险——一旦泄漏可被长期冒用。替代方案:用 TokenRequest API 获取短生命周期令牌,或用受保护的私钥 + 证书,或自建认证 webhook。
关闭不必要的 token 自动挂载
默认每个 Pod 都会自动挂载其 SA 令牌。若 workload 不需要访问 API server,应关闭以缩小攻击面(这是 GKE 等加固指南的明确要求)。
yaml
apiVersion: v1
kind: Pod
metadata:
name: no-api
spec:
automountServiceAccountToken: false
containers:
- name: app
image: nginx:1.271
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
也可在 ServiceAccount 级别统一关闭,Pod 未显式开启则继承:
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: no-api-sa
namespace: default
automountServiceAccountToken: false1
2
3
4
5
6
2
3
4
5
6
automountServiceAccountToken 的生效优先级(从高到低):Pod spec → SA spec → 默认 true。
Node audience 限制(v1.33 Beta)
ServiceAccountNodeAudienceRestriction feature gate(Beta,v1.33 默认开启)让 NodeRestriction 准入插件限制 kubelet 通过 TokenRequest API 能请求的 audience:默认只能请求该节点上 Pod 已引用的 audience(投影令牌或 CSI driver token)。若需给 kubelet 额外 audience,需 RBAC 授予 request-serviceaccounts-token-audience 动词。
适用范围
该限制只作用于 kubelet(节点身份),不影响 TokenRequest API 的其他调用方。对缓解"节点被控后批量申请任意 audience token"有意义,但非万能。[版本相关:v1.33 Beta,低于该版本不生效,需评估是否启用]
生产实践
- 每个 workload 一个 SA,避免共用
default。 - 不需要 API 的 Pod 关闭 automount(配合 PSA 检查)。
- 审计 Legacy token:找出仍存在 Secret 中的 SA token 并迁移。
- 集群外访问用短令牌:用 TokenRequest 获取,配合外部轮换。
bash
# 找出仍携带 Legacy token Secret 的 ServiceAccount(需 jq)
kubectl get secrets -A -o json | jq -r '.items[]
| select(.type=="kubernetes.io/service-account-token")
| .metadata.namespace + "/" + .metadata.name'
# 手动为 SA 申请一个短期令牌(调试用,默认 1h)
kubectl -n default create token my-sa --duration=1h1
2
3
4
5
6
7
2
3
4
5
6
7
故障排查
- Pod 拿不到 token:检查 SA 是否存在、kubelet 是否正常运行、projected volume 是否配置。
- API 调用 401:多半是 token 过期或 audience 不匹配;检查
kubectl get secret之外的投影卷路径。 - Legacy token 突然消失:确认集群 ≥ v1.24 且未手动创建;这是预期行为。
回滚与清理
删除 SA 需谨慎
删除 ServiceAccount 会使依赖它的 Pod 令牌失效,导致 workload 无法访问 API(如 operator 自愈中断)。先确认无 RoleBinding 引用、无 Pod 依赖,再删除。可用 kubectl auth can-i 评估影响。
参考资料
- Kubernetes 官方文档 - Service Accounts,访问日期:2026-10-08。
- Kubernetes 官方文档 - Configure Service Accounts for Pods,访问日期:2026-10-08。
- Kubernetes 官方文档 - Service Account Token Volume Projection,访问日期:2026-10-08。
- GKE 最佳实践 - 不要自动挂载 SA token,访问日期:2026-10-08。