从零搭建 K8s 权限体系:开发组只读 + 运维组管理(中集)
上集拆解了 RBAC 四个核心概念User / ServiceAccount / Role / Binding搞清楚了 Subject → Binding → Role 这条链路。这集中手把手搭建把概念变成能跑的命令。建议读完上集再看这篇概念定义和证书签发流程上集有详细说明。一、场景设定开发组dev-team→dev、staging命名空间有只读权限运维组ops-team→ 所有命名空间管理权限日志采集 ServiceAccount→prod命名空间 Pod 发现 节点信息采集下面分三个案例由浅入深先从最简单的单独给一个用户授权讲起再升级到给组授权的生产实战最后讲给程序ServiceAccount授权——对应上面三个场景设定。二、实战案例一单独给用户授权对应场景设定里的「新同事入职」——先从最简单的单个 User 讲起理解 RBAC 三元组怎么串起来。新同事 zhangsan 入职开发需要在dev和staging有只读权限。完整链路签发证书认证→ 创建 Role授权→ RoleBinding关联→ 验证。1.1 签发证书——搞定你是谁详细的证书签发流程参考上集「二.1.2 生产实战」关键点CSR 里CNzhangsan就是用户名RBAC 绑定时用kind: Username: zhangsan匹配。catzhangsan-csr.jsonEOF { CN: zhangsan, key: { algo: rsa, size: 2048 }, names: [{ C: CN, ST: BeiJing, L: BeiJing, OU: System }] } EOF# 找 CA → 签发 → 生成 kubeconfig参考上集二.1.2 第 1-4 步拿到zhangsan.kubeconfig后用户已经能连上集群——但kubectl get pods会报 403因为还没授权。1.2 创建 Role RoleBinding——搞定能干啥# rbac-zhangsan.yamlcatrbac-zhangsan.yaml EOF# 1. dev 命名空间的只读 RoleapiVersion:rbac.authorization.k8s.io/v1kind:Rolemetadata:name:dev-readernamespace:devrules:-apiGroups:[]resources:[pods,services,configmaps]verbs:[get,list,watch]-apiGroups:[apps]resources:[deployments,replicasets]verbs:[get,list,watch]---# 2. RoleBinding把 User zhangsan 绑到 dev-reader角色apiVersion:rbac.authorization.k8s.io/v1kind:RoleBindingmetadata:name:zhangsan-dev-readernamespace:devsubjects:-kind:User# ← 注意绑的是 User不是 Groupname:zhangsan# 匹配证书 CN 字段apiGroup:rbac.authorization.k8s.ioroleRef:kind:Rolename:dev-readerapiGroup:rbac.authorization.k8s.io---# 3. staging 命名空间也要创建同名 RoleRole 是命名空间级资源跨 ns 引用不到apiVersion:rbac.authorization.k8s.io/v1kind:Rolemetadata:name:dev-readernamespace:stagingrules:-apiGroups:[]resources:[pods,services,configmaps]verbs:[get,list,watch]-apiGroups:[apps]resources:[deployments,replicasets]verbs:[get,list,watch]---# 4. staging 的 RoleBindingapiVersion:rbac.authorization.k8s.io/v1kind:RoleBindingmetadata:name:zhangsan-staging-readernamespace:stagingsubjects:-kind:Username:zhangsanapiGroup:rbac.authorization.k8s.ioroleRef:kind:Rolename:dev-readerapiGroup:rbac.authorization.k8s.io EOFkubectl apply-frbac-zhangsan.yaml1.3 验证kubectl auth can-i get pods-ndev--aszhangsan# → yeskubectl auth can-i delete pods-ndev--aszhangsan# → no——Role 里没有 deletekubectl auth can-i get pods-nprod--aszhangsan# → no——prod 没授权 这种方式的缺点每来一个新同事都要写一套 RoleBinding。人多了之后 YAML 里全是一堆 User 绑定改都改不过来。解决办法——案例二。三、实战案例二给组授权——开发组只读 运维组管理3.1 核心原理证书的O字段 RBAC 的 Group name生产环境通过 Group 管权限不针对单个 User。组名从哪里来——证书签发时的O字段。# zhangsan开发{CN:zhangsan,O:dev-team# ← 这个 O 值就是 RBAC 的 Group name}# lisi运维{CN:lisi,O:ops-team}签发证书后kubeconfig 里的客户端证书自动带上Odev-team无需额外配置。角色CSR 里O字段权限范围开发dev-teamdev、staging 只读运维ops-team全集群管理3.2 新人入职签发证书以 lisi 为例原理清楚了下面实际操作一把。假设运维组新来一个同事 lisi给他签发证书——Oops-team签完 kubeconfig 里自动带上组信息。#编写证书签名请求CSRcatlisi-csr.jsonEOF { CN: lisi, hosts: [], key: { algo: rsa, size: 2048 }, names: [ { C: CN, ST: BeiJing, L: BeiJing, O: ops-team, OU: System } ] } EOF#用集群 CA 签发客户端证书cfssl gencert\-ca/etc/kubernetes/ssl/ca.pem\-ca-key/etc/kubernetes/ssl/ca-key.pem\-configca-config.json\-profilekubernetes lisi-csr.json|cfssljson-barelisi#生成 kubeconfig 文件# 4a. 写入集群信息kubectl config set-cluster kubernetes\--certificate-authority/etc/kubernetes/ssl/ca.pem\--embed-certstrue\--serverhttps://192.168.91.254:6443\--kubeconfiglisi.kubeconfig# 4b. 写入用户凭证kubectl config set-credentials lisi\--client-certificatelisi.pem\--client-keylisi-key.pem\--embed-certstrue\--kubeconfiglisi.kubeconfig# 4c. 创建上下文关联用户 集群kubectl config set-context lisikubernetes\--clusterkubernetes\--userlisi\--kubeconfiglisi.kubeconfig# 4d. 设为默认上下文kubectl config use-context lisikubernetes\--kubeconfiglisi.kubeconfig注意这时 RBAC 一个字符都不用动。RoleBinding/ClusterRoleBinding 绑的是 Groupops-team不是 lisi 这个人。lisi 证书签完自动继承运维组全部权限。3.3 开发组dev 和 staging 只读# rbac-dev-group.yamlcatrbac-dev-group.yaml EOF# 1. 创建只读 ClusterRole可被多个 命名空间 复用apiVersion:rbac.authorization.k8s.io/v1kind:ClusterRolemetadata:name:read-onlyrules:-apiGroups:[]resources:[pods,services,configmaps,secrets,endpoints]verbs:[get,list,watch]-apiGroups:[apps]resources:[deployments,replicasets,statefulsets,daemonsets]verbs:[get,list,watch]---# 2和3 RoleBinding把 dev-team 组绑定到 dev 命名空间和stagingapiVersion:rbac.authorization.k8s.io/v1kind:RoleBindingmetadata:name:dev-team-readonlynamespace:devsubjects:-kind:Groupname:dev-team# ← 组名匹配证书 Odev-teamapiGroup:rbac.authorization.k8s.ioroleRef:kind:ClusterRolename:read-onlyapiGroup:rbac.authorization.k8s.io---# 3. staging 也绑一次apiVersion:rbac.authorization.k8s.io/v1kind:RoleBindingmetadata:name:dev-team-readonlynamespace:stagingsubjects:-kind:Groupname:dev-teamapiGroup:rbac.authorization.k8s.ioroleRef:kind:ClusterRolename:read-onlyapiGroup:rbac.authorization.k8s.io EOFkubectl apply-frbac-dev-group.yaml3.4 运维组全集群管理# rbac-ops-group.yamlcatrbac-ops-group.yaml EOF# 利用 K8s 内置的 cluster-admin直接 ClusterRoleBindingapiVersion:rbac.authorization.k8s.io/v1kind:ClusterRoleBindingmetadata:name:ops-team-adminsubjects:-kind:Groupname:ops-team# ← 证书里 Oops-team 的人自动获得apiGroup:rbac.authorization.k8s.ioroleRef:kind:ClusterRolename:cluster-admin# K8s 内置超级管理员apiGroup:rbac.authorization.k8s.io EOFkubectl apply-frbac-ops-group.yaml如果运维只需特定权限如管理 Node、PV但不能删 Namespace可自定义 ClusterRole 替代cluster-admin按最小权限原则来。3.5 验证# 开发 zhangsandev staging 可读prod 进不去kubectl auth can-i get pods-ndev--aszhangsan# → yeskubectl auth can-i get pods-nstaging--aszhangsan# → yeskubectl auth can-i get pods-nprod--aszhangsan# → nokubectl auth can-i delete pods-ndev--aszhangsan# → no# 运维 lisi——注意要用 KUBECONFIG 或 --as-group不能只用 --as# ⚠️ --as lisi 只模拟 User lisi不会带上证书里的 Group ops-team# 运维的权限是通过 Group 绑定的直接用 lisi 的 kubeconfig 最准确KUBECONFIGlisi.kubeconfig kubectl auth can-i get pods-nprod# → yesKUBECONFIGlisi.kubeconfig kubectl auth can-i delete pods-ndev# → yesKUBECONFIGlisi.kubeconfig kubectl auth can-i get nodes# → yes# 或者用 --as --as-group 显式指定组kubectl auth can-i get pods-nprod--aslisi --as-group ops-team# → yes 关键区别案例一绑的是kind: User--as zhangsan就够了。案例二绑的是kind: Group--as lisi只模拟 User 身份、不带上 Group必须加--as-group ops-team或直接用 kubeconfig 文件验证。3.6 核心价值人动权限不动王五入职开发组 ① 写 CSROdev-team ② cfssl 签发 → 生成 kubeconfig ③ 结束。RBAC 一个字符都不用改 → 因为 RoleBinding 绑的是 Group dev-team不是 User 王五 王五转岗到运维 ① 重新签发证书O 改成 ops-team ② 权限自动从只读变管理 ③ 还是不用改 RBAC生产铁律永远通过 Group 分配权限。证书里的O字段就是天然的 RBAC Group。四、实战案例三给程序授权ServiceAccount前面两个案例讲的都是人的身份User Group日常kubectl操作靠它们。但集群里跑的日志/监控 AgentFilebeat、Prometheus是程序自动跑的——没有真人敲命令用的是ServiceAccount。4.1 创建 SA 授权以 Filebeat 为例它的 DaemonSet 跑在每个节点上需要发现新 Pod → 拿到 Pod 元数据标签、ns→ 给日志打标才能送到 Elasticsearch。这些操作全是调 K8s APIdefault SA 零权限直接 403。按最小权限原则只给刚好够的# log-collector-sa.yaml —— 一个文件三段SA → Role → RoleBinding 一步到位catlog-collector-sa.yaml EOF# 第 1 段创建 ServiceAccount apiVersion:v1kind:ServiceAccountmetadata:name:log-collectornamespace:prod# SA 是命名空间级日志 Agent 部署在哪个 ns 就建哪个 ns---# 第 2 段定义权限 apiVersion:rbac.authorization.k8s.io/v1kind:Rolemetadata:name:log-readernamespace:prodrules:-apiGroups:[]# 核心 API 组Pod/Node 在这resources:[pods]verbs:[get,list,watch]# get/list拉取当前 Pod 列表拿到 Pod 名、命名空间、标签等元数据# watch监听 Pod 增删新 Pod 一创建 Filebeat 马上开始采日志-apiGroups:[]resources:[nodes]verbs:[get,list]# 拿到节点名、IP 等信息日志记录里能标注这条日志来自哪个节点---# 第 3 段绑定 SA 到 Role apiVersion:rbac.authorization.k8s.io/v1kind:RoleBindingmetadata:name:log-collectornamespace:prodsubjects:-kind:ServiceAccountname:log-collectornamespace:prodroleRef:kind:Rolename:log-readerapiGroup:rbac.authorization.k8s.io EOFkubectl apply-flog-collector-sa.yaml到这里 SA 和权限就位了——log-collector这个ServiceAccount能看 Pod 和 Node 信息但不能改任何东西。但 Pod 怎么用这个 SA 呢往下看。4.2 Pod 如何使用 SADaemonSet 里serviceAccountName一行搞定K8s 自动把凭证挂进每个节点的日志采集 Pod# filebeat-daemonset.yamlcatfilebeat-daemonset.yaml EOFapiVersion:apps/v1kind:DaemonSetmetadata:name:filebeatnamespace:prodspec:selector:matchLabels:app:filebeattemplate:metadata:labels:app:filebeatspec:serviceAccountName:log-collector# ← 一行指定身份不写默认用 default零权限containers:-name:filebeatimage:docker.elastic.co/beats/filebeat:8.15.0 EOF kubectl apply-f filebeat-daemonset.yamlK8s 调度这个 Pod 时自动做三件事——把三个关键文件挂进容器文件路径用途Token/var/run/secrets/.../token认证凭证Filebeat 拿它调 API 证明我是 log-collectorCA 证书/var/run/secrets/.../ca.crt验证 API Server 身份防中间人命名空间/var/run/secrets/.../namespace知道自己在哪个 nsFilebeat 进程启动后读这三个文件就能调 K8s API 拉 Pod 列表、监听 Pod 变化。用 default SA 的话——刚启动就 403日志采集直接废掉。那什么时候需要自定义 SA、什么时候用 default 就行Pod 类型用哪个 SA原因纯业务 PodNginx、MySQL、Redis、Java 服务default甚至关掉 Token 挂载不调 K8s API不需要任何权限CI/CD 流水线GitLab Runner、Jenkins Agent自定义 SA如cicd-deployer要 update Deployment、create ConfigMap日志/监控采集Filebeat、Prometheus自定义 SA需要 get/watch pods、nodes 做服务发现Operator / Controllercert-manager、etcd-operator自定义 SA需要 CRUD 自定义资源Ingress Controllernginx-ingress自定义 SA需要 get/watch services、endpoints、secrets外部密钥同步External Secrets Operator自定义 SA需要 create/update secrets备份工具Velero自定义 SA需要 get/list 全集群资源自己写的调 API 的应用自定义 SA按需给如查 Pod 列表、动态创建 Job一句话Pod 里程序只要curl https://kubernetes.default.svc调 K8s API就不能用 default零权限必须自定义 SA 绑 Role。反过来跑纯业务的 Pod 用 default 完全够更建议设automountServiceAccountToken: false关掉 Token 挂载——安全又少一个攻击面。4.3 获取 SA 的 Token给集群外工具用Pod 内程序自动有 Token但有些场景需要在集群外用这个 SA——比如调试时从本机 curl API 拉 Pod 列表# 一条命令生成 Token直接打印到终端kubectl create token log-collector-nprod--duration720h输出就是一长串 JWT 字符串拿来当 Bearer Token 用。版本说明kubectl create token需要 kubectl ≥ 1.24。如果 kubectl 是 1.23create token还不存在但服务器是 1.24不再自动生成 Secret就手动建一个 Secret 兜底。那什么时候需要手动拿 Token场景需要手动拿原因Pod 里用这个 SAserviceAccountName: log-collector❌K8s 自动把 Token 注入容器程序直接读文件即可集群外工具用这个 SA本机脚本、调试✅没有 Pod、没有自动挂载得先create token拿到凭证一句话SA 是身份注册Token 是出门证。在集群内Pod自动发证在集群外笔记本得自己领。4.4 验证 SA 权限——从两个角度测一遍角度一集群内测试kubectl auth can-iSA 在 K8s 内部的完整身份是system:serviceaccount:ns:sa-name# Filebeat 能不能看 Pod 列表预期 yeskubectl auth can-i get pods-nprod\--assystem:serviceaccount:prod:log-collector# → yes# Filebeat 能不能删 Pod预期 no——只读 SA不该有删除权限kubectl auth can-i delete pods-nprod\--assystem:serviceaccount:prod:log-collector# → no# Filebeat 能不能操作 Deployment预期 no——日志采集碰不到 Deploymentkubectl auth can-i get deployments-nprod\--assystem:serviceaccount:prod:log-collector# → no# 列出 log-collector 的全部权限kubectl auth can-i--list-nprod\--assystem:serviceaccount:prod:log-collector# Resources Verbs# pods [get list watch]# nodes [get list]# ↑ 就两行干净利落。没有 delete、没有 create、没有 update。# 日志 Agent 只需要看不需要改任何东西。角度二集群外测试curl 模拟本机脚本从其他服务器模拟不带 Token 和带上 Token 各调一次 API# ① 不带 Token → 401API Server 不知道你是谁curl-khttps://192.168.91.254:6443/api/v1/namespaces/prod/pods# {status: Failure, message: Unauthorized, code: 401}# ② 带上 Token → 200log-reader Role 允许 get podsTOKEN$(kubectl create token log-collector-nprod--duration720h)curl-k-HAuthorization: Bearer$TOKEN\https://192.168.91.254:6443/api/v1/namespaces/prod/pods# 返回 prod 下 Pod 列表 JSON ← 认证 授权都通过# ③ 有 Token 但越权 → 403认证过了权限不够curl-k-HAuthorization: Bearer$TOKEN\-XDELETE\https://192.168.91.254:6443/api/v1/namespaces/prod/pods/some-pod# {status: Failure, message: Forbidden, code: 403}三条命令串起完整认知没 Token → 401不知道你是谁、有 Token → 200认证授权通过、越权操作 → 403认证通过但权限不够。Token 管认证Role 管授权各干各的。SA identity 格式速记system:serviceaccount:ns:sa-name。所有 SA 自动属于system:serviceaccounts这个 Group可按此做统一授权。例如给所有命名空间的 SA 批量开只读subjects: - kind: Group name: system:serviceaccounts apiGroup: rbac.authorization.k8s.io五、总结RBAC 三条链路缺一不可身份User/SA→ 绑定RoleBinding→ 权限Role断任何一条都是 403。生产永远用 Group 授权新人入职只改证书 O 字段RBAC 一个字不用动百人团队 RoleBinding 还是那几行。SA 是程序的身份不是人的替代品人用 kubectl 走证书程序调 API 走 SA Token两回事。配完权限必须验证kubectl auth can-i查集群内curl Token查集群外别猜。中集小结三个案例走完了从零搭建权限体系的完整流程。案例一理解三元组案例二是生产真玩法——Group 授权新人入职只签发证书、RBAC 不用动案例三讲程序身份 ServiceAccount——Pod 里的日志/监控采集器怎么安全调 K8s API。下集预告「K8s RBAC 进阶避坑指南」——最小权限原则、聚合 ClusterRole、SA Token 1.24 变化、安全审计巡检、apiGroups 排坑、6 条踩坑经验 落地清单。