Kubernetes 基础体系 · 第 44/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes RBAC:Role、ClusterRole、Binding、聚合和最小权限
Kubernetes RBAC(Role-Based Access Control,基于角色的访问控制)解决的是一个具体问题:已经通过认证的请求,是否允许执行某个动作。
它不负责判断“你是谁”。证书、Bearer Token、OIDC、Webhook 等认证机制先把请求映射为用户名、用户组或 ServiceAccount;随后授权组件根据请求属性和 RBAC 对象决定允许或拒绝。一个简化的请求路径是:
客户端请求
│
▼
认证 Authentication
│ 得到 user、groups、extra
▼
授权 Authorization
│ RBAC、Node、Webhook 等
▼
准入 Admission
│
▼
对象持久化或返回响应
因此,下面这个请求至少包含这些授权判断所需的信息:
主体:system:serviceaccount:team-a:reporter
动词:get
API Group:"" # 核心 API 组
资源:pods
命名空间:team-a
资源名:report-api-7d8c6
如果认证失败,请求通常会得到 401 Unauthorized;如果认证成功但没有匹配的授权规则,请求通常会得到 403 Forbidden。RBAC 只处理后者。
一、RBAC 的基本模型:主体、动作、资源和作用域
RBAC 规则可以抽象为一组允许条件。设一次请求为:
其中:
- :subject,主体,例如用户、用户组或 ServiceAccount;
- :verb,动词,例如
get、list、create; - :API Group,例如
apps,核心 API 组用空字符串表示; - :resource,例如
deployments、pods/log; - :namespace,命名空间;对集群级资源为空;
- :resourceName,具体对象名,可为空。
一条 RBAC 规则通常包含:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list"]
它表示:对 apps API 组中的 deployments 资源,允许执行 get 和 list。但这条规则仍然需要通过 Role 或 ClusterRole 被绑定到某个主体,才会生效。
对一个主体 ,RBAC 的允许集合可以理解为:
也就是说,主体通过所有匹配的 Binding 获得的权限取并集。RBAC 本身没有“显式拒绝”规则:
- 一条 Role 允许
get,另一条 Role 不能用deny get抵消它; - 没有任何匹配规则时,RBAC 返回拒绝;
- 如果 Kubernetes 配置了多个授权器,整体行为还取决于授权器链,但 RBAC 对象本身仍是“允许型”模型。
这也是 RBAC 最容易被误解的地方:Role 是权限声明,Binding 才是权限授予;仅创建 Role 不会让任何主体获得权限。
二、Role:命名空间范围内的权限声明
Role 是命名空间级对象,API 版本为:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
一个 Role 只能描述命名空间资源的权限,例如:
podsservicesconfigmapsdeploymentspods/log
它不能直接描述集群级资源,例如:
nodesnamespacespersistentvolumesclusterroles
下面的 Role 允许访问 team-a 命名空间中的 Pod 和特定 ConfigMap:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-observer
namespace: team-a
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["public-config"]
verbs: ["get"]
这里有几个容易忽略的细节。
1. 核心 API 组必须写成空字符串
Pod、Service、ConfigMap、Secret 等属于核心 API 组,不是名为 core 的 API 组:
apiGroups: [""]
Deployment 属于 apps API 组:
apiGroups: ["apps"]
写成下面这样不会匹配 Deployment:
apiGroups: [""]
resources: ["deployments"]
2. 资源名和子资源是不同的资源标识
日志、状态和扩缩容等接口通常通过子资源暴露:
resources:
- pods/log
例如,允许查看 Pod 对象本身,不等于允许查看日志:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
同样,Deployment 的扩缩容通常涉及:
resources: ["deployments/scale"]
而更新 Deployment 的 spec 是:
resources: ["deployments"]
如果只授予 deployments 的权限,不能假设它自动覆盖 deployments/scale。
3. get、list 和 watch 语义不同
get:读取单个对象;list:列出对象;watch:持续接收对象变化;create:创建对象;update:整体更新对象;patch:部分更新对象;delete:删除单个对象;deletecollection:批量删除对象。
例如,只给:
verbs: ["get"]
不能让控制器正常工作。控制器通常需要:
verbs: ["get", "list", "watch"]
因为它先要列出现有对象,再通过 watch 感知变化。将 watch 授予敏感资源还意味着主体可以持续观察资源变化,不能把它当作普通读取权限完全等价处理。
4. resourceNames 是精确对象名限制,不是通配符
resourceNames: ["public-config"]
表示只允许名为 public-config 的对象。它不是正则表达式,也不是前缀匹配。
resourceNames 对不同动作存在重要边界:
get、update、patch、delete等可以按对象名限制;create无法通过resourceNames限制,因为授权检查时规则不能可靠地把“将要创建的对象名”作为这种通用约束;deletecollection不能按单个resourceName限制;- 对
list、watch使用resourceNames时,客户端通常需要提供名称字段选择器,例如metadata.name=public-config,否则请求可能不匹配规则。
因此,下面的规则不能表达“只允许创建名为 public-config 的 ConfigMap”:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["public-config"]
verbs: ["create"]
如果必须限制创建出的对象名称,需要结合准入控制策略,而不是依赖 RBAC 的 resourceNames。
三、ClusterRole:集群级权限声明和可复用权限模板
ClusterRole 也是权限声明对象,但它不是简单的“更大版本 Role”:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
ClusterRole 可以描述三类权限:
- 集群级资源权限;
- 跨命名空间的命名空间资源权限;
- 非资源 URL 权限。
例如:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-observer
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/version", "/healthz"]
verbs: ["get"]
1. ClusterRoleBinding 使权限作用于整个集群
如果通过 ClusterRoleBinding 绑定 ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: node-observer-binding
subjects:
- kind: Group
name: platform-observers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-observer
apiGroup: rbac.authorization.k8s.io
那么 platform-observers 获得:
- 所有命名空间中的命名空间资源权限;
- 集群级资源权限;
- 规则中声明的非资源 URL 权限。
例如,如果 ClusterRole 里同时有:
resources: ["pods"]
verbs: ["get", "list"]
那么该 ClusterRoleBinding 会让主体读取所有命名空间的 Pod。
2. RoleBinding 也可以引用 ClusterRole
这是 ClusterRole 的一个关键用途。RoleBinding 位于某个命名空间,但其 roleRef 可以指向 ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-observer-in-team-a
namespace: team-a
subjects:
- kind: ServiceAccount
name: reporter
namespace: team-a
roleRef:
kind: ClusterRole
name: pod-observer-template
apiGroup: rbac.authorization.k8s.io
此时,ClusterRole 中的命名空间资源权限只在 team-a 生效,而不是所有命名空间。
因此:
| 绑定方式 | 命名空间资源的作用范围 | 集群级资源的作用 |
|---|---|---|
| Role + RoleBinding | RoleBinding 所在命名空间 | 不适用 |
| ClusterRole + RoleBinding | RoleBinding 所在命名空间 | 不能通过该 RoleBinding 获得集群级资源权限 |
| ClusterRole + ClusterRoleBinding | 所有命名空间 | 集群级资源也生效 |
这使 ClusterRole 可以作为“权限模板”复用,而不必为每个命名空间复制一份 Role。
3. Role 和 ClusterRole 不是继承关系
一个 Role 不会自动继承某个 ClusterRole;一个 ClusterRole 也不会因为名称相似而继承 Role。权限来源只能是:
- 直接绑定的 Role 或 ClusterRole;
- ClusterRole 聚合得到的规则;
- 其他授权器或特殊系统机制。
四、Binding:把权限声明授予主体
Binding 是连接“谁”和“哪些规则”的对象。Kubernetes 有两种:
RoleBinding:命名空间级绑定;ClusterRoleBinding:集群级绑定。
Binding 主要包含三部分:
subjects:
- ...
roleRef:
kind: Role 或 ClusterRole
name: ...
1. RoleBinding 的主体类型
ServiceAccount
subjects:
- kind: ServiceAccount
name: reporter
namespace: team-a
ServiceAccount 的完整身份通常是:
system:serviceaccount:team-a:reporter
不要把 ServiceAccount 名称当作集群范围唯一名称;命名空间是身份的一部分。
User
subjects:
- kind: User
name: alice@example.com
apiGroup: rbac.authorization.k8s.io
User 通常来自证书的 CN、OIDC 的用户名声明或外部认证 Webhook。Kubernetes 不会自动创建一个名为 alice@example.com 的 User 对象。
Group
subjects:
- kind: Group
name: platform-readers
apiGroup: rbac.authorization.k8s.io
Group 的名称必须与认证阶段提供的组名精确匹配。比如 OIDC 的组声明到底映射成 platform-readers 还是 groups:platform-readers,取决于认证配置和身份提供商,RBAC 本身不会替你规范化。
2. RoleBinding 的作用域由 Binding 决定
下面的 Binding 只在 team-a 生效:
metadata:
namespace: team-a
即使它引用了一个 ClusterRole:
roleRef:
kind: ClusterRole
name: pod-observer-template
也不会自动扩展到 team-b。如果要让同一个主体访问多个命名空间,通常需要在每个命名空间分别创建 RoleBinding,或者使用 ClusterRoleBinding 授予全局范围。
3. roleRef 不可修改
RoleBinding 和 ClusterRoleBinding 的 roleRef 是不可变的。要把绑定从 reader 改成 writer,不能直接修改 roleRef.name,而应删除并重新创建 Binding:
kubectl delete rolebinding pod-observer-in-team-a -n team-a
kubectl apply -f new-binding.yaml
这是为了避免通过修改现有绑定而绕过对“绑定新权限”的审计和控制。
Binding 的 subjects 可以修改,但修改主体本身就是高风险操作:能修改某个高权限 Binding 的主体,通常等价于能把该权限转交给其他身份。
五、一个可运行的最小权限示例
下面创建一个报告程序使用的 ServiceAccount。它只能在 team-a 中:
- 读取 Pod;
- 观察 Pod 变化;
- 读取名为
public-config的 ConfigMap; - 不能读取 Secret;
- 不能读取其他命名空间;
- 不能创建、删除或修改资源。
1. 创建命名空间、主体和 Role
apiVersion: v1
kind: Namespace
metadata:
name: team-a
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: reporter
namespace: team-a
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: reporter-read
namespace: team-a
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["public-config"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: reporter-read
namespace: team-a
subjects:
- kind: ServiceAccount
name: reporter
namespace: team-a
roleRef:
kind: Role
name: reporter-read
apiGroup: rbac.authorization.k8s.io
保存为 rbac-example.yaml 后执行:
kubectl apply -f rbac-example.yaml
预期结果类似:
namespace/team-a created
serviceaccount/reporter created
role.rbac.authorization.k8s.io/reporter-read created
rolebinding.rbac.authorization.k8s.io/reporter-read created
2. 验证允许和拒绝的请求
使用 --as 模拟该 ServiceAccount:
kubectl auth can-i get pods \
--as=system:serviceaccount:team-a:reporter \
-n team-a
预期:
yes
跨命名空间访问:
kubectl auth can-i get pods \
--as=system:serviceaccount:team-a:reporter \
-n team-b
预期:
no
读取指定 ConfigMap:
kubectl auth can-i get configmap/public-config \
--as=system:serviceaccount:team-a:reporter \
-n team-a
预期:
yes
读取其他 ConfigMap:
kubectl auth can-i get configmap/private-config \
--as=system:serviceaccount:team-a:reporter \
-n team-a
预期:
no
尝试读取 Secret:
kubectl auth can-i list secrets \
--as=system:serviceaccount:team-a:reporter \
-n team-a
预期:
no
查看主体在该命名空间下的权限列表:
kubectl auth can-i --list \
--as=system:serviceaccount:team-a:reporter \
-n team-a
输出会列出匹配的资源和动词,但不同 Kubernetes 版本、客户端和授权配置下的展示格式可能不同。--list 适合发现明显的过宽权限,不能替代对关键路径逐项测试。
3. 用实际客户端访问时,Token 不是 RBAC 对象
在现代 Kubernetes 中,ServiceAccount 通常通过短期、投影的 Token 访问 API,而不是依赖长期 Secret Token。可以让 Kubernetes 创建一个短期 Token:
TOKEN=$(kubectl create token reporter -n team-a)
然后使用它访问 API:
kubectl --server="$KUBE_SERVER" \
--token="$TOKEN" \
--certificate-authority="$CA_FILE" \
get pods -n team-a
其中:
KUBE_SERVER是 API Server 地址;CA_FILE是验证 API Server 证书的 CA 文件;- Token 只证明请求主体是
team-a中的reporter; - 是否能读取 Pod,仍由 RoleBinding 决定。
Token 的签发、过期和轮换属于认证与凭据生命周期问题,不会因为修改 Role 而改变 Token 的身份。修改 RBAC 后,已有 Token 通常仍代表同一个主体,但后续请求会按最新的授权对象重新判断。
六、权限规则的匹配边界
1. 通配符会跨越未来新增能力
下面的规则非常宽:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
它不仅覆盖当前已知资源,还可能覆盖未来安装的 API 资源。CRD、聚合 API Server 或 Kubernetes 新版本增加资源后,这类通配符可能自动获得新的权限。
最小权限设计通常应明确写出:
apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
但明确列表也不能消除所有风险,因为对 Deployment 的 update 或 patch 可能间接影响 Pod、Secret 挂载和容器权限。
2. list 不是“只能知道数量”
允许 list pods 通常意味着可以读取返回结果中的 Pod 元数据,往往包括:
- Pod 名称;
- 标签;
- 镜像;
- 节点;
- 状态;
- IP;
- 部分配置字段。
如果规则还允许读取 Secret、ConfigMap 或 Pod 详情,泄露范围会扩大。RBAC 按 API 操作控制,不会根据调用者是否“只打算读取一个字段”自动进行字段级脱敏。
3. 对象权限不等于字段权限
RBAC 通常控制资源和动词,不控制对象内部字段。例如:
resources: ["deployments"]
verbs: ["patch"]
意味着可以对匹配的 Deployment 执行补丁操作,而不是“只能修改副本数”。要限制可写字段,通常需要准入策略、专用控制器或 API 设计,而不是单靠 RBAC。
4. 非资源 URL 只适用于 ClusterRole
某些 API Server 端点不是 Kubernetes 资源,例如:
/version
/healthz
/readyz
ClusterRole 可以声明:
rules:
- nonResourceURLs: ["/version", "/healthz"]
verbs: ["get"]
非资源 URL 通常使用路径匹配规则,不能把它们写进 resources。这类权限应明确列出,避免使用过宽的 /*。
七、ClusterRole 聚合:把多个权限片段组合起来
ClusterRole 聚合允许一个 ClusterRole 根据标签自动收集其他 ClusterRole 的规则。聚合目标通常包含:
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.example.com/aggregate-to-reporting: "true"
例如先创建一个作为“目标角色”的 ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: reporting
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.example.com/aggregate-to-reporting: "true"
再创建一个带匹配标签的权限片段:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: reporting-pods
labels:
rbac.example.com/aggregate-to-reporting: "true"
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
ClusterRole 聚合控制器观察这些对象,并把 reporting-pods 的规则写入 reporting 的 rules。之后只需要绑定 reporting:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: reporting-binding
subjects:
- kind: Group
name: reporting-users
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: reporting
apiGroup: rbac.authorization.k8s.io
数据流可以表示为:
flowchart LR
A[权限片段 ClusterRole] -->|标签匹配| C[聚合控制器]
B[另一个权限片段 ClusterRole] -->|标签匹配| C
C -->|写入规则| D[目标 ClusterRole]
D --> E[RoleBinding 或 ClusterRoleBinding]
E --> F[主体获得权限]
聚合有三个重要边界:
-
聚合不是 Binding
聚合只修改目标 ClusterRole 的规则;目标 ClusterRole 仍需通过 Binding 授予主体。 -
聚合不是静态复制
当匹配标签的 ClusterRole 增加、修改或删除时,控制器会重新计算目标角色。权限可能因此动态增加或减少。 -
目标角色的
rules由控制器管理
对包含aggregationRule的 ClusterRole 手工编辑rules,可能被控制器覆盖。应修改被聚合的权限片段或聚合选择器。
Kubernetes 内置的 admin、edit、view 等默认 ClusterRole 也使用聚合机制向角色提供扩展资源权限。自定义资源的安装者可以通过约定标签把 CRD 相关权限聚合到这些默认角色中。常见标签包括:
rbac.authorization.k8s.io/aggregate-to-admin: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-view: "true"
这很方便,但也是安全边界:安装一个带有聚合标签的 ClusterRole,可能让已有的 admin、edit 或 view 用户获得新资源的权限。 因此,检查 CRD 或扩展组件安装包中的 RBAC 清单非常重要。
此外,标签本身也是权限输入。能够创建或修改 ClusterRole,并能给它添加聚合标签的主体,可能影响多个现有角色。不能把“只是修改标签”视为低风险操作。
八、默认角色、自动修复和系统角色
Kubernetes API Server 会创建一些默认 ClusterRole 和 ClusterRoleBinding,例如:
cluster-adminadmineditviewsystem:discoverysystem:node- 各类
system:前缀的控制器角色
带有 kubernetes.io/bootstrapping=rbac-defaults 标签的默认 RBAC 对象可能被 Kubernetes 的自动协调机制恢复缺失的规则或绑定。例如,直接从默认角色中删除一条规则,控制器可能在之后重新添加。
生产环境中不应直接修改默认角色来实现业务权限。更可控的方式是:
- 创建自己的 ClusterRole;
- 通过 RoleBinding 限制到指定命名空间;
- 如果确实需要复用默认角色的语义,创建独立角色或使用明确的聚合标签;
- 对升级和扩展组件安装后的实际权限重新验证。
cluster-admin 是全局高权限角色。尤其要注意,system:masters 组在 Kubernetes 的授权语义中具有特殊地位,通常被视为绕过普通授权检查的超级管理员组。不要把普通运维人员、应用 ServiceAccount 或自动化系统加入该组来“解决权限问题”。
九、最小权限:从形式化目标到实际规则
最小权限不是“规则行数最少”,而是主体在任务约束下获得的权限集合尽可能小。
设任务要求集合为:
实际授予权限集合为 。安全上首先要求:
如果存在某个权限:
那么它是潜在的额外权限。最小权限的目标是在满足任务的前提下,尽量减少:
完整推导一个报告程序的权限:
第一步:列出实际 API 调用
假设程序需要:
GET /api/v1/namespaces/team-a/pods
GET /api/v1/namespaces/team-a/pods/{name}
WATCH /api/v1/namespaces/team-a/pods
GET /api/v1/namespaces/team-a/configmaps/public-config
对应为:
("", "pods", "list", "team-a")
("", "pods", "get", "team-a")
("", "pods", "watch", "team-a")
("", "configmaps", "get", "team-a", "public-config")
第二步:删除没有必要的动作
程序不创建、不修改、不删除 Pod,因此不授予:
create、update、patch、delete
程序只读取一个 ConfigMap,因此不授予:
list configmaps
get configmaps/*
第三步:限定命名空间
如果程序只服务 team-a,使用 RoleBinding,而不是 ClusterRoleBinding:
RoleBinding(team-a) ⟹ 规则只在 team-a 生效
第四步:限定对象名
对 ConfigMap 使用 resourceNames:
resourceNames: ["public-config"]
但不对 Pod 使用对象名限制,因为程序需要观察一组动态 Pod。
第五步:验证反例
如果误用 ClusterRoleBinding:
roleRef:
kind: ClusterRole
name: reporter-read
即使 ClusterRole 中只有 get/list/watch pods,它也会把这些命名空间资源权限扩展到所有命名空间。规则本身看起来不宽,绑定范围却改变了权限集合。
再例如,如果把:
resources: ["pods"]
verbs: ["*"]
授予报告程序,它将获得创建、修改和删除 Pod 的权限。即使程序代码当前只调用 list,凭据泄露后攻击者仍可使用全部已授予权限。
十、RBAC 与“权限升级”风险
允许一个主体管理 RBAC 对象,通常比表面上的资源权限更危险。
例如,主体拥有:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "rolebindings"]
verbs: ["create", "update", "patch"]
它可能尝试创建一个包含 secrets/get 的 Role,再把 RoleBinding 指向自己。若没有额外限制,这相当于把“管理 RBAC”变成“自我授权”。
Kubernetes 的 RBAC 授权实现包含权限升级保护:
- 创建或更新 Role、ClusterRole 时,主体通常不能声明自己本身没有的权限;
- 把 RoleBinding 或 ClusterRoleBinding 绑定到超出自身权限的角色时,通常需要相应的
bind权限; escalate和bind是针对 RBAC 对象的特殊动词;- 拥有
escalate、bind或特殊超级管理员身份的主体可以绕过部分保护,因此这些权限必须单独审查。
一个典型的高风险规则是:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"]
verbs: ["*"]
它不只是“能查看权限配置”,而是可能控制整个集群的授权关系。
同理,以下权限虽然不是 RBAC API 本身,也常常可以导致间接提权:
- 修改可执行工作负载的 Deployment;
- 创建能挂载高权限 ServiceAccount 的 Pod;
- 读取 Secret;
- 修改 Webhook、准入配置或控制器配置;
- 修改命名空间中的资源,使其使用高权限身份。
所以最小权限分析必须考虑权限的可达效果,不能只按资源名称判断危险程度。
十一、常见误解和失败表现
误解一:创建 Role 后权限自动生效
错误示例:
kind: Role
metadata:
name: reader
没有任何 RoleBinding 时,所有主体仍然可能得到:
no
检查方法:
kubectl get role reader -n team-a
kubectl get rolebinding -n team-a
kubectl auth can-i get pods -n team-a \
--as=system:serviceaccount:team-a:reporter
误解二:RoleBinding 绑定 ClusterRole 就变成集群权限
RoleBinding 的命名空间决定命名空间资源的作用范围。引用 ClusterRole 只复用规则,不改变 RoleBinding 的范围。
误解三:给了 get pods 就能读日志
日志是 pods/log 子资源,需要单独授权:
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
误解四:允许 get secrets 不危险,因为没有 list
Secret 的 get 仍然可以读取已知名称的 Secret。名称可从 Pod、Deployment、配置或错误信息中推断,不能把“没有 list”当作足够的保密措施。
误解五:ServiceAccount 是命名空间级,所以它不能拥有集群权限
ServiceAccount 对象位于命名空间,但它可以被 ClusterRoleBinding 绑定。最终权限取决于 Binding,而不是 ServiceAccount 对象的存放位置。
误解六:认证成功就应该能访问 API
认证只产生身份。下面的错误通常属于授权问题:
Error from server (Forbidden): pods is forbidden:
User "system:serviceaccount:team-a:reporter" cannot list resource "pods"
应检查:
kubectl auth can-i list pods \
--as=system:serviceaccount:team-a:reporter \
-n team-a
如果实际客户端使用的是 OIDC 或 Webhook 用户,还要确认 API Server 看到的用户名和组名,而不是只看身份提供商界面中的显示名称。
十二、诊断 RBAC 问题的顺序
发生 403 Forbidden 时,可以按请求属性逐项核对。
1. 确认实际主体
对于 ServiceAccount:
system:serviceaccount:<namespace>:<name>
对于 OIDC、证书或 Webhook 用户,要确认:
- 用户名;
- 组;
- 是否使用了前缀;
- 是否存在大小写或空格差异。
2. 确认资源的 API Group
kubectl api-resources
该命令可以帮助确认资源名称、短名称、API Group 和是否命名空间级。
例如:
kubectl api-resources | grep -E 'pods|deployments|roles'
3. 确认子资源
命令行中的:
kubectl logs pod/example -n team-a
实际对应的授权资源通常是:
pods/log
而不是普通 pods。
4. 分别测试具体权限
kubectl auth can-i get pods -n team-a --as=...
kubectl auth can-i list pods -n team-a --as=...
kubectl auth can-i watch pods -n team-a --as=...
kubectl auth can-i get pods/log -n team-a --as=...
不要只测试 --list 后就认为所有客户端操作都正确,因为客户端可能还会调用 watch、patch、update/status 等接口。
5. 检查绑定范围和聚合状态
kubectl get role,rolebinding -n team-a
kubectl get clusterrole,clusterrolebinding
kubectl describe rolebinding reporter-read -n team-a
kubectl describe clusterrole reporting
如果使用了聚合角色,还要检查:
kubectl get clusterrole reporting -o yaml
kubectl get clusterrole --show-labels
确认匹配标签的权限片段是否存在,以及目标 ClusterRole 的 rules 是否已由控制器更新。
6. 区分 RBAC 拒绝和其他授权器拒绝
Kubernetes 可以配置 RBAC、Node、Webhook 等授权器。kubectl auth can-i 能帮助模拟授权,但最终拒绝原因仍可能来自:
- Node 授权器;
- 外部 Webhook 授权器;
- 准入控制器;
- API 资源本身的业务校验。
例如,请求得到 403 不一定意味着某个 Role 缺少规则,也可能是外部授权 Webhook 返回了拒绝。审计日志可以记录请求中的用户、动词、资源、命名空间和响应状态,用来将实际失败请求与 RBAC 配置对应起来。
十三、生产环境中的边界和取舍
1. 命名空间隔离不是完整的多租户隔离
RoleBinding 可以把命名空间资源权限限制在租户命名空间,但以下对象仍是集群级的:
- Node;
- PersistentVolume;
- ClusterRole;
- ClusterRoleBinding;
- StorageClass;
- CustomResourceDefinition;
- ValidatingWebhookConfiguration、MutatingWebhookConfiguration。
如果租户能创建 Pod,它还可能通过节点、主机路径、网络策略、运行时配置等其他机制影响隔离边界。因此,RBAC 是多租户的一部分,不是完整的租户隔离方案。
2. “只读角色”也可能泄露敏感数据
内置 view 角色通常不包含 Secret 读取权限,这是有意设计的。因为读取 Secret 的价值通常接近读取凭据本身。扩展内置角色时,应明确判断新资源是否包含:
- Token;
- 密钥;
- 数据库密码;
- 配置中的内部地址;
- 其他租户信息。
3. 自动化组件需要的不是“所有权限”
控制器常见的权限是:
get/list/watch:观察输入资源
create/update/patch/delete:管理它负责的输出资源
但不同控制器的输入和输出应分别列出。直接使用:
apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
会把控制器的故障、漏洞或凭据泄露影响面扩大到整个集群。
4. 权限变更应有验证和回滚路径
修改 RBAC 前,先导出相关对象:
kubectl get role,rolebinding -n team-a -o yaml > team-a-rbac-backup.yaml
kubectl get clusterrole,clusterrolebinding -o yaml > cluster-rbac-backup.yaml
应用后验证:
kubectl auth can-i --list \
--as=system:serviceaccount:team-a:reporter \
-n team-a
如果错误地创建了高权限 ClusterRoleBinding,应立即删除错误绑定,而不是只删除 Role:
kubectl delete clusterrolebinding <错误绑定名>
删除 Role 不一定能撤销其他 Binding 通过同名或其他角色授予的权限;撤销时必须定位实际的权限来源。
十四、设计 RBAC 时的检查框架
一个可验证的 RBAC 设计至少应回答以下问题:
- 主体是谁:用户、组还是 ServiceAccount?
- 主体来自哪种认证机制,API Server 最终看到的用户名和组是什么?
- 需要访问哪些 API Group、资源和子资源?
- 每个资源需要哪些动词,而不是笼统地使用
*? - 权限只需要一个命名空间,还是确实需要跨命名空间?
- 是否可以用 RoleBinding 引用 ClusterRole,而不是 ClusterRoleBinding?
- 是否能用
resourceNames缩小到特定对象? - 是否包含 Secret、Token、Webhook 配置或 RBAC 对象等高风险资源?
- 修改资源的权限是否会间接修改 Pod、身份或凭据?
- 是否会通过聚合标签影响内置角色?
- 是否存在
bind、escalate、impersonate或system:masters等提权路径? - 是否使用
kubectl auth can-i和实际审计日志验证了允许与拒绝两类结果?
其中 impersonate 也需要特别审查。允许主体模拟用户、组或 ServiceAccount 后,它可能以另一个身份发起请求;这种权限本质上是在控制授权输入,而不是普通的业务资源读取权限。
RBAC 的核心关系可以最后压缩为一句形式化描述:
而最小权限的工程目标是:
并尽量让两者之间的差集最小。Role 与 ClusterRole 决定“允许什么”,RoleBinding 与 ClusterRoleBinding 决定“授予谁、在哪个范围授予”,聚合决定“多个权限片段如何动态组合”。理解这三层关系,才能在命名空间隔离、扩展资源和自动化控制器之间建立可审计、可验证的授权边界。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 认证:证书、Token、OIDC、Webhook 和用户生命周期
- 下一篇:Kubernetes ServiceAccount:Projected Token、Audience、轮换和 Workload Identity
- 延伸:Kubernetes 审计日志:Policy、Stage、Backend、性能和合规留存
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论