Skip to content

RBAC 与安全 ​

生产集群里 admin.conf 到处复制、所有组件默认拿 cluster-admin,这两种做法会把权限面打得很开——任何人拿到这个 kubeconfig 就能删任意资源、读所有 Secret、改集群配置。RBAC 做的就是把"谁能做什么"收紧到最小范围。

RBAC 的对象模型不复杂,但和 ServiceAccount、Pod 安全上下文、审计日志放在一起,才能覆盖一条完整的 API 请求链路——谁、用什么身份、访问什么资源、做了什么操作、事后能不能追溯到。

RBAC 四类对象 ​

对象作用范围说明
Rolenamespace 内定义在某 namespace 里能做什么
ClusterRole集群级定义集群范围资源(nodes、PV、namespaces)或跨 namespace 操作
RoleBindingnamespace 内把 Role 或 ClusterRole 授权给 namespace 内的主体
ClusterRoleBinding集群级把 ClusterRole 授权到全集群
ServiceAccountnamespace 内Pod 访问 API Server 时用的身份

RoleBinding 可以引用 ClusterRole 但只在一个 namespace 内生效。这个设计允许定义一个"只读 Pod"的 ClusterRole,然后在每个 namespace 里分别绑给不同的人——不用在每个 namespace 都建一份 Role。

只读示例 ​

给用户在 demo namespace 里只读 Pod 和 Pod 日志:

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: demo
rules:
  - apiGroups: [""]              # 核心 API 组的 apiGroup 是空字符串
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-reader
  namespace: demo
subjects:
  - kind: User
    name: ops-readonly
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

验证权限——kubectl auth can-i 走 SubjectAccessReview API,直接问 apiserver "这个身份能不能做这个操作":

bash
kubectl auth can-i list pods -n demo --as=ops-readonly
kubectl auth can-i delete pods -n demo --as=ops-readonly    # 预期 no

ServiceAccount ​

Pod 访问 API Server 默认用的是所在 namespace 的 default ServiceAccount。生产里给不同组件单独建 SA 并绑定最小权限:

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-reader
  namespace: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: demo
spec:
  template:
    spec:
      serviceAccountName: app-reader

给控制器、采集组件、CI Runner 分配 SA 时,先想清楚它需要读什么(get/list/watch)、是否需要写(create/update/patch/delete)、是否需要删(delete/deletecollection)。只读场景给读写权限是多余的。

Secret 权限 ​

能 get secret 的人基本上能拿到 Secret 里的所有明文内容(base64 不是加密)。Secret 保护要从多个层面看:

防护层解决的问题
RBAC谁能通过 API 读取 Secret
etcd 加密Secret 在 etcd 存储层是否明文
审计日志事后能否追溯谁读了 Secret
CI/CDCI 流程是否把 Secret 打印到日志里
Git明文 Secret 是否被提交到了仓库

Pod 安全上下文 ​

Pod 和容器级别的安全配置通过 securityContext 设置:

yaml
securityContext:             # Pod 级别
  runAsNonRoot: true
  runAsUser: 10001
  fsGroup: 10001
containers:
  - name: app
    image: harbor.example.com/demo/app:v1.0.0
    securityContext:         # 容器级别
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL

高风险配置值得定期审计:

配置暴露的风险
privileged: true容器几乎等同宿主机 root
hostPath 挂载敏感目录直接读写宿主机文件系统
hostNetwork: true使用宿主机网络命名空间
hostPID: true能看到宿主机所有进程
挂载 Docker/containerd socket可以在容器内控制宿主机上的所有容器
capabilities.add: ["SYS_ADMIN", "NET_ADMIN"]接近 privileged 的权限面

审计日志 ​

审计策略决定 API Server 记录什么请求:

yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: Metadata
    verbs: ["get", "list", "watch"]
  - level: RequestResponse   # 记录完整请求体和响应体
    verbs: ["create", "update", "patch", "delete"]
    resources:
      - group: ""
        resources: ["secrets"]

API Server 参数:

yaml
- --audit-policy-file=/etc/kubernetes/audit-policy.yaml
- --audit-log-path=/var/log/kubernetes/audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100

RequestResponse 级别会记录请求和响应的具体内容——如果包含 Secret、token,审计日志本身就成了敏感数据源。策略要根据资源敏感度分层:对 Secret 的读写用 RequestResponse,对普通 Pod 的 get/list 用 Metadata。

权限排查 ​

查权限时先确认身份,再查绑定链:

bash
kubectl auth can-i create pods -n demo --as=alice
kubectl auth can-i get secrets -A --as=system:serviceaccount:demo:app-reader
kubectl describe rolebinding -n demo
kubectl describe clusterrolebinding

排查顺序:

  1. 确认身份(User、Group、ServiceAccount)
  2. 查 RoleBinding/ClusterRoleBinding 指向哪些 Role/ClusterRole
  3. 看 Role/ClusterRole 里的具体 rules
  4. 用 kubectl auth can-i 验证单项操作

生产里至少把人为操作、系统组件和 CI/CD 的身份分开。共用 admin.conf 的问题不只是权限过大,而是误删和误改之后无法追溯到人。