深色模式
RBAC 权限模型实战
摘要:本文面向需要为工作负载和工程师设计 Kubernetes 访问控制的 SRE / 平台工程师。覆盖 RBAC 四种对象、最小权限落地、聚合 ClusterRole 的模式,以及最容易被忽视的提权路径(escalate / bind / impersonate)。适用版本:Kubernetes v1.28+,apiVersion
rbac.authorization.k8s.io/v1(GA,未弃用)。
适用版本与前提
- Kubernetes:v1.28+(RBAC API 自 v1.8 起 GA)
- 需要
kubectl且具有创建 Role/ClusterRole 的权限(或escalate) - 理解 User / Group / ServiceAccount 身份概念(见 ServiceAccount 与 Token 管理)
核心概念:四种对象
RBAC 由四种对象组成,权限纯累加(不存在 deny 规则):
| 对象 | 作用域 | 用途 |
|---|---|---|
Role | 命名空间内 | 授予某命名空间内资源的权限 |
ClusterRole | 集群级(非命名空间) | 授予集群级资源(Node/PV)、非资源 URL(/healthz),或跨命名空间的命名空间资源 |
RoleBinding | 命名空间内 | 把 Role/ClusterRole 绑定到本命名空间的 subject |
ClusterRoleBinding | 集群级 | 把 ClusterRole 绑定到全集群 subject |
一个常见误区:RoleBinding 也能引用 ClusterRole——此时权限只在该 RoleBinding 所在命名空间生效。这在"一份通用 ClusterRole 复用到多个命名空间"时非常有用。
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane # 大小写敏感
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
roleRef 不可变
创建绑定后无法修改其 roleRef。若要改绑定的角色,必须删除并重建。这是刻意的:允许某人 update 绑定的 subjects,但不允许其偷偷改绑定的角色。可用 kubectl auth reconcile 自动处理删除重建。
最小权限落地
1. 避免通配符
官方文档明确警告:resources: ["*"] 和 verbs: ["*"] 会在新增资源类型、子资源、自定义 verb 时自动授予新权限。例如下面的 ClusterRole 等价于 cluster-admin 的一部分,应禁止:
yaml
# 危险示例:不要用于生产
rules:
- apiGroups: ["example.com"]
resources: ["*"]
verbs: ["*"]1
2
3
4
5
2
3
4
5
2. 用 resourceNames 收敛敏感动词
不能按资源名限制 create(创建时名字未知),但可限制 update / delete / get 到具体名字。例如只允许修改某个 ConfigMap:
yaml
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["update", "list"]
resourceNames: ["seccomp-high"]1
2
3
4
5
2
3
4
5
3. 不要用 default ServiceAccount
每个命名空间自带 default ServiceAccount,未显式指定的 Pod 会自动挂载它。不要给它绑定任何角色。为每个 workload 创建专用 SA,并仅在需要时绑定最小权限。
聚合 ClusterRole(Aggregated ClusterRoles)
聚合 ClusterRole 通过 aggregationRule 的 label selector,把多个匹配标签的 ClusterRole 的规则自动合并成一个。控制平面会持续重写其 rules 字段。
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.example.com/aggregate-to-monitoring: "true"
# 注意:不要写 rules 字段,控制平面会自动填充1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
贡献规则的子角色:
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-pods
labels:
rbac.example.com/aggregate-to-monitoring: "true"
rules:
- apiGroups: [""]
resources: ["pods", "pods/metrics"]
verbs: ["get", "list", "watch"]1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
聚合 ClusterRole 与 Server-Side Apply 冲突
官方文档明确:不要在聚合 ClusterRole 的 manifest 里写 rules(哪怕是空列表)。在使用 Server-Side Apply(GitOps 控制器常用)时,manifest 对 .rules 的所有权声明会与控制平面的自动填充冲突:要么 apply 失败报 field manager 冲突,要么(强制 apply 时)反复清空聚合规则又被控制平面填回,造成抖动。正确做法是完全省略 rules。[版本相关:v1.28+ 行为,已实测需验证 SSA 冲突场景]
特权升级防护(重点)
Kubernetes 在 API 层默认阻止权限提升:你无法创建/修改一个包含自己尚不具备权限的 Role 或 RoleBinding,即使关闭 RBAC authorizer 这条限制依然存在。例外是三个"提权动词"。
escalate / bind / impersonate
| 动词 | 作用 | 风险 |
|---|---|---|
escalate | 绕过"必须已拥有 Role 内全部权限"的检查,可写出任意权限的 Role | 等于授予 namespace/集群管理员 |
bind | 绕过对 RoleBinding 引用角色的权限检查,可把任意角色绑定给自己 | 可绑定到高权限 ClusterRole |
impersonate | 以 Impersonate-User 头伪装成他人 | 可伪装成特权身份 |
对 bind,可用 resourceNames 限制只能绑定到指定角色:
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-binder
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
verbs: ["bind"]
resourceNames: ["edit", "view"] # 只能绑定到 edit / view,不能绑定到 cluster-admin1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
间接提权路径
即使不授予 escalate/bind,以下路径仍可提权,需在安全设计中一并封堵:
- 创建 Pod → 挂载高权限 SA:能创建 Pod 就能
serviceAccountName: admin-sa借用其 token。需配合 PSA 与限制 SA 创建。 - pods/exec → 读取 token:有
exec权限即可cat /var/run/secrets/.../token。用resourceNames限制 exec 到特定 Pod。 - 读 Secret → 取 Legacy token:Legacy SA token 存在 Secret 中。改用 projected token 并收紧 Secret 读权限。
- hostPath 卷 → 读节点 kubelet 凭证:用 PSA
restricted禁止hostPath。
验证与审计
bash
# 检查某身份是否具备某权限
kubectl auth can-i delete pods --as=system:serviceaccount:rbac:privesc -n rbac
# 找出含 escalate/bind/impersonate 的角色(需 yq)
kubectl get clusterrole -A -o yaml | yq '.items[] |
select(.rules[].verbs[] | test("escalate|bind|impersonate")) | .metadata.name'
# 找出被绑定到 cluster-admin 的 ServiceAccount
kubectl get clusterrolebindings -o json | jq -r '.items[]
| select(.roleRef.name=="cluster-admin")
| .subjects[] | select(.kind=="ServiceAccount") | .namespace + "/" + .name'1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
回滚与清理
生产变更先评估
删除/收紧 RoleBinding 可能立即中断依赖该权限的 workload 或 CI。变更前 kubectl auth can-i 确认影响面,并在非高峰灰度。kubectl auth reconcile -f old.yaml --remove-extra 可回滚到旧清单。
常见问题
- Q:为什么通配 verbs 仍拿不到新子资源? 因为
*在授权时展开为"当前已知"的动词,新增子资源/verb 后需要重新评估,通常下次请求即生效,但依赖缓存时可能有短暂延迟。 - Q:ClusterRoleBinding 能引用 Role 吗? 不能。
ClusterRoleBinding只能引用ClusterRole;跨命名空间复用某命名空间权限请用RoleBinding引用ClusterRole。
参考资料
- Kubernetes 官方文档 - Using RBAC Authorization,访问日期:2026-10-08。
- Kubernetes 官方文档 - Privilege Escalation Prevention and Bootstrapping,访问日期:2026-10-08。
- GKE 最佳实践 - RBAC,访问日期:2026-10-08。
- Kubernetes RBAC: Privilege Escalation Exploits and Mitigations,访问日期:2026-10-08。