RBAC 是 Kubernetes 里最有杠杆的安全控制之一:它能把“一个小失误”限制在命名空间内,而不是扩散成集群级事故。但 RBAC 也最容易走向另一个极端——大家被权限折腾到崩溃,最后一句“直接给 cluster-admin 吧”。

这篇 tips 的目标是:实用最小权限,让权限体系可运营、可审计、可迭代。

三个概念先搞清楚

  • Subject(主体):谁在发起请求(用户/用户组/ServiceAccount)。
  • Role / ClusterRole:允许做什么(对哪些资源做哪些动词)。
  • RoleBinding / ClusterRoleBinding:把角色绑定给主体(真正生效的地方)。

记住一句话:

Role 只是规则,Binding 才让规则生效。

命名空间权限 vs 集群权限

  • Role:只能描述某个命名空间内的权限。
  • ClusterRole:可以描述命名空间资源、集群级资源和非资源 URL;真正授权范围由 Binding 决定。
  • RoleBinding:只在自身 namespace 授权,也可以引用一个可复用的 ClusterRole。
  • ClusterRoleBinding:把 ClusterRole 授权到整个集群,包括所有 namespace。

生产更稳妥的做法:

  • 业务应用优先用 Role + RoleBinding
  • 只有确实需要跨命名空间/集群范围时才上 ClusterRole

ServiceAccount:别再用 default 了

Pod 不指定 serviceAccountName 时会用 namespace 的 default ServiceAccount。很多团队的默认 SA 被不小心绑定了高权限,风险非常大。

推荐:

  1. 每个应用使用独立 ServiceAccount
  2. 默认关闭 token 挂载(除非真的需要访问 K8s API)
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api
  namespace: app
automountServiceAccountToken: false

Deployment 里也显式写:

spec:
  template:
    spec:
      serviceAccountName: api
      automountServiceAccountToken: false

如果应用确实需要访问 API(比如 leader election、watch 资源),再把 token 挂载打开,并严格审核 RBAC。

Role 规则怎么写:先从“可用的只读”开始

RBAC 规则由三部分组成:

  • apiGroups
  • resources
  • verbs

一个常见的“排障只读” Role(给人用):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ns-observer
  namespace: app
rules:
  - apiGroups: [""]
    resources: ["pods", "services", "configmaps", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get"]
  - apiGroups: ["discovery.k8s.io"]
    resources: ["endpointslices"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch"]

注意 pods/log:很多人只给了 pods,却发现看不了日志,就是因为漏了子资源。

子资源是 RBAC 的高频坑

常见子资源:

  • 看日志:pods/log
  • 远程执行:pods/exec
  • 端口转发:pods/portforward
  • 写 status:deployments/status、pods/status

pods/exec 和 pods/portforward 通常使用 create 动词,不是 get。当你看到 “forbidden” 时,不要只盯着 pods,还要检查实际请求的子资源和动词。

绑定策略:把“人”和“应用”分开

不要把人和应用随便绑在同一个 Role 上。推荐做法:

  • 应用 SA:只给程序真正需要的最小权限
  • 人(运维/开发):默认只读;需要写权限时走升级 Role(更严格、可审计)

这样你才能在审计里说清楚:

  • “应用代码能做什么”
  • “某个用户在什么情况下能做什么”

RBAC 权限是加法,没有显式 deny。给主体新增一个更窄的 RoleBinding,不能抵消它从其他 Binding、用户组或 ClusterRoleBinding 获得的宽权限。审查时必须合并主体的全部授权来源。

用 kubectl auth can-i 做权限验算

这是最实用的 RBAC 调试工具:

kubectl auth can-i get pods -n app
kubectl auth can-i get pods/log -n app
kubectl auth can-i create pods/exec -n app

支持模拟某个 ServiceAccount:

kubectl auth can-i get secrets -n app --as system:serviceaccount:app:api
kubectl auth can-i --list -n app --as system:serviceaccount:app:api

使用 --as 需要调用者拥有对应的 impersonation 权限;普通用户不能任意模拟其他身份。这些命令能让你在上线前验证权限是否“刚好够用”,而不是靠线上报错摸索。

常见最小权限配方

1)只读 ConfigMap(watch 热更新)

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cm-reader
  namespace: app
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "watch"]

2)leader election(leases)

很多控制器通过 coordination.k8s.io 的 leases 做 leader election:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: leader-election
  namespace: app
rules:
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]

3)允许创建 Pods(高风险权限)

“能创建 Pod”通常意味着很强的能力:可能拉任意镜像、选择 namespace 中权限更高的 ServiceAccount、读取可挂载的 Secret/PVC、访问内部服务或消耗资源。它应被视为高危权限,需要:

  • 严格限定命名空间
  • 结合 Pod Security / Admission Policy 限制 Pod 能做什么
  • 尽量用固定模板的控制器,而不是让业务任意创建 Pod

红旗(能避免很多事故)

  • 给应用 ServiceAccount 绑定 cluster-admin
  • resources: ["*"] + verbs: ["*"] 的通配角色;未来新增的 API、资源或子资源也可能自动落入权限范围
  • 广泛授予读取 secrets(secrets 常常就是“钥匙”)
  • 把 break-glass 权限变成长期常态

让升级权限“可控”(break-glass)

真实世界里你总会遇到需要更高权限的场景。关键是把升级变成:

  • 有时效(临时)
  • 可审计(关联工单/事故)
  • 有范围(限定 namespace/动作)

即使只是流程+日志记录,也比“永久 cluster-admin”强很多。

Kubernetes RBAC 本身没有 Binding TTL。临时权限要靠身份平台、审批系统或控制器按时删除 Binding,并在到期后重新执行 kubectl auth can-i 验证权限确实回收。

进阶:更成熟团队会用的技巧

用 resourceNames 做“点状权限”

比如只允许读一个 ConfigMap:

rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["api-config"]
    verbs: ["get"]

把 pods/exec、pods/portforward 当成高权限

exec/port-forward 基本等价于“进入容器”,风险很高。建议单独做一个 debug Role,由值班/运维组按需申请,而不是默认给所有人。

内置角色更适合“人”,不一定适合应用

Kubernetes 自带 view、edit、admin 这类 ClusterRole。它们对人类账号通常是好起点,尤其适合先给团队一个统一基线,再叠加项目专用策略。

但 edit 可以读取 Secret,并能用 namespace 中任意 ServiceAccount 运行 Pod,可能间接获得更高 API 权限。对应用 ServiceAccount,更应该显式写 Role;给人绑定内置角色前,也要先读清它的完整规则。

上线前让它变成习惯

RBAC 应作为持续流程维护:

  • 新工作负载创建时,先建最小 ServiceAccount 和 Role。
  • 权限变更像代码一样 review。
  • 升级权限要有入口,但不是默认路径。

这样团队不会因为“权限太麻烦”绕开规则,集群也不需要靠“全员 cluster-admin”来换效率。

上线前自检

  • 每个应用都有独立 ServiceAccount
  • 默认关闭 automountServiceAccountToken,除非需要访问 API
  • 业务优先 Role/RoleBinding,不乱用 ClusterRoleBinding
  • 子资源(pods/log、pods/exec)按需显式授权
  • 用 kubectl auth can-i + impersonation 做上线前验算
  • break-glass 权限临时、可审计、可回收

权限审查时的三个判断

Q: Role 和 ClusterRole 有什么区别? A: Role 只描述命名空间权限;ClusterRole 可描述更广的资源,但经 RoleBinding 引用时仍只在该 Binding 的 namespace 生效。

Q: 为什么 Pod 权限超出预期? A: 可能绑定了默认 ServiceAccount 或过宽的 ClusterRoleBinding,检查 SA 与绑定关系。

Q: 如何验证权限? A: 用 kubectl auth can-i 并指定对应 ServiceAccount。

参考链接