Technical documentation
Pod 有 Token 却删不掉资源?K8s 认证和授权的根本区别
Pod 有 Token 却删不掉资源?K8s 认证和授权的根本区别
在 Kubernetes 中,拥有有效的 ServiceAccount 凭据仅表示通过身份认证,实际能执行的操作完全取决于 RBAC 授权策略,而非凭据本身。
为什么在 Kubernetes 里拿到凭证不等于能访问所有资源
Kubernetes 将身份验证与资源授权物理隔离,导致 Pod 即使持有有效凭证被系统识别,仍因缺乏特定权限绑定而无法访问具体资源。
很多运维人员都遇到过这种尴尬场景:明明 Pod 里挂载了有效的 ServiceAccount Token,调用 API Server 也返回了 200 OK,可一旦尝试删除某个资源或读取 Secret,立刻被拒绝。这种“能进门却进不了房间”的现象,根源在于 Kubernetes 将身份验证与资源授权做了物理隔离[1]。
从“你是谁”到“你能做什么”的安全分水岭
认证只是第一道关卡。当 Pod 内的进程带着凭据向 API Server 发起请求时,系统只负责核对令牌是否有效。只要令牌没过期、签名对得上,这个请求就被标记为 system:authenticated[2]。这仅仅回答了“你是谁”,确认你拥有合法的身份,并不代表你有权操作任何数据。
真正的权限控制发生在第二道关卡——授权。Kubernetes 此时会调取 RBAC(基于角色的访问控制)策略,检查该主体是否被明确允许对特定资源执行特定动作[1]。如果缺少对应的 RoleBinding 或 ClusterRoleBinding,哪怕凭据完美无缺,系统也会直接拦截写操作或敏感数据的读取。
这里有一个常被外行误解的环节:很多人以为 ServiceAccount Token 就像一把万能钥匙,只要拿到了就能开所有的门。事实恰恰相反,Kubernetes 的认证机制并不传递“能力”,它只传递“合法性”。当你拿到 Token 时,你只是向系统证明“我是合法的进程”,但系统随后会立即问:“既然你是合法的,那你具体能干什么?”如果 RBAC 策略里没有把你和“删除 Pod”或“读取 Secret”这两个动作关联起来,Token 再完美也无法触发这些操作。这种设计意味着:身份层的作用是把“无主体请求”挡在门外,而授权层才决定了事故的爆炸半径[2]。若审计时只盯着 Token 的有效性,却忽略了 RBAC 绑定配置,就会把“可认证”误判为“最小权限”。实际上,取得凭据只代表通过了身份核验,离获得任意 API 权限还有巨大的鸿沟。
| 阶段 | 核心问题 | 判定依据 | 结果状态 |
|---|---|---|---|
| 认证 | 你是谁? | ServiceAccount Token 有效性 | system:authenticated |
| 授权 | 你能做什么? | RBAC Role/RoleBinding 绑定 | Allow / Deny |
| 审计误区 | 以为有身份就有权限 | 仅检查 Token 存在性 | 错误的高估风险 |
只有当这两层逻辑同时通过,Pod 才能真正执行操作。否则,那个看似强大的凭证,不过是一张没有具体房间钥匙的通用门禁卡。
Kubernetes 如何区分人与机器的身份模型
Kubernetes 采用双轨模型区分身份,普通用户需手动维护,而机器账号则通过 API 动态生成随应用部署边界自动产生的临时实体。
你在集群里看到的普通用户账号,根本没法通过 API 直接创建。能由 API 动态生成的,只有 ServiceAccount。这种设计把“人”和“机器”切成了两条平行的线。前者是管理员手动维护的静态实体,后者是随应用部署边界自动诞生的临时身份。[1][2]
ServiceAccount 作为机器主体的独特性
ServiceAccount 的核心特征在于它的“命名空间作用域”。它不像人类账户那样全局存在,而是被死死锁定在特定的 Namespace 内。[1][2] 这意味着一个在 dev 命名空间创建的 ServiceAccount,天然无法触碰 prod 环境的数据。这种隔离不是靠配置锁死的,而是对象本身的属性。
| 对比维度 | 普通用户账户 (User) | ServiceAccount (机器主体) |
|---|---|---|
| 创建方式 | 外部管理,不可由 K8s API 生成 | 完全由 API 动态创建与管理 |
| 作用域范围 | 通常跨集群或全局有效 | 严格限定在单个命名空间内 |
| 生命周期 | 长期稳定,依赖人工轮换 | 随 Pod 部署/销毁动态流转 |
| 绑定对象 | 关联具体的人或运维流程 | 直接挂载给 Pod 进程使用 |
| 主要用途 | 人类交互式操作 | 应用进程间认证与通信 |
这种架构差异解决了工程上的大麻烦:防止将人员账户、共享静态密钥与进程访问混为一谈。[1][2] 如果把所有东西都塞进同一个模型,一旦某个开发人员的 Token 泄露,或者某个旧脚本还在用硬编码的密钥,整个集群的爆炸半径就会瞬间失控。
Pod 启动时,会自动把关联的 ServiceAccount 凭据挂载到容器内部。进程拿到这个凭据去请求 API Server,系统会先确认它是 system:authenticated(已认证),但这只是第一步。[1][2] 接下来的授权环节,RBAC 才会根据该 ServiceAccount 的具体角色定义,决定它能读写哪些资源。
机器身份不强调“你是谁”,而强调“你在哪个上下文”。人类账户看重个人属性,ServiceAccount 看重的是部署边界。这种两段式结构——先降低无主体请求的风险,再通过授权层控制爆炸半径——才是工作负载安全治理的真正边界。[1][2]
RBAC 绑定策略如何决定 Pod 的实际权限范围
RBAC 绑定策略是连接已认证身份与操作权限的唯一桥梁,它决定同一个 ServiceAccount 在不同命名空间下能执行的具体动作范围。
同一个 ServiceAccount,换个命名空间或换一套 RoleBinding,它手里的刀就能从“切菜”变成“拆家”。[1][2] 这并非因为凭据变了,而是背后的授权规则变了。在 Kubernetes 里,拿到凭证只是拿到了入场券,RBAC(基于角色的访问控制)才是那张真正决定你能去哪个房间、能拿什么物品的地图。[1][2] 它是连接“已认证身份”与“具体操作权限”的唯一桥梁。[1][2]
Pod 里的进程拿着 ServiceAccount 的 Token 向 API Server 发起请求,这一步叫认证。认证通过后,系统确认你属于 system:authenticated 组,但这并不代表你可以为所欲为。[1][2] 接下来的每一步动作——读配置、删节点、扩容副本——都要经过 RBAC 的二次拦截。[1][2] 如果这张地图上没画你的路线,哪怕你手里有最完美的钥匙,门也不会开。[1][2] 这种两段式结构是机器身份治理的共同边界:身份层负责降低“无主体请求”的风险,而授权层才真正决定了事故的爆炸半径。[1][2]
很多审计工作容易在这里栽跟头。审核员看到 Pod 持有有效的 ServiceAccount 凭据,就默认该账号拥有集群内的所有能力。[1][2] 这是一种典型的“可认证即全权”陷阱。[1][2] 仅仅确认身份有效,却不去检查它绑定了什么 Role 或 ClusterRole,就会把“能进门”误判为“能进保险柜”。[1][2] 正确的做法必须把 ServiceAccount 本身和它的绑定关系拆开看:前者证明你是谁,后者定义你能做什么。[1][2]
为了看清这种差异,我们可以对比不同绑定场景下的实际权限表现:
| 场景特征 | ServiceAccount 状态 | RoleBinding 指向 | 实际权限结果 |
|---|---|---|---|
| 默认配置 | 已创建且有效 | 无绑定或仅基础只读 | 只能读取少量资源,无法修改任何对象 |
| 跨命名空间部署 | 同一账号复用 | 绑定至特定 Namespace 的 Role | 仅在该命名空间内拥有定义的操作权 |
| 错误的高权绑定 | 同一账号复用 | 绑定至 ClusterRole (admin) | 获得整个集群的读写权限,风险极大 |
| 审计盲区 | 凭据有效 | 未检查绑定关系 | 误判为拥有全部权限,掩盖真实风险 |
这种差异揭示了为什么不能只看凭证。[1][2] 即使两个 Pod 使用完全相同的 ServiceAccount Token,只要它们所在的命名空间不同,或者挂载的 RoleBinding 不同,它们的实际能力就是天壤之别。[1][2] 这种设计不是偶然的特性,而是 Kubernetes 认证后进入授权流程的必然结果。[1][2] 如果你只盯着 Token 的有效性,却忽略了 RBAC 的精细度,就等于在评估资产风险时,只看了锁有没有坏,却没看钥匙孔到底通向哪里。[1][2]
最终,整个安全链条是这样运转的:ServiceAccount 提供身份锚点,API Server 验证其合法性,RBAC 引擎根据当前的绑定策略计算具体的 Action-Resource 对。[1][2] 只有当这三者严丝合缝地配合时,才能界定出真正的最小权限。[1][2] 忽略其中任何一环,尤其是 RBAC 绑定策略,都会让“有凭证不等于能访问所有资源”这句原则失效,留下巨大的安全缺口。[1][2]
实操建议:如何快速定位“隐形高权”账号 不要依赖肉眼排查 YAML 文件,建议编写一个简单的自动化脚本或利用
kubectl auth can-i命令进行批量扫描。具体步骤如下:首先列出集群中所有非默认的 ServiceAccount;其次,对每个账号执行kubectl auth can-i --list --as=system:serviceaccount:<namespace>:<sa-name>命令;最后,将输出结果与业务需求文档比对。如果发现某个仅用于“日志收集”的 Pod 拥有delete权限,或者某个测试环境的账号绑定了cluster-admin,这就是典型的“隐形高权”,应立即切断其不必要的绑定。这种基于“能力清单”而非“凭据存在性”的审计方法,能精准识别出那些持有有效 Token 却越权的隐患。
常见问题解答 (FAQ)
Q: 既然 ServiceAccount 是机器账号,为什么还需要像人类一样进行复杂的授权配置? A: 正是因为它是机器账号,所以更容易被自动化脚本批量利用。如果不做精细的 RBAC 限制,一旦某个 Pod 被攻破,攻击者就能利用其高权限凭证横向移动,造成比人类账号泄露更严重的后果。
Q: 如何快速发现那些“有凭证但权限过大”的 ServiceAccount?
A: 不要只看 Token 是否存在。应该定期扫描集群中的 RoleBinding 和 ClusterRoleBinding,找出那些绑定了 admin 或 cluster-admin 等宽泛角色的 ServiceAccount,并对照业务需求进行裁剪。
Q: ServiceAccount 和普通 User 账号在安全性上最大的区别是什么? A: 最大的区别在于作用域和生命周期。User 通常是全局的、长期的;而 ServiceAccount 严格限定在命名空间内,且随 Pod 的生命周期动态产生和销毁,这大大减少了密钥泄露后的暴露窗口。