Kubernetes RBAC 实战:用 ServiceAccount 与 Role 实现最小权限

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 集群级

维度RoleClusterRole
作用域单个命名空间整个集群
典型用途限定某团队的资源Node/PV 等集群资源、只读视图
绑定方式RoleBindingClusterRoleBinding 或 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 集群才会既好用又安全。

上一篇 Rust 实战:所有权、借用与生命周期
下一篇 数据库压测实战:用 sysbench 与 pgbench 量化性能
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]