上集拆解了 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 里 CN=zhangsan 就是用户名,RBAC 绑定时用 kind: User,name: zhangsan 匹配。
bash
cat > zhangsan-csr.json <<EOF
{
"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------搞定"能干啥"
yaml
# rbac-zhangsan.yaml
cat > rbac-zhangsan.yaml <<EOF
# 1. dev 命名空间的只读 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-reader
namespace: dev
rules:
- 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/v1
kind: RoleBinding
metadata:
name: zhangsan-dev-reader
namespace: dev
subjects:
- kind: User # ← 注意:绑的是 User,不是 Group
name: zhangsan # 匹配证书 CN 字段
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-reader
apiGroup: rbac.authorization.k8s.io
---
# 3. staging 命名空间也要创建同名 Role(Role 是命名空间级资源,跨 ns 引用不到)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-reader
namespace: staging
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
---
# 4. staging 的 RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: zhangsan-staging-reader
namespace: staging
subjects:
- kind: User
name: zhangsan
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-reader
apiGroup: rbac.authorization.k8s.io
EOF
bash
kubectl apply -f rbac-zhangsan.yaml
1.3 验证
bash
kubectl auth can-i get pods -n dev --as zhangsan # → yes
kubectl auth can-i delete pods -n dev --as zhangsan # → no------Role 里没有 delete
kubectl auth can-i get pods -n prod --as zhangsan # → no------prod 没授权


💡 这种方式的缺点:每来一个新同事,都要写一套 RoleBinding。人多了之后 YAML 里全是一堆 User 绑定,改都改不过来。解决办法------案例二。
三、实战案例二:给组授权------开发组只读 + 运维组管理
3.1 核心原理:证书的 O 字段 = RBAC 的 Group name
生产环境通过 Group 管权限,不针对单个 User 。组名从哪里来?------证书签发时的 O 字段。
bash
# zhangsan(开发)
{
"CN": "zhangsan",
"O": "dev-team" # ← 这个 O 值就是 RBAC 的 Group name
}
# lisi(运维)
{
"CN": "lisi",
"O": "ops-team"
}
签发证书后,kubeconfig 里的客户端证书自动带上 O=dev-team,无需额外配置。
| 角色 | CSR 里 O 字段 |
权限范围 |
|---|---|---|
| 开发 | dev-team |
dev、staging 只读 |
| 运维 | ops-team |
全集群管理 |
3.2 新人入职:签发证书(以 lisi 为例)
原理清楚了,下面实际操作一把。假设运维组新来一个同事 lisi,给他签发证书------O=ops-team,签完 kubeconfig 里自动带上组信息。
bash
#编写证书签名请求(CSR)
cat > lisi-csr.json <<EOF
{
"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 \
-config=ca-config.json \
-profile=kubernetes lisi-csr.json | cfssljson -bare lisi
#生成 kubeconfig 文件
# 4a. 写入集群信息
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/ssl/ca.pem \
--embed-certs=true \
--server=https://192.168.91.254:6443 \
--kubeconfig=lisi.kubeconfig
# 4b. 写入用户凭证
kubectl config set-credentials lisi \
--client-certificate=lisi.pem \
--client-key=lisi-key.pem \
--embed-certs=true \
--kubeconfig=lisi.kubeconfig
# 4c. 创建上下文(关联用户 + 集群)
kubectl config set-context lisi@kubernetes \
--cluster=kubernetes \
--user=lisi \
--kubeconfig=lisi.kubeconfig
# 4d. 设为默认上下文
kubectl config use-context lisi@kubernetes \
--kubeconfig=lisi.kubeconfig
注意:这时 RBAC 一个字符都不用动。RoleBinding/ClusterRoleBinding 绑的是 Group
ops-team,不是 lisi 这个人。lisi 证书签完,自动继承运维组全部权限。
3.3 开发组:dev 和 staging 只读
yaml
# rbac-dev-group.yaml
cat > rbac-dev-group.yaml <<EOF
# 1. 创建只读 ClusterRole(可被多个 命名空间 复用)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: read-only
rules:
- 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 命名空间和staging
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-team-readonly
namespace: dev
subjects:
- kind: Group
name: dev-team # ← 组名,匹配证书 O=dev-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: read-only
apiGroup: rbac.authorization.k8s.io
---
# 3. staging 也绑一次
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-team-readonly
namespace: staging
subjects:
- kind: Group
name: dev-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: read-only
apiGroup: rbac.authorization.k8s.io
EOF
bash
kubectl apply -f rbac-dev-group.yaml
3.4 运维组:全集群管理
yaml
# rbac-ops-group.yaml
cat > rbac-ops-group.yaml <<EOF
# 利用 K8s 内置的 cluster-admin,直接 ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ops-team-admin
subjects:
- kind: Group
name: ops-team # ← 证书里 O=ops-team 的人自动获得
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin # K8s 内置超级管理员
apiGroup: rbac.authorization.k8s.io
EOF
bash
kubectl apply -f rbac-ops-group.yaml
如果运维只需特定权限(如管理 Node、PV,但不能删 Namespace),可自定义 ClusterRole 替代
cluster-admin,按最小权限原则来。
3.5 验证
bash
# 开发 zhangsan:dev + staging 可读,prod 进不去
kubectl auth can-i get pods -n dev --as zhangsan # → yes
kubectl auth can-i get pods -n staging --as zhangsan # → yes
kubectl auth can-i get pods -n prod --as zhangsan # → no
kubectl auth can-i delete pods -n dev --as zhangsan # → no
# 运维 lisi------注意要用 KUBECONFIG 或 --as-group,不能只用 --as
# ⚠️ --as lisi 只模拟 User "lisi",不会带上证书里的 Group "ops-team"
# 运维的权限是通过 Group 绑定的,直接用 lisi 的 kubeconfig 最准确:
KUBECONFIG=lisi.kubeconfig kubectl auth can-i get pods -n prod # → yes
KUBECONFIG=lisi.kubeconfig kubectl auth can-i delete pods -n dev # → yes
KUBECONFIG=lisi.kubeconfig kubectl auth can-i get nodes # → yes
# 或者用 --as + --as-group 显式指定组
kubectl auth can-i get pods -n prod --as lisi --as-group ops-team # → yes

🔑 关键区别 :案例一绑的是
kind: User,--as zhangsan就够了。案例二绑的是kind: Group,--as lisi只模拟 User 身份、不带上 Group,必须加--as-group ops-team或直接用 kubeconfig 文件验证。
3.6 核心价值:人动权限不动
bash
王五入职开发组:
① 写 CSR,O=dev-team
② cfssl 签发 → 生成 kubeconfig
③ 结束。RBAC 一个字符都不用改
→ 因为 RoleBinding 绑的是 Group dev-team,不是 User 王五
王五转岗到运维:
① 重新签发证书,O 改成 ops-team
② 权限自动从只读变管理
③ 还是不用改 RBAC
生产铁律 :永远通过 Group 分配权限。证书里的
O字段就是天然的 RBAC Group。
四、实战案例三:给程序授权(ServiceAccount)
前面两个案例讲的都是"人"的身份(User + Group),日常 kubectl 操作靠它们。但集群里跑的日志/监控 Agent(Filebeat、Prometheus)是程序自动跑的------没有真人敲命令,用的是 ServiceAccount。
4.1 创建 SA + 授权
以 Filebeat 为例:它的 DaemonSet 跑在每个节点上,需要发现新 Pod → 拿到 Pod 元数据(标签、ns)→ 给日志打标才能送到 Elasticsearch。这些操作全是调 K8s API,default SA 零权限,直接 403。按最小权限原则,只给刚好够的:
yaml
# log-collector-sa.yaml ------ 一个文件三段,SA → Role → RoleBinding 一步到位
cat > log-collector-sa.yaml << EOF
# ===== 第 1 段:创建 ServiceAccount =====
apiVersion: v1
kind: ServiceAccount
metadata:
name: log-collector
namespace: prod # SA 是命名空间级,日志 Agent 部署在哪个 ns 就建哪个 ns
---
# ===== 第 2 段:定义权限 =====
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: log-reader
namespace: prod
rules:
- 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/v1
kind: RoleBinding
metadata:
name: log-collector
namespace: prod
subjects:
- kind: ServiceAccount
name: log-collector
namespace: prod
roleRef:
kind: Role
name: log-reader
apiGroup: rbac.authorization.k8s.io
EOF
bash
kubectl apply -f log-collector-sa.yaml

到这里 SA 和权限就位了------log-collector 这个ServiceAccount能看 Pod 和 Node 信息,但不能改任何东西。但 Pod 怎么用这个 SA 呢?往下看。
4.2 Pod 如何使用 SA
DaemonSet 里 serviceAccountName 一行搞定,K8s 自动把凭证挂进每个节点的日志采集 Pod
yaml
# filebeat-daemonset.yaml
cat > filebeat-daemonset.yaml << EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
namespace: prod
spec:
selector:
matchLabels:
app: filebeat
template:
metadata:
labels:
app: filebeat
spec:
serviceAccountName: log-collector # ← 一行指定身份,不写默认用 default(零权限)
containers:
- name: filebeat
image: docker.elastic.co/beats/filebeat:8.15.0
EOF
kubectl apply -f filebeat-daemonset.yaml

K8s 调度这个 Pod 时自动做三件事------把三个关键文件挂进容器:
| 文件 | 路径 | 用途 |
|---|---|---|
| Token | /var/run/secrets/.../token |
认证凭证,Filebeat 拿它调 API 证明"我是 log-collector" |
| CA 证书 | /var/run/secrets/.../ca.crt |
验证 API Server 身份,防中间人 |
| 命名空间 | /var/run/secrets/.../namespace |
知道自己在哪个 ns |
Filebeat 进程启动后读这三个文件,就能调 K8s API 拉 Pod 列表、监听 Pod 变化。用 default SA 的话------刚启动就 403,日志采集直接废掉。
那什么时候需要自定义 SA、什么时候用 default 就行?
| Pod 类型 | 用哪个 SA | 原因 |
|---|---|---|
| 纯业务 Pod(Nginx、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 / Controller(cert-manager、etcd-operator) | 自定义 SA | 需要 CRUD 自定义资源 |
| Ingress Controller(nginx-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 列表:
bash
# 一条命令生成 Token,直接打印到终端
kubectl create token log-collector -n prod --duration=720h
输出就是一长串 JWT 字符串,拿来当 Bearer Token 用。
版本说明 :
kubectl create token需要 kubectl ≥ 1.24。如果 kubectl 是 1.23(create token还不存在),但服务器是 1.24+(不再自动生成 Secret),就手动建一个 Secret 兜底。
那什么时候需要手动拿 Token?
| 场景 | 需要手动拿? | 原因 |
|---|---|---|
Pod 里用这个 SA(serviceAccountName: log-collector) |
❌ | K8s 自动把 Token 注入容器,程序直接读文件即可 |
| 集群外工具用这个 SA(本机脚本、调试) | ✅ | 没有 Pod、没有自动挂载,得先 create token 拿到凭证 |
一句话:SA 是身份注册,Token 是出门证。在集群内(Pod)自动发证,在集群外(笔记本)得自己领。
4.4 验证 SA 权限------从两个角度测一遍
角度一:集群内测试(kubectl auth can-i)
SA 在 K8s 内部的完整身份是 system:serviceaccount:<ns>:<sa-name>:
bash
# Filebeat 能不能看 Pod 列表?(预期 yes)
kubectl auth can-i get pods -n prod \
--as system:serviceaccount:prod:log-collector # → yes
# Filebeat 能不能删 Pod?(预期 no------只读 SA,不该有删除权限)
kubectl auth can-i delete pods -n prod \
--as system:serviceaccount:prod:log-collector # → no
# Filebeat 能不能操作 Deployment?(预期 no------日志采集碰不到 Deployment)
kubectl auth can-i get deployments -n prod \
--as system:serviceaccount:prod:log-collector # → no
# 列出 log-collector 的全部权限
kubectl auth can-i --list -n prod \
--as system:serviceaccount:prod:log-collector
# Resources Verbs
# pods [get list watch]
# nodes [get list]
# ↑ 就两行,干净利落。没有 delete、没有 create、没有 update。
# 日志 Agent 只需要"看",不需要改任何东西。

角度二:集群外测试(curl 模拟本机脚本)
从其他服务器模拟:不带 Token 和带上 Token 各调一次 API:
sh
# ① 不带 Token → 401,API Server 不知道你是谁
curl -k https://192.168.91.254:6443/api/v1/namespaces/prod/pods
# {"status": "Failure", "message": "Unauthorized", "code": 401}
# ② 带上 Token → 200,log-reader Role 允许 get pods
TOKEN=$(kubectl create token log-collector -n prod --duration=720h)
curl -k -H "Authorization: Bearer $TOKEN" \
https://192.168.91.254:6443/api/v1/namespaces/prod/pods
# 返回 prod 下 Pod 列表 JSON ← 认证 + 授权都通过
# ③ 有 Token 但越权 → 403,认证过了,权限不够
curl -k -H "Authorization: Bearer $TOKEN" \
-X DELETE \
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 批量开只读:
sh
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 条踩坑经验 + 落地清单。