Kubernetes RBAC 是集群权限控制的基石:它用 ServiceAccount、Role 与 ClusterRole 把”谁能对哪些资源做什么”声明式地管起来。本文从最小权限原则出发,手把手创建一个只读账号,覆盖 RoleBinding 绑定、verbs 规则表、CI/CD 集成与 Forbidden 排障,让你把权限收得既安全又省心。
一、RBAC 在 Kubernetes 安全里扮演什么角色
Kubernetes 里任何请求都要经过”认证(你是谁)→ 鉴权(你能做什么)→ 准入(请求合不合法)”三道闸。RBAC(Role-Based Access Control,基于角色的访问控制)负责第二道闸:它不关心你是人还是 Pod,只看”这个身份被授权访问哪些 API 资源、能执行哪些动词”。把 RBAC 配好,意味着即使某个 ServiceAccount 的令牌泄露,攻击者能做的事也被牢牢限制在授权范围内。
和给节点打标签、给容器降权一样,RBAC 也是”最小权限”思路的一环——我们此前在 K8s Pod 安全上下文实战 里讲过运行时最小权限,RBAC 则是 API 层面的最小权限,两者配合才能堵住整条攻击链。
二、四个核心对象:谁是 Subject、谁能做什么
RBAC 由四个对象组成,理解它们的分工就理解了 RBAC 的全部:
| 对象 | 作用域 | 职责 |
|---|---|---|
| ServiceAccount | 命名空间 | 给 Pod / 工作负载用的”账号”,持有令牌 |
| Role | 命名空间 | 定义”某命名空间内”允许的资源与动词 |
| ClusterRole | 集群 | 定义”集群级”或跨命名空间的规则 |
| RoleBinding | 命名空间/集群 | 把 Role/ClusterRole 绑定到 Subject |
一个常见误区:ClusterRole 只能用于集群级资源。其实 ClusterRole 也能通过 RoleBinding 绑定到某个命名空间,从而”缩小”为命名空间内权限——这是复用同一套规则、却限定作用域的常用技巧。
三、最小权限实战:创建一个只读 ServiceAccount
3.1 创建 ServiceAccount
先建一个只用于读取 default 命名空间资源的账号:
apiVersion: v1
kind: ServiceAccount
metadata:
name: reader-sa
namespace: default
3.2 定义 Role:只允许 get/list/watch
Role 把权限收得很死——只给 Pods 与 ConfigMaps 的读权限,绝不给 create/delete:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: default
rules:
- apiGroups: [""]
resources: ["pods", "configmaps"]
verbs: ["get", "list", "watch"]
3.3 用 RoleBinding 把两者绑起来
RoleBinding 是”胶水”,把上面的 Role 授权给 reader-sa:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader-binding
namespace: default
subjects:
- kind: ServiceAccount
name: reader-sa
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
3.4 给 Pod 指定 ServiceAccount
工作负载通过 spec.serviceAccountName 使用该账号,automountServiceAccountToken 还能进一步控制令牌是否自动挂载:
apiVersion: apps/v1
kind: Deployment
metadata:
name: exporter
spec:
template:
spec:
serviceAccountName: reader-sa
automountServiceAccountToken: true
containers:
- name: app
image: nginx:1.27
四、Role 与 ClusterRole:命名空间级 vs 集群级
| 维度 | Role | ClusterRole |
|---|---|---|
| 作用域 | 单个命名空间 | 整个集群 |
| 典型用途 | 限定某团队的资源 | Node/PV 等集群资源、只读视图 |
| 绑定方式 | RoleBinding | ClusterRoleBinding 或 RoleBinding |
| 安全风险 | 低,泄漏影响小 | 高,需严格审计 |
经验法则:能用 Role 就别用 ClusterRole;必须操作 Node、PersistentVolume、StorageClass 这类不带命名空间概念的资源时,才上 ClusterRole。
五、常用 verbs 与 resources 规则
| verbs | 含义 | 风险 |
|---|---|---|
| get / list / watch | 只读 | 低 |
| create / update / patch | 写 | 中 |
| delete / deletecollection | 删除 | 高 |
| * | 全部动词 | 极高,禁止 |
resources 也尽量写具体,不要用 [“*”]。例如只放行 pods 和 pods/log,而不是整个核心 API 组。
六、用 kubectl 验证:auth can-i
配完别凭感觉,直接用 auth can-i 模拟身份验证权限,这是排障第一招:
# 以 reader-sa 身份模拟访问
kubectl auth can-i get pods \
--as=system:serviceaccount:default:reader-sa
# 期望输出:yes
# 尝试越权删除,应当被拒
kubectl auth can-i delete pods \
--as=system:serviceaccount:default:reader-sa
# 期望输出:no
还能为 ServiceAccount 现发一个短期令牌做手动验证:
kubectl create token reader-sa -n default --duration=1h
七、在 CI/CD 中集成最小权限 ServiceAccount
把 RBAC 接进流水线,能让部署机器人只拥有它真正需要的权限。例如在 GitHub Actions 实战 里,用 OIDC 或直接挂载受限 ServiceAccount 令牌,而不是把 cluster-admin 的 kubeconfig 写进仓库密钥。一个最小权限的部署步骤示意:
- name: Deploy with limited SA
run: |
kubectl --as=system:serviceaccount:ci:deployer \
apply -f k8s/
这里的 deployer 账号只绑定了指定命名空间的 deployments 写权限,即使 CI 环境被攻破,影响也被锁死在该命名空间内。
八、Forbidden 错误怎么定位
遇到 Error from server (Forbidden),按三步排查:第一,确认 Pod 用的是哪个 ServiceAccount(看 spec.serviceAccountName 与 automount 设置);第二,用 kubectl auth can-i 模拟该身份,定位到底是缺哪个 verbs 还是哪个 resources;第三,检查 RoleBinding 的 namespace 与 subject 的 namespace 是否一致——这是最容易踩的坑,跨命名空间绑定会静默失效。
九、最小权限落地清单
把下面几条当成发布前的检查项:① 默认拒绝,只开必需权限;② 优先 Role 而非 ClusterRole;③ verbs 不写通配符 *;④ 定期用 kubectl get clusterrolebindings 审计谁拿到了 cluster-admin;⑤ 给不同环境(dev/staging/prod)用不同 ServiceAccount,避免一把令牌走天下;⑥ 配合 Docker 容器基础 与运行时降权,形成纵深防御。
十、ClusterRole 实战:给 SRE 一个集群只读视图
只读需求常常跨命名空间——SRE 排查故障时需要看所有命名空间的 Pod,但绝不该能删任何东西。这时用 ClusterRole 定义一次规则,再按团队绑定:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-reader
rules:
- apiGroups: [""]
resources: ["pods", "configmaps", "services", "endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
通过 ClusterRoleBinding 把它赋予某个组,或者用 RoleBinding 把它”缩小”绑定到单个命名空间——同一套规则,既能集群级也能命名空间级复用,这正是 ClusterRole 比 Role 灵活的地方。
十一、审计:谁到底拥有什么权限
权限会随人员变动悄悄膨胀,定期审计是必须的。先看某个命名空间里到底绑了哪些 RoleBinding:
# 列出 default 命名空间的所有 RoleBinding 及绑定对象
kubectl get rolebindings -n default -o wide
# 反查:谁有权删除 pods?(需安装 kubectl-who-can)
kubectl who-can delete pods -n default
kubectl-who-can 这类小工具能直接回答”哪个 ServiceAccount、哪个 Group 拥有某某权限”,做权限回收时特别省事。建议把它写进你的排障脚本箱。
十二、把 RBAC 写进 GitOps,并避开反模式
RBAC 对象也是声明式 YAML,理应和应用一起进 Git,由 GitHub Actions 这类流水线 在评审通过后自动 apply。这样既有了变更记录,也避免了有人悄悄在集群里手动 kubectl create rolebinding 留下影子权限。
| 反模式 | 后果 | 正确做法 |
|---|---|---|
| 直接给 cluster-admin | 权限过大,一钥失守全局 | 按需建最小 Role |
| verbs 用 [“*”] | 连带 delete 也被放开 | 只列必要动词 |
| 跨命名空间绑定 RoleBinding | 绑定静默失效 | 确认 subject 与绑定同 ns |
| 长期不审计 | 影子权限堆积 | 定期 who-can 复查 |
十三、三句话记住 RBAC
如果只带走三点:第一,工作负载的身份是 ServiceAccount,Pod 与人类走的是同一套 RBAC 鉴权通道;第二,权限尽量落在命名空间级 Role,能用 RoleBinding 就别碰 ClusterRoleBinding;第三,配完一定要用 kubectl auth can-i 验证,再让 CI 以最小权限账号执行部署,而不是把 cluster-admin 的 kubeconfig 写进仓库。
权限不是配完就完事,它是会随着架构演进而腐化的东西。把 RBAC 当作代码一样纳入版本管理、随应用一起评审,你的 Kubernetes 集群才会既好用又安全。




