本章你将学会
- ServiceAccount 的作用是什么,默认行为是什么
- Pod 是怎样拿着 ServiceAccount 身份去访问 API Server 的
- v1.24+ 之后,ServiceAccount Token 为什么变了
- 什么是 Token 投射(projected token)与身份边界控制
- 工作负载身份管理在生产环境里的最佳实践
1. ServiceAccount 到底是什么
ServiceAccount 是 Kubernetes 为 Pod / 工作负载 提供的身份对象。它和“给人使用的账号”不是一类东西:用户账号更偏向人类运维、开发者、CI 系统,而 ServiceAccount 更偏向运行在集群里的应用进程::cite[174]。
从使用角度看,ServiceAccount 主要解决这些问题:
- Pod 调用 Kubernetes API 时,用什么身份发起请求
- 某个工作负载应该拥有哪些 RBAC 权限
- 某个工作负载如何和外部云厂商或密钥系统建立信任关系
- 镜像拉取、控制器运行、自动化任务等场景如何获得独立身份::cite[174]
2. 默认行为:每个命名空间都有一个 default ServiceAccount
Kubernetes 会在每个命名空间里自动创建一个名为 default 的 ServiceAccount。如果你创建 Pod 时没有显式指定 spec.serviceAccountName,那这个 Pod 就会自动使用该 namespace 下的 default ServiceAccount::cite[174]。
这一点非常关键,因为它意味着:
- 你不写
serviceAccountName,不等于“没有身份” - 你只是退回到了
default这个身份
官方同时说明,default ServiceAccount 默认除了认证后的基础 API discovery 权限外,并不会自动拥有额外业务权限;但如果团队后续给它绑定了更大权限,那么所有没显式声明账号的 Pod 都会一起继承这些权限::cite[174]。
3. ServiceAccount 的使用路径:创建、授权、分配给 Pod
一个典型流程通常分 3 步:
- 创建 ServiceAccount
- 使用 RBAC 给它授予最小权限
- 在 Pod 或 Deployment 中通过
serviceAccountName使用它::cite[174]
4. 实操:给一个只读工作负载配置独立身份
4.1 场景目标
我们希望一个应用:
- 运行在
app-demo命名空间 - 使用独立的 ServiceAccount
- 只允许读取本命名空间中的 Pod
- 禁止自动挂载不必要的 token 到不需要访问 API 的容器
4.2 完整 YAML
apiVersion: v1
kind: Namespace
metadata:
name: app-demo
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-viewer
namespace: app-demo
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-read-only
namespace: app-demo
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-read-only-binding
namespace: app-demo
subjects:
- kind: ServiceAccount
name: pod-viewer
namespace: app-demo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pod-read-only
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: viewer
namespace: app-demo
spec:
replicas: 1
selector:
matchLabels:
app: viewer
template:
metadata:
labels:
app: viewer
spec:
serviceAccountName: pod-viewer
automountServiceAccountToken: true
containers:
- name: main
image: curlimages/curl:8.8.0
command: ["sleep", "3600"]
4.3 应用配置
kubectl apply -f sa-demo.yaml
4.4 验证权限
kubectl auth can-i list pods \
--as=system:serviceaccount:app-demo:pod-viewer \
-n app-demo
kubectl auth can-i get secrets \
--as=system:serviceaccount:app-demo:pod-viewer \
-n app-demo
如果第一条是 yes、第二条是 no,说明这个工作负载身份已经被限制在恰当范围内。
5. Pod 是如何使用 ServiceAccount 访问 API Server 的
当 Pod 指定了 spec.serviceAccountName 之后,Kubernetes 会为它准备访问 API Server 所需的凭据。自 v1.22 起,默认机制已经变成:通过 TokenRequest API 获取短时、自动轮换的 token,并作为 projected volume 挂载到 Pod 内::cite[174]。
也就是说,现代 Kubernetes 的默认思路不是“塞一个长期 Secret 给 Pod”,而是“按需发一个短期、可轮换、和 Pod 绑定的令牌”。这比旧式静态 Token 安全得多::cite[175]。
5.1 默认挂载位置
当 ServiceAccount admission controller 生效时,Pod 创建过程中会被自动补充一个卷,并挂载到下面这个路径:
/var/run/secrets/kubernetes.io/serviceaccount
这个卷里通常包含:
token:访问 API Server 的令牌ca.crt:集群 CA 证书namespace:当前 Pod 所在命名空间信息::cite[175]
5.2 API 调用过程怎么理解
工作负载容器拿到 token 后,向 https://kubernetes.default.svc 发请求时会把它作为 Bearer Token 带上。API Server 识别出这个 token 对应的 ServiceAccount,再走授权判断,看它是否可以执行相应操作::cite[174]。
6. Token 投射:v1.24+ 之后最重要的变化之一
在旧版本 Kubernetes 里,ServiceAccount 往往会自动生成一个长期存在的 Secret 型 token。这个机制的问题很明显:
- 生命周期长
- 泄露后可长期滥用
- 很难做边界收敛与失效控制
Kubernetes 从 v1.24 开始不再自动为每个 ServiceAccount 创建这类长期 token Secret;此后默认推荐使用 TokenRequest + projected volume 机制。长期 token 仍然可以手工创建,但已经不再是推荐路径::cite[174]。
6.1 投射 token 的几个安全特征
根据官方说明,基于 TokenRequest 的 Pod 内 token 具备以下特点:
- 短生命周期:默认有效期通常为 1 小时
- 自动刷新:kubelet 会在过期前自动轮换
- Pod 绑定:token 与具体 Pod 关联
- 默认 audience 为 kube-apiserver:降低滥用面::cite[175]
6.2 如果 Pod 被删除,会发生什么
对于 Pod 绑定的 ServiceAccount token,如果 Pod 被删除,这些 token 也会随之失效。因此它们比过去那种长期 Secret token 更符合“最小暴露时间”的安全原则::cite[175]。
7. 实操:显式使用 ServiceAccount Token Projection
如果你的应用确实需要 API Token,但你希望更明确地控制 token 的挂载路径、受众(audience)和有效期,可以显式写出 projected volume。
7.1 完整 YAML
apiVersion: v1
kind: Pod
metadata:
name: api-client
namespace: app-demo
spec:
serviceAccountName: pod-viewer
automountServiceAccountToken: false
containers:
- name: client
image: curlimages/curl:8.8.0
command: ["sleep", "3600"]
volumeMounts:
- name: k8s-api-token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: k8s-api-token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: https://kubernetes.default.svc
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
fieldPath: metadata.namespace
7.2 为什么推荐这么写
这种方式的好处是:
- 明确知道 token 放在哪
- 控制 token 生命周期
- 控制 token 的 audience
- 避免把默认挂载能力“无意中”给到所有容器
这正是“身份边界控制”的核心:不是只有有没有权限的问题,还包括凭据如何分发、挂载到哪里、多久失效、谁能看到::cite[175]。
8. 自动挂载控制:automountServiceAccountToken
官方最佳实践非常强调一点:不是所有 Pod 都应该自动拿到 ServiceAccount token。如果某个工作负载根本不需要访问 Kubernetes API,就应该把自动挂载关掉::cite[167]。
你可以在两个层面控制它:
- 在 ServiceAccount 上设置
automountServiceAccountToken: false - 在 Pod 上设置
automountServiceAccountToken: false
Pod 级配置可以覆盖 ServiceAccount 级配置,这在“同一个 ServiceAccount 供多个工作负载使用,但只有少数 Pod 需要 token”时会很有用::cite[175]。
一个完全不需要 API 身份的 Pod,可以这样写:
apiVersion: v1
kind: Pod
metadata:
name: no-api-access
namespace: app-demo
spec:
serviceAccountName: pod-viewer
automountServiceAccountToken: false
containers:
- name: app
image: nginx:1.27
9. 手动创建长期 token,为什么不推荐
虽然 Kubernetes 仍允许你手动创建 kubernetes.io/service-account-token 类型的 Secret,并由控制平面填充 token,但官方已经明确建议:优先使用短期 TokenRequest,而不是长期 Secret token::cite[175]。
原因主要有三个:
- 长期 bearer token 一旦泄露,风险持续时间长
- 轮换和吊销成本高
- 很容易被脚本、日志、临时调试文件无意保留
从 v1.29 开始,历史上自动生成但长期未使用的 legacy ServiceAccount token 还会被标记失效,并在持续未使用后被清理。这说明 Kubernetes 整体方向就是逐步淘汰长期自动 token::cite[175]。
10. 工作负载身份管理的最佳实践
10.1 一个应用一个 ServiceAccount
不要让一堆 Deployment 共用 default。一旦共用,你很难回答下面这个问题:
这个 token 到底代表哪个应用?
独立 ServiceAccount 可以让 RBAC、审计、问题定位都更清晰::cite[174]。
10.2 不需要 API,就不要挂 token
这是一条最容易落地、收益又很高的规则。很多业务容器根本不需要访问 Kubernetes API,把 token 挂进去只是在扩大暴露面::cite[167]。
10.3 ServiceAccount 只配最小权限
ServiceAccount 只是身份,不应该天然拥有大权限。真正的权限来自 RBAC 绑定,因此你应该按场景拆账号:
- 只读账号
- 控制器账号
- Job 账号
- 外部系统同步账号
不要一个 ServiceAccount 既能读 Secret、又能创建 Deployment、还能 patch Admission 配置。
10.4 跨命名空间访问要显式设计
ServiceAccount 本身是 namespaced 对象,但通过 RBAC 绑定,它可以访问其他命名空间的资源。因此“账号在哪个 namespace”不等于“权限就只在这个 namespace 内”。跨 namespace 权限要单独审查::cite[174]。
10.5 外部身份集成优先用短期凭据
如果你的应用要和云厂商 IAM、Vault、密钥系统打通,优先考虑基于短期 token、受众约束、信任映射的方式,而不是把永久云凭证直接写进 Secret 或镜像。ServiceAccount 应该是身份桥梁,而不是长期密钥仓库::cite[174]。
11. 常见误区
误区一:默认 ServiceAccount 没权限,所以可以放心共用
今天没权限,不代表明天没人给它绑上角色。平台上最常见的权限污染,就是默认账号一点点变成“历史包袱账号”。
误区二:只要是只读 token 就没风险
不对。只读也可能读到 Secret、Pod 规范、镜像地址、环境变量引用、控制器状态等敏感信息。
误区三:Pod 里有 token 是正常现象,不用管
正常不等于合理。你应该问的是:这个 Pod 真的需要它吗?
12. 本章小结
ServiceAccount 是 Kubernetes 工作负载身份的基础设施。你要记住的重点有这几条:
- 每个 namespace 都有
defaultServiceAccount,不显式指定就会自动使用它::cite[174] - 现代 Kubernetes 默认通过 TokenRequest + projected volume 给 Pod 提供短期、自动轮换的 token::cite[174]
- v1.24+ 不再自动为每个 ServiceAccount 创建长期 Secret token,长期 token 只应在特殊场景下手工创建并严格管控::cite[175]
automountServiceAccountToken是一个非常重要的身份暴露控制开关::cite[167]- 最佳实践是:每个工作负载使用独立 ServiceAccount、只授予最小权限、按需投射 token、尽量缩短凭据生命周期
掌握了 ServiceAccount,你就真正进入了 Kubernetes 安全体系的中层:身份怎么发、权限怎么收、边界怎么控。下一章我们继续看 Pod 安全标准,理解“就算有身份,也不能随便用危险能力”。
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!