RBAC 授权机制深度解析

RBAC 授权机制深度解析

快速参考

核心概念速览

  • Role: 命名空间级别的权限集合
  • ClusterRole: 集群级别的权限集合
  • RoleBinding: 将 Role 绑定到用户/组/ServiceAccount
  • ClusterRoleBinding: 将 ClusterRole 绑定到用户/组/ServiceAccount

常用命令

# 检查权限
kubectl auth can-i create pods --as=alice
kubectl auth can-i delete deployments --as=alice -n production

# 列出所有权限
kubectl auth can-i --list -n production

# 查看角色
kubectl get roles,clusterroles
kubectl describe role <role-name>

RBAC 概述

RBAC (Role-Based Access Control) 是 Kubernetes 中最常用的授权机制,通过角色和绑定来控制用户或服务账户的权限。

RBAC 模型

┌──────────────────────────────────────────────┐
│              RBAC 模型                        │
├──────────────────────────────────────────────┤
│                                              │
│  Subject (主体)                              │
│  ├─ User (用户)                              │
│  ├─ Group (组)                               │
│  └─ ServiceAccount (服务账户)                │
│           │                                   │
│           ▼                                   │
│  RoleBinding / ClusterRoleBinding            │
│  (绑定关系)                                   │
│           │                                   │
│           ▼                                   │
│  Role / ClusterRole                          │
│  (权限集合)                                   │
│  ├─ API Groups                               │
│  ├─ Resources                                │
│  └─ Verbs (get, list, create...)            │
│                                              │
└──────────────────────────────────────────────┘

RBAC 资源类型

1. Role(命名空间级别)

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""]  # "" 表示 core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list"]
  resourceNames: ["nginx"]  # 限制特定资源名称

2. ClusterRole(集群级别)

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-reader
rules:
- apiGroups: [""]
  resources: ["nodes", "persistentvolumes"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list"]
- apiGroups: ["apps"]
  resources: ["deployments", "daemonsets", "statefulsets"]
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics", "/healthz"]
  verbs: ["get"]

3. RoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# 用户
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
# 组
- kind: Group
  name: developers
  apiGroup: rbac.authorization.k8s.io
# ServiceAccount
- kind: ServiceAccount
  name: app-sa
  namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

4. ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cluster-admin-binding
subjects:
- kind: User
  name: admin
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

授权流程源码分析

API Server 授权检查

// k8s.io/apiserver/pkg/endpoints/filters/authorization.go

func WithAuthorization(handler http.Handler, a authorizer.Authorizer) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        ctx := req.Context()
        
        // 1. 获取请求属性
        attributes := authorizer.AttributesRecord{
            User:            authenticatedUser,
            Verb:            verb,              // get, list, create, etc.
            Namespace:       namespace,
            APIGroup:        apiGroup,
            APIVersion:      apiVersion,
            Resource:        resource,
            Subresource:     subresource,
            Name:            name,
            ResourceRequest: isResourceRequest,
            Path:            req.URL.Path,
        }
        
        // 2. 执行授权检查
        authorized, reason, err := a.Authorize(ctx, attributes)
        
        // 3. 处理授权结果
        if authorized != authorizer.DecisionAllow {
            http.Error(w, fmt.Sprintf("Forbidden: %s", reason), http.StatusForbidden)
            return
        }
        
        // 4. 授权通过,继续处理请求
        handler.ServeHTTP(w, req)
    })
}

RBAC 授权器实现

// k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go

type RBACAuthorizer struct {
    authorizationRuleResolver *rbacregistryvalidation.AuthorizationRuleResolver
}

func (r *RBACAuthorizer) Authorize(ctx context.Context, requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
    // 1. 获取用户信息
    user := requestAttributes.GetUser()
    
    // 2. 收集用户的所有角色
    rules, err := r.GetRoleReferenceRules(user, requestAttributes.GetNamespace())
    if err != nil {
        return authorizer.DecisionNoOpinion, "", err
    }
    
    // 3. 检查是否有匹配的规则
    if RulesAllow(requestAttributes, rules...) {
        return authorizer.DecisionAllow, "", nil
    }
    
    return authorizer.DecisionNoOpinion, "no matching rule found", nil
}

// 检查规则是否匹配
func RulesAllow(requestAttributes authorizer.Attributes, rules ...rbacv1.PolicyRule) bool {
    for _, rule := range rules {
        if RuleAllows(requestAttributes, rule) {
            return true
        }
    }
    return false
}

func RuleAllows(requestAttributes authorizer.Attributes, rule rbacv1.PolicyRule) bool {
    // 1. 检查 API Group
    if !apiGroupMatches(&rule, requestAttributes.GetAPIGroup()) {
        return false
    }
    
    // 2. 检查 Resource
    if !resourceMatches(&rule, requestAttributes.GetResource(), requestAttributes.GetSubresource()) {
        return false
    }
    
    // 3. 检查 Verb
    if !verbMatches(&rule, requestAttributes.GetVerb()) {
        return false
    }
    
    // 4. 检查 ResourceName(如果指定)
    if !resourceNameMatches(&rule, requestAttributes.GetName()) {
        return false
    }
    
    return true
}

内置 ClusterRole

查看内置角色

# 查看所有 ClusterRole
kubectl get clusterroles

# 常用内置 ClusterRole
kubectl get clusterrole cluster-admin -o yaml
kubectl get clusterrole edit -o yaml
kubectl get clusterrole view -o yaml

关键内置角色

ClusterRole 权限范围 适用场景
cluster-admin 完全控制权限 集群管理员
admin 命名空间管理权限 命名空间管理员
edit 读写权限(不包括 Role/RoleBinding) 开发者
view 只读权限 只读用户
system:node Node 组件权限 kubelet
system:kube-scheduler Scheduler 权限 kube-scheduler

ServiceAccount RBAC

创建 ServiceAccount

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: default

绑定权限

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]
  resourceNames: ["app-config"]

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

Pod 使用 ServiceAccount

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
  namespace: default
spec:
  serviceAccountName: app-sa
  containers:
  - name: app
    image: myapp

在 Pod 中访问 API Server

# ServiceAccount Token 自动挂载到 Pod
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
APISERVER=https://kubernetes.default.svc

# 使用 Token 访问 API
curl --cacert $CACERT --header "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/default/pods

权限聚合

Aggregate ClusterRole

# 定义一个聚合角色
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-role
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
rules:
- apiGroups: [""]
  resources: ["pods", "services", "endpoints"]
  verbs: ["get", "list", "watch"]

---
# 聚合规则
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: []  # 自动从匹配的 ClusterRole 聚合

扩展内置角色

# 扩展 view 角色
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: view-custom-resources
  labels:
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["mycompany.com"]
  resources: ["myresources"]
  verbs: ["get", "list", "watch"]

权限审计

检查用户权限

# 检查当前用户是否有某个权限
kubectl auth can-i create deployments
kubectl auth can-i list pods --namespace=default

# 检查指定用户的权限
kubectl auth can-i create deployments --as=jane
kubectl auth can-i list pods --as=system:serviceaccount:default:app-sa

# 检查所有权限
kubectl auth can-i --list
kubectl auth can-i --list --namespace=default

查看有效权限

# 查看用户的所有 RoleBinding
kubectl get rolebindings,clusterrolebindings \
  --all-namespaces \
  -o json | jq '.items[] | select(.subjects[]?.name=="jane")'

# 查看 ServiceAccount 的权限
kubectl describe sa app-sa -n default
kubectl get rolebindings,clusterrolebindings --all-namespaces \
  -o json | jq '.items[] | select(.subjects[]?.name=="app-sa")'

最小权限原则实践

❌ 反模式:过大权限

# 不推荐:给予 cluster-admin 权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: developer-admin
subjects:
- kind: User
  name: developer
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin  # ⚠️ 危险:完全控制权限
  apiGroup: rbac.authorization.k8s.io

问题

  • 开发者拥有删除整个集群的权限
  • 无法追踪具体操作范围
  • 违反最小权限原则

✅ 推荐:精确权限控制

1. 应用只读权限

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]
  resourceNames: ["app-config"]  # 只读特定 ConfigMap
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
  resourceNames: ["app-secret"]  # 只读特定 Secret

2. 开发者完整权限

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: dev
rules:
# Pod 相关
- apiGroups: [""]
  resources: ["pods", "pods/log", "pods/exec"]
  verbs: ["get", "list", "watch", "create", "delete"]
# 应用部署
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Service
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
# ConfigMap(只读)
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "list"]

3. CI/CD 部署权限

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: production
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get", "list", "create", "update"]
- apiGroups: [""]
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list"]

4. 监控系统权限

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
- apiGroups: [""]
  resources: ["nodes", "nodes/metrics", "services", "endpoints", "pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]
- nonResourceURLs: ["/metrics", "/metrics/cadvisor"]
  verbs: ["get"]

权限提升攻击防御

危险的权限组合

# ⚠️ 危险:可以创建 RoleBinding 绑定任意 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: dangerous-role
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings"]
  verbs: ["create"]

# ⚠️ 危险:可以编辑 ServiceAccount 并冒充其他身份
- apiGroups: [""]
  resources: ["serviceaccounts"]
  verbs: ["impersonate"]

# ⚠️ 危险:可以创建 Pod 并挂载任意 Secret
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]

安全配置

# ✅ 安全:限制只能绑定特定的 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: safe-role-manager
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings"]
  verbs: ["create", "update"]
  resourceNames: ["app-reader", "app-editor"]  # 只能绑定这些 Role

调试 RBAC 问题

启用审计日志

# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
  verbs: ["create", "update", "patch", "delete"]
  omitStages: ["RequestReceived"]
- level: Request
  verbs: ["get", "list", "watch"]
  resources:
  - group: "rbac.authorization.k8s.io"

查看拒绝日志

# API Server 日志
kubectl logs -n kube-system kube-apiserver-master -f | grep "Forbidden"

# 审计日志
tail -f /var/log/kubernetes/audit/audit.log | jq 'select(.responseStatus.code==403)'

常见错误

# 错误 1:Forbidden
# 原因:用户没有对应权限
# 解决:检查 RoleBinding 和 ClusterRoleBinding

# 错误 2:User "system:serviceaccount:default:app-sa" cannot list pods
# 原因:ServiceAccount 缺少权限
# 解决:创建 Role 和 RoleBinding

# 错误 3:No matches for kind "Role" in version "rbac.authorization.k8s.io/v1beta1"
# 原因:API 版本过时
# 解决:使用 v1 而不是 v1beta1

RBAC 最佳实践

1. 命名约定

# ServiceAccount 命名
<component>-sa
例如:nginx-ingress-sa, prometheus-sa

# Role 命名
<namespace>-<resource>-<action>
例如:prod-pods-reader, dev-deploy-editor

# RoleBinding 命名
<role>-binding 或 <subject>-<role>
例如:pods-reader-binding, jane-editor

2. 定期审查权限

#!/bin/bash
# rbac-audit.sh

echo "=== Cluster Admin Users ==="
kubectl get clusterrolebindings -o json | \
  jq -r '.items[] | select(.roleRef.name=="cluster-admin") | .subjects[]?.name'

echo "=== ServiceAccounts with cluster-admin ==="
kubectl get clusterrolebindings -o json | \
  jq -r '.items[] | select(.roleRef.name=="cluster-admin") | select(.subjects[]?.kind=="ServiceAccount") | .metadata.name'

echo "=== Users with wildcard permissions ==="
kubectl get clusterroles -o json | \
  jq -r '.items[] | select(.rules[]? | .resources[]? == "*") | .metadata.name'

3. 使用命名空间隔离

# 团队 A 的命名空间
apiVersion: v1
kind: Namespace
metadata:
  name: team-a

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: team-a-admin
  namespace: team-a
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: team-a-admin-binding
  namespace: team-a
subjects:
- kind: Group
  name: team-a
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: team-a-admin
  apiGroup: rbac.authorization.k8s.io

总结

RBAC 是 Kubernetes 安全的基石,理解其原理对于:

  • 权限管理:精确控制访问权限
  • 安全加固:防止权限提升
  • 故障排查:快速定位权限问题
  • 合规审计:满足安全合规要求

核心原则:

  1. 最小权限原则:只授予必需的权限
  2. 职责分离:不同角色不同权限
  3. 定期审计:定期检查和清理权限
  4. 命名空间隔离:使用命名空间隔离团队