RBAC 基于角色的访问控制
本教程共 65 篇 · 第 44 篇 · 更新于 2026-08-14 · 约 22 分钟阅读
本节目标:掌握 Role、ClusterRole、RoleBinding、ClusterRoleBinding 这四个对象,学会写出精确到某个资源某个动词的权限规则。
上一章说到鉴权有六种模式,RBAC 是现在唯一值得你花时间学的那一种。它用 rbac.authorization.k8s.io API 组驱动决策,规则本身就是 Kubernetes 对象,可以 kubectl apply 动态改,不用重启 API 服务器。
RBAC 的思路很朴素:不直接给人授权,而是先定义角色,再把角色绑给人。 人事变动只需要改绑定,不用重写规则。
44-1 四个对象,两个维度
RBAC 只有四种对象,先把它们的关系记牢:
| 命名空间级 | 集群级 | |
|---|---|---|
| 定义权限 | Role | ClusterRole |
| 授予权限 | RoleBinding | ClusterRoleBinding |
左边一列是”角色”,说明能做什么。右边一列是”绑定”,说明谁拿到了这些能力。
上下两行的区别是作用域:Role 必须属于某个命名空间,ClusterRole 是集群作用域的资源。
Kubernetes 之所以拆成两套名字,是因为一个对象要么是命名空间级的,要么是集群级的,不可能两者兼具。
NoteRBAC 的权限是纯累加的,不存在”拒绝”规则。没有明确授予的动作,默认就是不允许。所以你永远不需要写”禁止某人删除 Pod”,只需要不给他
delete权限。
44-2 Role:命名空间内的权限清单
看一个最经典的例子——只读 Pod:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" 代表核心 API 组
resources: ["pods"]
verbs: ["get", "watch", "list"]
三个字段撑起了整条规则:
apiGroups——资源属于哪个 API 组。空字符串""是核心组(Pod、Service、ConfigMap 等都在这里)。Deployment在apps,Ingress在networking.k8s.io。resources——资源类型,用 URL 里出现的那个复数小写名字,比如pods而不是Pod。verbs——允许的动作,就是上一章讲的那套请求动词。
rules 是个列表,可以写多条。每条独立生效,取并集。
不确定某个资源属于哪个 API 组?查一下:
# 列出全部资源类型及其 API 组、是否命名空间级
kubectl api-resources
# 只看某一个
kubectl api-resources | grep deployments
这个命令我几乎每次写 RBAC 都要敲一遍,比翻文档快。
子资源怎么写
有些 API 有子资源,比如取 Pod 日志的请求路径是:
GET /api/v1/namespaces/{namespace}/pods/{name}/log
这里 pods 是资源,log 是子资源。RBAC 里用斜杠分隔:
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
常见子资源还有 pods/exec(进容器执行命令)、pods/portforward、deployments/scale、pods/status。
Warning
pods/exec权限威力极大。给了它,等于让人能钻进任何 Pod 里,拿到容器内的一切——包括挂载进去的 Secret。给权限时想清楚。
精确到某个对象名
用 resourceNames 可以把权限锁到具体某个实例上:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: configmap-updater
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-configmap"]
verbs: ["update", "get"]
这条规则只能改 my-configmap 这一个,别的碰不了。
Note
resourceNames对create和deletecollection无效。create是因为鉴权时对象名字还不确定。另外,如果你用resourceNames限制list或watch,客户端必须在请求里带上匹配的字段选择器才能通过,比如kubectl get configmaps --field-selector=metadata.name=my-configmap。这个细节挺容易让人困惑——明明给了权限,kubectl get configmaps却还是 403。
44-3 ClusterRole:三种用法
ClusterRole 能干 Role 能干的一切,此外还能授权:
- 集群作用域的资源——
Node、PersistentVolume、Namespace、ClusterRole自己。 - 非资源端点——
/healthz、/metrics、/version这类路径。 - 跨全部命名空间的命名空间级资源——比如让某人能跑
kubectl get pods --all-namespaces。
一个 Secret 读取角色:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# ClusterRole 不写 namespace
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
非资源端点这样写:
rules:
- nonResourceURLs: ["/metrics", "/healthz"]
verbs: ["get"]
注意 nonResourceURLs 和 resources 不能出现在同一条规则里,得分开写。
44-4 绑定:把角色发给主体
绑定对象有两个关键字段:subjects(发给谁)和 roleRef(发什么)。
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 # 只能是 Role 或 ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
subjects 支持三种 kind:
subjects:
# 人类用户
- kind: User
name: "alice@example.com"
apiGroup: rbac.authorization.k8s.io
# 用户组
- kind: Group
name: "frontend-admins"
apiGroup: rbac.authorization.k8s.io
# 服务账号(注意这个不写 apiGroup,要写 namespace)
- kind: ServiceAccount
name: default
namespace: kube-system
User 和 Group 只是字符串,Kubernetes 不存储它们——上一章说过。ServiceAccount 是真实对象,所以要指定它所在的命名空间。
Tip前缀
system:是 Kubernetes 系统保留的,别给自己的用户或组起这种名字。ServiceAccount 在认证层的用户名前缀是system:serviceaccount:(单数),所属组的前缀是system:serviceaccounts:(复数)。差一个字母,含义完全不同,我第一次配的时候就写错过。
一个容易忽略的组合技
RoleBinding 可以引用 ClusterRole。这时候 ClusterRole 里的权限只在这个 RoleBinding 所在的命名空间生效。
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-secrets
namespace: development # 只在 development 生效
subjects:
- kind: User
name: dave
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole # 引用集群角色
name: secret-reader
apiGroup: rbac.authorization.k8s.io
上面这个 secret-reader 是集群角色,但 dave 只能读 development 命名空间的 Secret。
这个技巧特别实用:定义一次通用角色,在不同命名空间反复绑定。不然你得在每个命名空间复制一份一模一样的 Role。
要真正跨全集群生效,就换成 ClusterRoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
Warning
roleRef创建后不可修改。想换角色只能删掉绑定重建。这个限制是故意的:换角色本质上是一次全新的授权,强制重建能确保你重新确认过主体列表里每个人都该拿到新权限。kubectl auth reconcile命令可以自动处理这种删除重建。
44-5 通配符:能用但要克制
apiGroups、resources、verbs 都支持 *:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: example.com-superuser # 此角色仅作示范,请勿使用
rules:
- apiGroups: ["example.com"]
resources: ["*"]
verbs: ["*"]
nonResourceURLs 里 * 可以做后缀通配。resourceNames 为空集表示不限制。
问题在哪?通配符会自动吞掉未来新增的东西。 集群升级后多了个新资源类型、新子资源、新自定义动词,你的通配符规则立刻把它们也授权了出去。你不知道,也没审批过。
按最小特权原则,把 resources 和 verbs 一个个列清楚。麻烦一点,但麻烦的是你,不是攻击者。
44-6 内置角色:先别急着自己写
Kubernetes 自带一批 ClusterRole。带 system: 前缀的是给组件用的,不带前缀的是给人用的:
| 角色 | 默认绑定 | 说明 |
|---|---|---|
cluster-admin | system:masters 组 | 超级用户,任何资源任何操作 |
admin | 无 | 用 RoleBinding 授予,命名空间内近乎全权,能建 Role/RoleBinding |
edit | 无 | 命名空间内大多数对象读写,但不能看改 Role/RoleBinding |
view | 无 | 命名空间内只读,看不到 Secret |
日常配权限,view、edit 两个覆盖了绝大多数场景。一行命令就搞定:
# 让 zhangsan 在 dev 命名空间有读写权限
kubectl create rolebinding zhangsan-edit \
--clusterrole=edit \
--user=zhangsan \
--namespace=dev
# 让某个 ServiceAccount 有全集群只读
kubectl create clusterrolebinding monitor-view \
--clusterrole=view \
--serviceaccount=monitoring:prometheus
几个必须知道的细节:
view故意排除了 Secret。因为读 Secret 等于读走凭据。edit能访问 Secret,还能以命名空间里任意 ServiceAccount 的身份运行 Pod。也就是说,拿到edit就能拿到该命名空间里任何 ServiceAccount 的 API 权限。这是一条真实的提权路径,别把edit当成”安全的中等权限”。admin不能写 ResourceQuota,也不能改命名空间本身。- v1.22 及以后创建的集群里,
admin和edit都不能写EndpointSlices。
Warning
cluster-admin配ClusterRoleBinding就是完全的集群 root。它甚至能改 RBAC 规则本身,也就是能给自己和别人再授任何权。生产环境里应该只有极少数账号拥有它。
44-7 聚合 ClusterRole:给内置角色打补丁
装了 CRD(自定义资源)之后,你会发现 edit 角色管不了它——内置角色不认识你的新资源类型。
不用去改 edit 本身(改了升级会被覆盖)。正确做法是新建一个带特定标签的 ClusterRole,控制平面会自动把它聚合进去:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: my-crd-edit
labels:
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-admin: "true"
rules:
- apiGroups: ["example.com"]
resources: ["mycrds"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
可用的标签有三个:aggregate-to-admin、aggregate-to-edit、aggregate-to-view。
聚合的原理是:目标 ClusterRole 上有个 aggregationRule 字段定义标签选择器,控制器持续监视匹配的 ClusterRole,把它们的规则合并进目标的 rules。
Warning聚合角色的
rules字段由控制平面接管,你手写的值会被覆盖。写聚合ClusterRole的清单时干脆别写rules字段——哪怕设为空列表也不行。服务端应用(Server-Side Apply)会认为你声明了对该字段的所有权,之后每次 apply 要么因字段管理器冲突失败,要么反复清空聚合规则再被控制平面填回来,来回打架。GitOps 环境里这个坑尤其常见。
44-8 防提权:为什么你建不了那个角色
RBAC 有个保护机制,新手常在这里卡住:你不能凭空创建出超过自己权限的角色。
具体规则:
- 创建/更新角色,必须满足其一——你已经拥有该角色里包含的所有权限(作用域相同),或者你被显式授予了
roles/clusterroles上的escalate动词。 - 创建/更新绑定,必须满足其一——你已经拥有被引用角色的全部权限,或者你被授予了该角色上的
bind动词。
举例:user-1 没有集群范围列举 Secret 的权限,那他就建不出一个包含该权限的 ClusterRole,API 请求会直接 403。
这个设计挡住了一条经典攻击路径:拿到一个能创建 RBAC 对象的低权限账号,然后给自己写一个 cluster-admin 绑定。
如果确实要让某人代为授权(比如命名空间自治),给他 bind 动词,并用 resourceNames 限定他只能绑哪几个角色:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings"]
verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
resourceNames: ["admin", "edit", "view"] # 只允许绑这三个
verbs: ["bind"]
这样他能在自己命名空间发 admin/edit/view,但发不出 cluster-admin。
44-9 排查与常用命令
快速建对象:
# 建 Role
kubectl create role pod-reader \
--verb=get,list,watch --resource=pods -n dev
# 建 ClusterRole
kubectl create clusterrole node-reader \
--verb=get,list,watch --resource=nodes
# 加 --dry-run=client -o yaml 可以先看生成的 YAML
kubectl create role pod-reader --verb=get --resource=pods \
--dry-run=client -o yaml
排查 403 的三步法:
- 先确认到底缺哪个动词:
kubectl auth can-i list secrets -n dev \
--as=system:serviceaccount:dev:my-app
- 看这个主体现在有什么:
kubectl describe rolebinding -n dev
kubectl describe clusterrolebinding | grep -A5 my-app
- 看角色具体给了什么:
kubectl describe role pod-reader -n dev
kubectl describe clusterrole view
kubectl describe clusterrole view 输出的那张表值得你认真读一遍,能学到很多规则怎么组织。
44-10 三个高频错误
资源名写成单数或大写。 Pod、pod 都不对,必须是 pods。RBAC 用的是 URL 里的那个名字。
API 组填错。 Deployment 在 apps 组,不在核心组。写成 apiGroups: [""] 规则不会报错,但也不会生效——静默失效最难查。拿不准就 kubectl api-resources 确认。
忘了 ServiceAccount 主体不需要 apiGroup。 User 和 Group 要写 apiGroup: rbac.authorization.k8s.io,ServiceAccount 要写 namespace 而不写 apiGroup。写反了会被 API 校验挡下来。
小结
- 四个对象两个维度:
Role/ClusterRole定义权限,RoleBinding/ClusterRoleBinding授予权限。 - 权限纯累加,没有拒绝规则,默认全禁。
RoleBinding引用ClusterRole是最实用的组合——通用角色定义一次,多命名空间复用。- 优先用内置的
view/edit/admin,别一上手就自己造。但要清楚edit隐含的提权路径。 - 通配符会自动包含未来新增资源,能不用就不用。
- 防提权机制会拦住”给自己发超权角色”,需要代授权时用
bind加resourceNames。
下一章讲 ServiceAccount——工作负载自己的身份是怎么来的。