技术博客

Kubernetes RBAC 精细化权限管理实战:Role、ClusterRole、ServiceAccount

深入讲解 Kubernetes RBAC 权限体系:Role/ClusterRole 定义权限规则、RoleBinding/ClusterRoleBinding 绑定权限、ServiceAccount 为工作负载授权,通过多租户场景、CI/CD 最小权限、运维只读账号等生产案例,掌握 K8s 精细化访问控制。

KubernetesRBAC权限管理ServiceAccount安全多租户

Kubernetes RBAC(基于角色的访问控制)是集群安全的核心机制。默认的 cluster-admin 权限等于“root”,真实生产环境绝不能给所有人这个权限。本文从 RBAC 核心模型出发,通过真实场景演示如何精细化管理集群访问权限。

RBAC 核心模型

主体(Subject)     角色(Role/ClusterRole)    资源(Resource)
用户/服务账号   →   绑定(Binding)          →   API 对象操作权限

三种主体类型:
- User(用户):K8s 本身不管理用户,通常用证书或 OIDC 集成
- Group(组):用户分组
- ServiceAccount(服务账号):Pod 内程序使用的身份

两种角色作用域:
- Role:命名空间级别(只能授权本命名空间内的资源)
- ClusterRole:集群级别(可以授权所有命名空间 + 非命名空间资源)

一、Role 和 ClusterRole

创建 Role(命名空间级别)

# 在 production 命名空间中,允许读取 Pod 信息
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
- apiGroups: [""]          # "" 代表核心 API 组(Pod/Service/ConfigMap 等)
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods/log"]  # 子资源(允许查看日志)
  verbs: ["get"]
# 开发者角色:在 dev 命名空间可以读写大部分资源
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: dev
rules:
- apiGroups: [""]
  resources: ["pods", "services", "configmaps", "secrets", "endpoints"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets", "statefulsets", "daemonsets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["batch"]
  resources: ["jobs", "cronjobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

创建 ClusterRole(集群级别)

# 只读集群角色(可用于所有命名空间)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-readonly
rules:
- apiGroups: [""]
  resources: ["*"]         # 所有核心资源
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps", "batch", "extensions"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["nodes"]     # 节点信息(非命名空间资源,只有 ClusterRole 能授权)
  verbs: ["get", "list", "watch"]

---
# CI/CD 流水线角色(只能更新 Deployment 镜像)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: deployment-updater
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "patch", "update"]
- apiGroups: ["apps"]
  resources: ["deployments/status"]
  verbs: ["get"]

二、RoleBinding 和 ClusterRoleBinding

RoleBinding(将角色绑定到主体)

# 把 developer 角色绑定给用户 alice(在 dev 命名空间)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: alice-developer
  namespace: dev
subjects:
- kind: User
  name: alice             # 用户名(来自证书 CN 或 OIDC claims)
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: developer
  apiGroup: rbac.authorization.k8s.io
# 把 ClusterRole 限制在单个命名空间使用(RoleBinding 绑 ClusterRole)
# 注意:ClusterRole 的全局资源(nodes 等)在这种绑定下仍然无法访问
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: readonly-production
  namespace: production
subjects:
- kind: Group
  name: ops-team           # 用户组
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-readonly   # 引用 ClusterRole
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding(集群范围绑定)

# 让 monitoring 服务账号可以访问所有命名空间的 Pod 指标
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus-cluster-reader
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: monitoring    # ServiceAccount 所在的命名空间
roleRef:
  kind: ClusterRole
  name: cluster-readonly
  apiGroup: rbac.authorization.k8s.io

三、ServiceAccount:为 Pod 授权

Pod 内的程序(如 Prometheus、ArgoCD、Operator)需要调用 K8s API 时,必须使用 ServiceAccount。

创建并使用 ServiceAccount

# 创建 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-deployer
  namespace: production

---
# 给 ServiceAccount 绑定权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-deployer-role
  namespace: production
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "update", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-deployer-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: app-deployer
  namespace: production
roleRef:
  kind: Role
  name: app-deployer-role
  apiGroup: rbac.authorization.k8s.io

---
# Deployment 使用此 ServiceAccount
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: production
spec:
  template:
    spec:
      serviceAccountName: app-deployer   # 指定 ServiceAccount
      containers:
      - name: app
        image: my-app:v1.0

禁用默认 Token 自动挂载(安全加固)

# 不需要调用 API 的 Pod,禁用 ServiceAccount Token 挂载
spec:
  automountServiceAccountToken: false   # Pod 级别

# 或在 ServiceAccount 上默认禁用
apiVersion: v1
kind: ServiceAccount
metadata:
  name: no-api-access
automountServiceAccountToken: false     # SA 级别

四、生产场景实战

场景一:多团队命名空间隔离

# 为不同团队创建命名空间
kubectl create namespace team-frontend
kubectl create namespace team-backend
kubectl create namespace team-data

# 创建团队角色(可以管理本命名空间所有资源)
cat << 'EOF' | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-admin
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]
- nonResourceURLs: ["*"]
  verbs: ["*"]
EOF

# 为各团队绑定其命名空间管理权
for ns in team-frontend team-backend team-data; do
  team=$(echo $ns | sed 's/team-//')
  kubectl create rolebinding ${ns}-admin \
    --clusterrole=namespace-admin \
    --group=${team}-developers \
    --namespace=${ns}
done

场景二:CI/CD 最小权限

# ArgoCD 或 Jenkins 的部署权限:只能更新指定命名空间的 Deployment
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cicd-deployer
  namespace: cicd

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cicd-deploy
  namespace: production
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "update", "patch"]
  resourceNames: ["web-app", "api-server"]  # 进一步限制:只能操作这两个 Deployment
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]    # 可以查看 Pod 状态(确认部署是否成功)

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: cicd-deploy-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: cicd-deployer
  namespace: cicd
roleRef:
  kind: Role
  name: cicd-deploy
  apiGroup: rbac.authorization.k8s.io

场景三:运维只读账号

# 给新入职运维人员:只读访问,但可以查看日志和进入 Pod exec
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ops-readonly
rules:
# 读取所有工作负载资源
- apiGroups: ["", "apps", "batch", "extensions", "networking.k8s.io"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
# 查看 Pod 日志
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get", "list"]
# 允许 exec 进入 Pod(排查问题用,敏感环境可去掉)
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create"]
# 禁止查看 Secret 内容(虽然能看到 Secret 对象存在,但不能 get 内容)
# 注意:下面要排除 secrets

---
# 为 ops 组的所有用户绑定
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ops-team-readonly
subjects:
- kind: Group
  name: ops-team
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: ops-readonly
  apiGroup: rbac.authorization.k8s.io

场景四:Prometheus 监控权限

apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
  namespace: monitoring

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups: [""]
  resources:
  - nodes
  - nodes/proxy
  - services
  - endpoints
  - pods
  verbs: ["get", "list", "watch"]
- apiGroups: ["extensions", "networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]   # 采集节点 /metrics 端点
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus
subjects:
- kind: ServiceAccount
  name: prometheus
  namespace: monitoring
roleRef:
  kind: ClusterRole
  name: prometheus
  apiGroup: rbac.authorization.k8s.io

五、RBAC 权限验证与审计

# 检查当前用户有哪些权限
kubectl auth can-i list pods
# yes

kubectl auth can-i delete deployments -n production
# no

# 检查特定用户的权限(管理员用)
kubectl auth can-i list pods --as=alice
kubectl auth can-i list pods --as=alice --namespace=production

# 检查 ServiceAccount 的权限
kubectl auth can-i list pods \
  --as=system:serviceaccount:monitoring:prometheus

# 列出当前命名空间的所有 Role
kubectl get roles -n production
kubectl get rolebindings -n production

# 列出集群级别的角色
kubectl get clusterroles | grep -v "^system:"
kubectl get clusterrolebindings | grep -v "^system:"

# 查看某个 Role 的详细规则
kubectl describe role developer -n dev

# 查看谁有权操作 secrets(安全审计)
kubectl get rolebindings,clusterrolebindings -A -o json | \
  jq -r '.items[] | select(.roleRef.name | test("secret|admin")) | 
  "\(.kind)/\(.metadata.name) → \(.roleRef.name)"'

使用 kubectl-who-can(第三方工具)

# 安装
kubectl krew install who-can

# 查看谁能 delete pods
kubectl who-can delete pods -n production

# 查看谁能读 secrets
kubectl who-can get secrets -n production

六、常见错误与修复

# 错误:"User cannot get resource pods"
# 排查步骤:
kubectl auth can-i get pods --as=<username> -n <namespace>

# 找到绑定关系
kubectl get rolebindings -n <namespace> -o wide | grep <username>
kubectl get clusterrolebindings -o wide | grep <username>

# 检查用户属于哪些 group(证书模式下 O= 字段是 group)
kubectl config view --raw | \
  grep client-certificate-data | awk '{print $2}' | \
  base64 -d | openssl x509 -noout -subject
# subject=CN=alice, O=dev-team   ← CN=用户名, O=组

# 错误:ServiceAccount 调用 API 报 403
# 排查:
# 1. 确认 Pod 使用了正确的 ServiceAccount
kubectl get pod <pod-name> -o jsonpath='{.spec.serviceAccountName}'

# 2. 确认 ServiceAccount 有对应权限
kubectl auth can-i list pods \
  --as=system:serviceaccount:<namespace>:<sa-name>

# 3. 检查 Token 是否正确挂载
kubectl exec -it <pod-name> -- ls /var/run/secrets/kubernetes.io/serviceaccount/

RBAC 设计原则

几个实践中积累的原则:最小权限原则 — 只给真正需要的权限,宁可后期扩充,不要一开始给太多;优先用 RoleBinding + ClusterRole — 避免为每个命名空间重复创建相同的 Role;ServiceAccount 与工作负载一对一 — 不同功能的 Pod 用不同 SA,权限精确到用途;禁用默认 Token 挂载 — 不需要调用 API 的 Pod 设置 automountServiceAccountToken: false定期审计 — 用 kubectl auth can-iwho-can 周期性检查权限范围,避免权限蔓延(Permission Drift)。