ServiceAccount、RBAC、Leader Election 与容器安全

安全不是一个开关,而是一组互相补位的边界

本项目同时使用:

  • ServiceAccount 表达工作负载身份;
  • RBAC 限制 API 权限;
  • leader election 保证多个 Operator 副本只有一个主动协调;
  • Pod Security Admission 限制 Pod 配置;
  • securityContext 限制容器进程权限;
  • NetworkPolicy 限制网络路径;
  • Secret 隔离敏感数据。

这些机制没有哪个能单独解决全部问题。RBAC 管 API,不负责网络;NetworkPolicy 管流量,不负责文件权限;非 root 容器也不能弥补过大的 API 权限。

ServiceAccount:Pod 在集群中的机器身份

用户通过 kubeconfig 访问 API Server,Pod 通常通过 ServiceAccount 身份访问。

本项目有两类 ServiceAccount:

ServiceAccountNamespace用途
subpool-operatorsubpool-systemOperator 调用 Kubernetes API
subpool每个 Pool Namespaceruntime Pod 的身份
mihomo-gatewaysubpool-gatewayShared Mihomo Pod 身份

为工作负载单独创建 ServiceAccount,比使用 Namespace 的 default ServiceAccount 更容易审计,也便于精确绑定权限。

为什么 runtime 禁止自动挂载 API token

项目在 ServiceAccount 和 Pod 两处都设置:

1
automountServiceAccountToken: false

CPA 与 cpa-manager 不需要调用 Kubernetes API,因此没有必要把 ServiceAccount token 放进容器。

如果应用被攻破,攻击者即使进入容器,也少了一份可直接拿来访问 API Server 的凭据。这是最小权限原则中非常实用的一步:不用的身份能力不要发放。

Mihomo 同样禁用 token 自动挂载,因为代理进程也不需要访问 Kubernetes API。

RBAC 的四个问题

一条 RBAC rule 可以拆成四个问题:

1
2
3
4
谁(Subject)
通过什么绑定(RoleBinding / ClusterRoleBinding)
可以对什么资源(apiGroups + resources)
执行哪些动作(verbs)

例如 Operator 需要管理 StatefulSet:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
apiGroups:
  - apps
resources:
  - statefulsets
verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch

get 读取单个对象,list 读取集合,watch 订阅变化,create/update/patch/delete 才会修改状态。

Role 与 ClusterRole 的区别

Role 只在一个 Namespace 中定义权限。ClusterRole 可以表达 cluster-scoped 资源权限,也可以被不同 Namespace 的 RoleBinding 引用。

SubPool Operator 需要:

  • 监听 cluster-scoped SubPool
  • 创建 cluster-scoped Namespace;
  • 在任意派生 Pool Namespace 创建资源;
  • 读取共享 Gateway、EndpointSlice;
  • 更新 SubPool 的 status 和 finalizers。

因此它的主要管理权限必须通过 ClusterRole 和 ClusterRoleBinding 提供。单一 Namespace 内的 Role 无法完成跨 Namespace 生命周期管理。

权限大并不意味着可以省略边界。控制器代码仍严格检查 owner reference,不凭名字接管对象。RBAC 是“技术上能做什么”,所有权校验是“业务上允许对谁做”。

status 与 finalizers 为什么是独立子资源权限

RBAC 中单独出现:

1
2
subpools/status
subpools/finalizers

Kubernetes 把 status 和 finalizer 更新视为独立 API 路径。控制器需要更新现实状态和删除收尾标记,但普通业务使用者未必需要这些权限。

把它们单独授权,能避免“只想允许用户编辑 spec,却顺手允许伪造 Ready 状态或移除 finalizer”。

可以用 auth can-i 验证权限

查看当前身份能否执行某动作:

1
2
3
kubectl auth can-i get subpools
kubectl auth can-i update subpools/status
kubectl auth can-i create namespaces

模拟 ServiceAccount:

1
2
kubectl auth can-i list endpointslices \
  --as=system:serviceaccount:subpool-system:subpool-operator

它适合验证 RBAC,不证明控制器业务逻辑一定正确。

两个 Operator Pod 为什么不会同时 reconcile

Operator Deployment 设置 replicas 为 2,同时启用 leader election:

1
2
--leader-elect=true
--leader-election-namespace=subpool-system

多个 controller-manager 实例通过 Lease 对象竞争领导权。获得 Lease 的实例执行 controller 工作,其他实例保持待命;leader 退出或续租失败后,另一个实例接管。

这解决的是控制面单实例故障:

1
Operator Pod A 是 leader → A 故障 → Lease 到期/转移 → Pod B 接管

它不让两个实例并行处理同一控制器以提高吞吐,更不会把 runtime StatefulSet 变成双副本。

leader election 为什么单独使用 namespaced Role

Lease 位于 subpool-system,所以项目用该 Namespace 中的 Role 授权对 coordination.k8s.io/leases 的读写,并授权创建或 patch Event。

这部分不需要 cluster-wide 权限。把 leader election 权限限制在固定 Namespace,减少无关权限范围。

PDB 与 leader election 是互补关系

Operator 有两个 replica,PDB minAvailable: 1。二者分工如下:

机制作用
replicas: 2保持一个待命实例
leader election确保只有一个实例主动协调
PDB自愿中断时尽量保留至少一个可用 Pod
readinessProbe判断某个 Operator Pod 是否可用

缺少 leader election,两个副本可能同时写对象;缺少第二个副本,leader election 没有接管者;缺少 PDB,维护工具可能同时驱逐两个 Pod。

即使三者都具备,Node 同时故障、API Server 不可用等场景仍可能中断控制面。

容器为什么使用 UID 65532

Operator 的最终镜像基于 distroless/static:nonroot,并声明:

1
USER 65532:65532

Deployment 也要求 non-root,并固定 UID/GID 65532。双重声明使镜像默认行为与 Kubernetes 运行约束一致。

Operator 容器还使用:

1
2
3
4
allowPrivilegeEscalation: false
capabilities.drop: ALL
readOnlyRootFilesystem: true
seccompProfile: RuntimeDefault

这些配置分别限制提权、Linux capabilities、根文件系统写入和系统调用面。

runtime 容器也以 non-root 运行、drop all capabilities,并通过 PVC 挂载提供需要写入的路径。

Pod Security Admission 的 Namespace 级防线

每个 Pool Namespace 带 restricted 级别标签:

1
2
3
pod-security.kubernetes.io/enforce=restricted
pod-security.kubernetes.io/audit=restricted
pod-security.kubernetes.io/warn=restricted

enforce 会拒绝不符合 restricted 标准的 Pod;audit 记录违规;warn 给请求者提示。

它可以阻止常见危险配置,例如特权容器、危险 capabilities 或不满足 non-root 等要求。具体规则随 Kubernetes Pod Security Standards 定义,应在集群升级时做兼容验证。

liveness、readiness 与 RBAC 无关

Operator 的 /healthz/readyz 只说明进程健康,不会自动验证 ClusterRole 是否包含全部业务权限。

一个 Operator Pod 可以 Ready,但 reconcile 时不断收到 Forbidden。因此故障时要同时看:

1
2
3
4
kubectl -n subpool-system get pods
kubectl -n subpool-system logs deployment/subpool-operator \
  --all-containers=true --tail=200
kubectl auth can-i ...

安全设计中的几个诚实边界

  • ClusterIP 不等于只有受认证用户能访问;它只是集群内入口类型。
  • Secret 不等于加密保险箱;访问 Secret 的 RBAC 必须严格。
  • non-root 不等于容器没有漏洞;仍要维护镜像和依赖。
  • NetworkPolicy 不限制 Kubernetes API 权限;RBAC 也不限制普通 TCP 流量。
  • leader election 不等于业务数据高可用。
  • PDB 不阻止所有故障。

成熟的安全不是给系统贴上“secure”标签,而是每层都明确允许什么、拒绝什么,以及这层机制覆盖不到什么。

使用 Hugo 构建
主题 StackJimmy 设计