Kubernetes ServiceAccount 实战:Pod 身份、Token 与跨 Namespace 授权
目录
引言
RBAC 定义“某个身份能做什么”,但 Pod 首先要有身份。Kubernetes 使用 ServiceAccount 为 workload、controller、Job 和自动化程序提供 non-human identity。
理解 ServiceAccount 最重要的一句话是:
| |
ServiceAccount 本身没有权限。创建一个 ServiceAccount 不会自动获得 Pod、Secret 或 Deployment 的访问能力;只有 RoleBinding 或 ClusterRoleBinding 把角色授给它以后,API Server 才会允许对应请求。
本文是 Kubernetes 权限系列第 2/4 篇,重点是 Pod identity、token 与跨 namespace 授权。
ServiceAccount 到底是什么
ServiceAccount 是 Kubernetes API 中的 namespaced object:
| |
它在认证系统中的完整用户名是:
| |
上面的 ServiceAccount 对应:
| |
同时属于以下 group:
| |
这意味着 RBAC 既可以绑定单个 ServiceAccount,也可以绑定某个 namespace 或整个集群中的所有 ServiceAccount。后两种范围很大,生产环境应谨慎使用。
User、ServiceAccount 与 Pod 的关系
| 身份 | 存放位置 | 典型使用者 | 生命周期 |
|---|---|---|---|
| User / Group | 外部 IdP 或证书系统 | 人类用户 | 由外部 IAM 管理 |
| ServiceAccount | Kubernetes API,属于 namespace | Pod、controller、CI/CD | 跟随集群对象管理 |
| Pod | 通过 serviceAccountName 选择 ServiceAccount | workload process | Pod 生命周期 |
Pod spec 中的 serviceAccountName 决定它以哪个 ServiceAccount 身份调用 API:
| |
Pod 创建后不能修改 serviceAccountName。如果没有显式指定,Kubernetes 会使用当前 namespace 中名为 default 的 ServiceAccount。
default ServiceAccount 的风险
每个 namespace 创建时都会自动获得一个 default ServiceAccount。它默认通常只有 API discovery 等基础权限,但 Pod 仍可能自动挂载它的 token。
不要为了方便给 default ServiceAccount 绑定 view、edit 或自定义高权限角色。否则任何没有显式声明 serviceAccountName 的 Pod 都会继承这些权限。
更稳妥的设计是:
| |
可以在 ServiceAccount 层关闭 token 自动挂载:
| |
Pod 也可以单独设置:
| |
如果 ServiceAccount 和 Pod 都设置了该字段,Pod spec 的值优先。一个 workload 不访问 Kubernetes API 时,即使需要 ServiceAccount 名称用于其他 identity integration,也应关闭 API token automount。
ServiceAccount Token 是怎样工作的
现代 Kubernetes 默认通过 TokenRequest API 给 Pod 提供短期 bound token。kubelet 把它作为 projected volume 挂载,并在过期前自动轮换。
默认挂载目录是:
| |
Token 通常包含:
| Claim | 作用 |
|---|---|
sub | ServiceAccount 身份 |
aud | 允许接收该 token 的 audience |
exp | 过期时间 |
| bound object reference | 将 token 生命周期绑定到 Pod、Secret 或其他对象 |
与旧式永久 ServiceAccount token Secret 相比,projected bound token 具有短生命周期、audience 限制和自动轮换能力。Kubernetes v1.24 起不再为每个 ServiceAccount 自动创建永久 token Secret。
不要把 ServiceAccount token 当成普通配置:
- 不写入 Git、ConfigMap、镜像或 CI log。
- 不复制 Pod 内 token 给另一个长期运行的服务。
- 不创建永久 Secret token 解决临时集成问题。
- 接收 token 的服务必须验证 signature、expiry、audience 与 bound object。
实战一:给 Pod 最小的本 namespace 读取权限
场景与目标
applications namespace 中有一个 inventory agent,它需要观察 Pod 和 EndpointSlice,但不能:
- 读取 Secret。
- 创建或删除 Pod。
- 修改 Deployment。
- 访问其他 namespace。
先准备实验 namespace:
| |
第一步:创建 ServiceAccount 和 Role
保存为 inventory-reader.yaml:
| |
先做 server-side dry-run,再应用:
| |
第二步:直接验证 ServiceAccount 的有效权限
不需要进入 Pod,也不需要手工读取 token:
| |
预期结果:
| |
执行测试的管理员需要 impersonate ServiceAccount 的权限。应用自身可以通过 Kubernetes client library 的 in-cluster configuration 使用自动挂载的 token 和 CA,不应在代码中硬编码 token 路径之外的凭据。
实战二:跨 namespace 授权
场景与错误做法
ops namespace 中的 release-auditor 需要读取 payments namespace 的 Deployment。
常见错误是创建 ClusterRoleBinding:
| |
需求只是访问一个目标 namespace,正确做法是在目标 namespace 创建 Role 和 RoleBinding。RoleBinding 的位置决定权限作用范围,subject 可以来自另一个 namespace。
准备两个 namespace:
| |
创建跨 namespace binding
保存为 cross-namespace-auditor.yaml:
| |
应用并验证:
| |
预期只有 payments/list deployments 返回 yes。如果同一个 ServiceAccount 还要访问 orders,就在 orders 再创建一份 RoleBinding,而不是扩大为 ClusterRoleBinding。
实战三:显式 projected token 与自定义 audience
有时 workload 不需要 Kubernetes API token,却需要向内部服务证明自己的 Kubernetes identity。可以关闭默认 automount,只投射一枚面向指定 audience 的短期 token:
| |
这枚 token 的 audience 不是 Kubernetes API Server,而是 https://internal-api.example.com。内部服务应通过 TokenReview API 或 Kubernetes OIDC discovery 验证它,并显式检查 audience;使用 TokenReview 时,验证服务自身还需要调用 tokenreviews.authentication.k8s.io 的最小 RBAC 权限。kubelet 会在过期前更新 projected volume,应用必须重新读取文件,不能只在启动时缓存 token。
临时调试可以请求短期 token:
| |
该命令会把 token 输出到终端,只适合受控调试。不要把输出复制到脚本、工单、聊天工具或 CI log。
401 与 403 应该怎样排查
调用 API 失败时,先区分 Authentication 与 Authorization:
| 返回结果 | 含义 | 常见原因 |
|---|---|---|
401 Unauthorized | API Server 没有接受身份 | token 过期、audience 不匹配、signature 无效 |
403 Forbidden | 身份已认证,但权限不足 | Role/Binding 缺失、namespace 错误、verb 或 subresource 不匹配 |
排查顺序:
| |
不要通过解码或打印真实 token 来排查 RBAC。403 已经说明 Authentication 成功,继续查看 token 内容通常没有价值。
ServiceAccount 的高风险权限
以下授权应作为 privilege escalation risk 审核:
| 权限 | 风险 |
|---|---|
get/list/watch secrets | 直接读取 Secret data |
create serviceaccounts/token | 为其他 ServiceAccount 签发 token |
| 创建 Pod/Deployment/Job | 可以选择 namespace 内其他 ServiceAccount |
impersonate serviceaccounts | 以其他 workload identity 发起请求 |
给 system:serviceaccounts 绑定角色 | 给集群内所有 ServiceAccount 授权 |
给 system:serviceaccounts:<ns> 绑定角色 | 给 namespace 内所有 Pod identity 授权 |
即使某个 ServiceAccount 自身权限很小,只要它能创建 workload,就可能让新 Pod 使用更强的 ServiceAccount。这正是下一篇要解决的问题。
常见错误
给 default ServiceAccount 授权
任何未显式指定身份的 Pod 都会继承权限,授权范围难以追踪。
用 ClusterRoleBinding 实现单 namespace 访问
跨 namespace 不等于 cluster-wide。在目标 namespace 创建 RoleBinding 即可。
把长期 token 放到 Secret
永久 token 不自动过期,泄露后的有效窗口不可控。优先 TokenRequest 和 projected bound token。
认为 automount false 等于 ServiceAccount 无效
它只是不把 Kubernetes API credential 自动放进 Pod。Pod 仍然声明了 ServiceAccount identity,可用于 admission rule、云 workload identity 或显式 projected token。
为每个 Pod 创建 ServiceAccount
ServiceAccount 应按 workload security boundary 建模,而不是机械地一 Pod 一个。Deployment 的所有 replicas 通常共享同一 ServiceAccount,但不同组件或不同权限级别不能共享。
总结
ServiceAccount 是 workload identity,不是权限集合。Pod 通过 serviceAccountName 选择身份,API Server 使用短期 projected token 完成认证,RBAC 再决定这个身份能访问哪些资源。
生产设计应做到:一个组件一个清晰身份、默认关闭不需要的 token、使用 bound token、在目标 namespace 做跨 namespace RoleBinding,并通过 kubectl auth can-i --as system:serviceaccount:... 验证最终权限。
下一篇将处理 ServiceAccount 带来的关键安全边界:为什么一个只有 create deployments 的用户,仍可能借用 namespace 内的高权限身份,以及如何用 Pod Security 与 Admission Policy 阻止它。
系列文章
- Kubernetes RBAC 基础与实战:四个对象讲清最小权限
- Kubernetes ServiceAccount 实战:Pod 身份、Token 与跨 Namespace 授权(本文)
- 为什么 Kubernetes 只有 RBAC 不够:从隐式提权到 Admission Policy
- Kubernetes 权限体系进阶:完整 Access Control 架构
参考资料
- Service Accounts
- Configure Service Accounts for Pods
- Managing Service Accounts
- Using RBAC Authorization
- TokenRequest API
本文基于 Kubernetes v1.36 编写。