Kubernetes RBAC 实战指南:四类对象一张决策表,5 分钟给集群装上门禁
【免费下载链接】kubernetes-handbookKubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南项目地址: https://gitcode.com/gh_mirrors/ku/kubernetes-handbook
你在 kubernetes-handbook 仓库里翻到 concepts/rbac.md,想给新入职的同事讲清楚:集群里"谁、在哪个命名空间、能对什么资源做什么"到底怎么管起来?Kubernetes RBAC 就是回答这个问题的门禁系统。Role、ClusterRole、RoleBinding、ClusterRoleBinding四个对象,加上一组kubectl命令,就能把权限发到你想要的粒度。本文给决策表、速查规则和一条升级老集群的灰度路线。
先打开开关:为什么弃用 AlwaysAllow 和 ABAC
默认状态下 API Server 的AlwaysAllow模式对任何已认证用户放行所有请求,不少装机工具还默认这么开。仓库里的 guide/kubernetes-security-best-practice.md 给的修正姿势很直接:
--authorization-mode=RBAC --anonymous-auth=false老一代方案 ABAC 的痛点在运维侧:策略是一份躺在 master 节点上的文件,改一条规则要 SSH 上去改文件,然后重启 API Server。RBAC 把策略变成了 Kubernetes 自己的 API 对象,用kubectl就能创建、修改、删除,不用碰节点文件系统。更妙的是递归性——RBAC 可以给某个用户授予"管理授权"的权限,等于把发卡台也开放了。仓库中 practice/rbac-support-in-kubernetes.md 用真实集群验证过这一点:系统角色覆盖足够,集群可以完全跑在 RBAC 管理之下。
旧清单里
apiVersion多为rbac.authorization.k8s.io/v1beta1(本文基于 Kubernetes 1.6 时代资料),新版本改成rbac.authorization.k8s.io/v1即可,字段语义没变。
一个心智模型:授权三问
遇到任何授权需求,先问三个问题:
- 谁?——Subject,三种取值:
User、Group、ServiceAccount。 - 能干什么?——权限规则,装进
Role或ClusterRole。规则只累加,没有"否定"规则,写不了"允许除了 X 以外的一切"。 - 在哪干?——范围,由
RoleBinding或ClusterRoleBinding决定。
类比:Role像门禁卡的样式——写清楚能刷开哪些门;Binding才是发卡的动作,把样式交给具体的人。ClusterRole则是公司级通行卡的样式:在总部定义一次,各楼层随时复用。
四类对象怎么选:一张决策表
| 对象 | 一句话定位 |
|---|---|
Role | 只在单一命名空间内定义权限规则 |
ClusterRole | 集群级规则,覆盖集群范围资源(如 node)、非资源 endpoint(如/healthz)、跨命名空间查询 |
RoleBinding | 在自己所在命名空间内发卡,可引用同命名空间的Role或任意ClusterRole |
ClusterRoleBinding | 全集群发卡,只能引用ClusterRole |
最容易踩的坑是最后一行组合:RoleBinding引用ClusterRole时,权限范围被收窄到 Binding 所在的命名空间。角色说的是"能读 secret",Binding 说的是"只在 development 里"。
最短示例——给 default 命名空间的 jane 发一张只读 pod 的卡:
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: default name: pod-reader rules: - apiGroups: [""] # "" 代表 core API group resources: ["pods"] verbs: ["get", "list", "watch"]kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: default name: read-pods subjects: - kind: User name: jane apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io权限规则速查:rules 里每个字段怎么写
rules是权限的最小单位,一条规则四要素:
| 字段 | 写法要点 |
|---|---|
apiGroups | 空字符串""= core 组;可列多个,如["extensions", "apps"] |
resources | 资源名按 API URL 中的写法;子资源用斜线拼接 |
verbs | getlistwatchcreateupdatepatchdelete等 |
nonResourceURLs | 非资源 endpoint,*是后缀通配符,只能写在ClusterRole |
子资源是新手最常忘的:看 pod 日志的 endpoint 是/pods/{name}/log,所以要写pods/log才算授权:
rules: - apiGroups: [""] resources: ["pods", "pods/log"] verbs: ["get", "list"]resourceNames把权限钉到具体实例上,但有个硬限制:设了resourceNames,动词只能是get、delete、update、patch。list、watch、create、deletecollection 的请求 URL 里根本不出现资源名,规则匹配不上。
rules: - apiGroups: [""] resources: ["configmaps"] resourceNames: ["my-configmap"] verbs: ["get", "update"]其余常见写法:
# 跨两个组读写 deployments rules: - apiGroups: ["extensions", "apps"] resources: ["deployments"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]# 非资源 endpoint(ClusterRole 专用,需配 ClusterRoleBinding) rules: - nonResourceURLs: ["/healthz", "/healthz/*"] verbs: ["get", "post"]动手授权:kubectl 三种姿势
姿势一:一条命令发 namespace 内的卡
kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme把--user换成--group或--serviceaccount=acme:myapp就是给组、给服务账户发卡。
姿势二:YAML 声明式
上一节的 Role + RoleBinding 示例落盘成两个文件,kubectl apply -f即可。适合进 Git 做版本管理。
姿势三:全集群发卡
kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root⚠️
cluster-admin是超级用户权限。给个人用户发全集群卡之前,先问自己三次是不是必须。
实战:devuser 只准进 dev 和 test 两个命名空间
仓库里 guide/kubectl-user-authentication-authorization.md 是完整链路:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \ -profile=kubernetes devuser-csr.json | cfssljson -bare devuser生成devuser.pem/devuser-key.pem后,三步组装 kubeconfig:
kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/ssl/ca.pem \ --embed-certs=true --server=https://172.20.0.113:6443 --kubeconfig=devuser.kubeconfigkubectl config set-credentials devuser --client-certificate=/etc/kubernetes/ssl/devuser.pem \ --client-key=/etc/kubernetes/ssl/devuser-key.pem --embed-certs=true --kubeconfig=devuser.kubeconfigkubectl config set-context kubernetes --cluster=kubernetes --user=devuser \ --namespace=dev --kubeconfig=devuser.kubeconfig授权两条命令:
kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=test验证结果立竿见影——devuser 去 default 命名空间看 pod:
Error from server (Forbidden): User "devuser" cannot list pods in the namespace "default". (get pods)dev 和 test 里畅通无阻,default 直接 403。admin角色"限定在 RoleBinding 所在命名空间内生效"的语义,到这里就闭环了。仓库的 tools/create-user/ 还带了个一键建用户脚本,可参考 说明。
内置角色速查卡:cluster-admin / admin / edit / view
API Server 启动时会自带一套角色。面向用户的四张"标准卡":
| 角色 | 用在哪 | 能力 | 边界 |
|---|---|---|---|
cluster-admin | ClusterRoleBinding(或RoleBinding) | 超级用户 | 全集群;用在RoleBinding时限该命名空间,含命名空间本身 |
admin | RoleBinding | 命名空间内大部分资源读写 | 不能写 resource quota 和 namespace 本身 |
edit | RoleBinding | 命名空间内大部分对象读写 | 不能看角色和角色绑定 |
view | RoleBinding | 命名空间内大部分对象只读 | 不能看角色、角色绑定,也不能看 secret |
view读不了 secret 是刻意设计:secret 里存着服务账户 token,谁能读 secret,谁就能拿到 token 冒充服务账户。给view放行 secret 等于开了权限扩散的口子。
带system:前缀的资源是另一类:system:kube-scheduler、system:node、system:controller:*这些是控制面组件自用的,API Server 每次启动会自动补齐缺失的权限。别手改——system:node里写着 kubelet 能读所有 secret,改坏了节点直接不工作。想停掉自动修复,把注解rbac.authorization.kubernetes.io/autoupdate设为false,但通常没必要。
服务账户最小权限阶梯:5 档从安全到危险
先记住命名规则:服务账户认证时的用户名是system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT),同属system:serviceaccounts和system:serviceaccounts:(NAMESPACE)两个组。这就是--group能批量授权的底层逻辑(详见 guide/authentication.md)。
从最安全到最危险,五档如下:
第 1 档:专用服务账户 + 最小角色(推荐)
kubectl create rolebinding my-sa-view --clusterrole=view \ --serviceaccount=my-namespace:my-sa --namespace=my-namespace风险:几乎没有。前提是你真的建了服务账户并让应用在 pod spec 里指定serviceAccountName。
第 2 档:给 default 服务账户发卡
kubectl create rolebinding default-view --clusterrole=view \ --serviceaccount=my-namespace:default --namespace=my-namespace风险:命名空间内没写serviceAccountName的 pod 全都继承这份权限。仓库里 manifests/heapster/heapster-rbac.yaml 和 manifests/dashboard/dashboard-rbac.yaml 就是这档的老写法——给 kube-system 里的服务账户绑cluster-admin,能跑,但权限远超监控所需。
第 3 档:命名空间内全部服务账户
kubectl create rolebinding serviceaccounts-view --clusterrole=view \ --group=system:serviceaccounts:my-namespace --namespace=my-namespace风险:新上线的应用自动拿到权限,回收时要记得清。
第 4 档:全集群服务账户只读(不鼓励)
kubectl create clusterrolebinding serviceaccounts-view --clusterrole=view \ --group=system:serviceaccounts风险:管理成本换安全边界,一个命名空间的事会外溢到全集群。
第 5 档:全集群服务账户超级用户(强烈不推荐)
kubectl create clusterrolebinding serviceaccounts-cluster-admin --clusterrole=cluster-admin \ --group=system:serviceaccounts风险:任何能读 secret 的人、任何能注入容器的人,都能借 token 拿到集群最高权限。
两道安全红线
红线一:API 层挡提权,连授权器没开都生效
RBAC API 内置一条约束:你手里没有的权限,写不进新角色和新绑定。user-1 读不了集群范围的 secret,他就创建不出"含读 secret 权限"的ClusterRole,也绑不了引用该角色的ClusterRoleBinding。检查在 API 层做,RBAC 授权器开着不开着都拦。
绑定权限可以显式授予,不要求本人拥有全部权限。给clusterroles资源加bind动词,再用resourceNames圈定范围:
kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: role-grantor-binding namespace: user-1-namespace subjects: - kind: User name: user-1 apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: role-grantor apiGroup: rbac.authorization.k8s.iokind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: role-grantor rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["rolebindings"] verbs: ["create"] - apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterroles"] verbs: ["bind"] resourceNames: ["admin", "edit", "view"]user-1 现在能把admin、edit、view绑定给他人,但绑不了cluster-admin。首次初始化第一个角色时,这个机制会形成死锁,解法是用system:masters组(默认绑定到cluster-admin)的凭证来落第一笔授权。
红线二:能读 secret,就能冒充服务账户
服务账户 token 就存在 secret 里,任何读到这个 secret 的主体都可以拿着 token 以该服务账户身份访问 API。所以两件事要同时做:给服务账户发权限时收敛动词和资源,给"读 secret"这个动作设更高的门槛。这也是view刻意屏蔽 secret 的原因。
老集群升级:ABAC 灰度切 RBAC
1.5 及更早的集群多半开着宽泛 ABAC 策略,直接换 RBAC 会把存量 workload 挡在门外。灰度路线:
- API Server 同时挂两个授权器,旧策略文件继续生效:
--authorization-mode=RBAC,ABAC --authorization-policy-file=mypolicy.jsonl请求先问 RBAC,被拒再落 ABAC,等于"任一方放行即放行"。把日志开到
--v=2,被 RBAC 拒掉的请求会打RBAC DENY:前缀,照着清单逐条补角色。日志里不再出现拒绝记录,摘掉 ABAC。
⚠️ 并行期有个陷阱:当初 ABAC 放行的请求,摘掉 ABAC 后未必有对应角色兜底。建议先拿一个命名空间灰度验证,再全量切换。
如果实在等不起,还有一条宽泛过渡绑定(practice/rbac-support-in-kubernetes.md 中有记录):
kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin \ --user=admin --user=kubelet --group=system:serviceaccounts⚠️ 这条命令等于把集群钥匙发给所有服务账户:容器里的任何应用自动持有 SA 凭据,可读 secret、可改权限。只配当过渡期止血贴,别留到生产长期运行。
收尾自检清单
| # | 检查项 | 目标状态 |
|---|---|---|
| 1 | 授权模式 | --authorization-mode=RBAC,无AlwaysAllow残留 |
| 2 | 匿名认证 | --anonymous-auth=false |
| 3 | 应用身份 | 每个应用专用 ServiceAccount,不裸奔在 default 上 |
| 4 | 动词收敛 | 读多写少的场景不给create/delete,从最小动词集起步 |
| 5 | secret 读权限 | 只发给必须方;审计谁拿着 secrets 的get |
| 6 | cluster-admin | 只给运维通道,不发全集群服务账户 |
| 7 | 系统资源 | 没手改system:前缀的 ClusterRole/Binding 和kubernetes.io/bootstrapping标记资源 |
| 8 | 升级收尾 | ABAC 已摘除,RBAC DENY:日志清零 |
参考
- concepts/rbac.md:RBAC 概念全量文档(本文骨架出处)
- concepts/serviceaccount.md:服务账户机制
- concepts/authentication-and-permission.md:认证与授权总览
- guide/authentication.md:用户与身份认证授权
- guide/kubectl-user-authentication-authorization.md:devuser 证书 + kubeconfig + 授权实战
- guide/kubernetes-security-best-practice.md:集群安全配置最佳实践
- guide/tls-bootstrapping.md:节点引导中的
system:组用法 - practice/rbac-support-in-kubernetes.md:真实集群 RBAC 落地记录
- manifests/heapster/heapster-rbac.yaml、manifests/dashboard/dashboard-rbac.yaml:加载项绑定
cluster-admin的真实清单 - tools/create-user/:批量建用户与授权脚本
【免费下载链接】kubernetes-handbookKubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南项目地址: https://gitcode.com/gh_mirrors/ku/kubernetes-handbook
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考