权限为什么会悄悄变大
一句话总结
RBAC 默认阻止权限提升,但 escalate、bind、impersonate 三个动词,以及自动挂载的 ServiceAccount 令牌,会绕过这道防线。强化就是封住这四个缺口。
为什么需要它
RBAC 有内置保护:用户要创建 Role 或 RoleBinding,就必须已经拥有该 Role 包含的全部权限。这条规则阻断了“低权限用户给自己授予高权限”的路径。问题在于,存在明确突破这种保护的动词。
| 动词 | 突破什么限制 | 实际应对措施 |
|---|---|---|
escalate |
可向 Role 添加自己并不拥有的权限 | 仅平台管理员拥有,必须记录审计日志 |
bind |
可绑定包含自己并不拥有权限的 Role | 限制创建 ClusterRoleBinding 的权限本身 |
impersonate |
可冒充其他用户、组或 SA | 用 resourceNames 固定目标并进行审计 |
此外还有 system:masters 组。该组成员会绕过全部 RBAC 检查,即使删除 RoleBinding 或 ClusterRoleBinding 也无法撤销权限。一个 kubeconfig 泄露就会彻底失守。它只能用于 break-glass 流程,平时不应有任何人属于该组。
工作原理
第二个方面是 ServiceAccount 令牌。Pod 默认使用自己命名空间中的 default ServiceAccount 运行,其令牌会自动挂载到 /var/run/secrets/kubernetes.io/serviceaccount/token。即使应用不使用 Kubernetes API,也会挂载。也就是说,一个 Pod 被攻破后,攻击者立刻就能取得一份集群 API 凭据。
可以在两个位置关闭:ServiceAccount 对象的 automountServiceAccountToken: false,以及 Pod spec 中的同名字段。Pod 级设置会覆盖 ServiceAccount 级设置,因此实践中会同时设置两者。关闭 default SA 后,新工作负载因疏忽而获得令牌的路径就会消失;只有确实需要的工作负载才创建专用 SA 并明确启用。
令牌形式也已改变。过去,每个 SA 都会自动创建永不过期的令牌 Secret,任何能读取该 Secret 的人都会获得永久有效的凭据。现在通过 TokenRequest API 签发带生存期与目标(audience)的令牌,并以 projected volume 的 serviceAccountToken 来源注入 Pod。仍可手动创建 kubernetes.io/service-account-token Secret,但它不会过期,只应在确有需要时使用。
第三个方面是验证。RBAC 很难通过肉眼阅读来验证。在 kubectl auth can-i 上添加 --as=system:serviceaccount:<네임스페이스>:<이름>,API 服务器会直接回答该主体实际能做什么。这是确认结果而不是设计的唯一方法。
实际现场中的情况
采用策略引擎的组织经常出现一种 P0 故障:OPA Gatekeeper 的 ValidatingWebhook 以 failurePolicy: Fail 注册,而 Gatekeeper Pod 全部宕机后,API 服务器因无法收到 Webhook 响应,拒绝创建或修改所有目标资源。为了保护策略,整个集群部署反而停止。紧急措施是删除 Webhook 配置对象;根本措施则是在生产环境使用 failurePolicy: Ignore,同时必须对 Gatekeeper 宕机设置告警。限制权限的装置本身也可能造成可用性事故,这是强化设计的现实约束。
作者把家庭实验室控制平面从 1 台扩展到 3 台时,凭据生存期也是问题。kubeadm init phase upload-certs 会加密 CA 密钥包并以集群内 Secret 上传,certificate-key 是解密该密文的对称密钥。这个 Secret 会在 2 小时后自动删除,以尽量缩短 CA 密钥即使以加密形式存在于集群中的时间。同样原则也适用于 SA 令牌:与隐藏凭据相比,缩短生存期几乎总是更有效。
下一项实验要做什么
在集群中直接找出带通配符的 ClusterRole 并列出,另行提取包含危险动词的角色。然后创建收窄的 Role,绑定到 ServiceAccount,并用 auth can-i --as= 确认结果。最后,在 Pod 和 default ServiceAccount 两端都关闭令牌自动挂载。