LabHub
学习 学习路径 课程

CRD 与 Operator

安装顺序、权限边界、升级

在 LabHub 中继续学习

一句话总结

Operator 是代替人操作集群状态的自动化管理员。因此,一旦 Operator 的服务账户被攻破,攻击者就会原样获得该 Operator 的全部权限。

概念图: 安装顺序 · 权限边界 · 安装顺序就是依赖关系图。 · 权限控制的核心是减少动词。

为什么需要了解这些

大多数工作负载只处理自己命名空间内的事务。Web 应用遭到入侵时,损害通常局限于该应用及其数据。Operator 则不同。它会 watch 多个命名空间中的 CR、创建 Deployment、读取 Secret,有时甚至创建 RBAC 对象。控制器 Pod 中的一个漏洞,就可能导致整个集群被接管。

因此,Operator 运维中最重要的两项内容不是华丽功能,而是安装顺序权限边界

工作原理

安装顺序就是依赖关系图。 不遵守顺序,系统可能在没有明显报错的情况下异常运行。

1. CustomResourceDefinition   — 새 타입을 API 에 먼저 등록
2. ServiceAccount / ClusterRole / ClusterRoleBinding — 권한 부여
3. Deployment                 — 컨트롤러 기동

CRD 必须最先安装,因为控制器一启动就会 watch 该类型。类型不存在时,watch 配置会失败,控制器进入崩溃循环。权限必须提前创建也是同样的原因——没有权限的控制器连列表查询都会收到 403。通过 GitOps 部署时,应使用同步波次或依赖关系明确表示这一顺序。

权限控制的核心是减少动词。 初学者编写 Operator 时,最常见的错误是使用 verbs: ["*"]。真正所需的权限要窄得多。

控制器执行的操作 必需动词 不必要的宽泛权限
读取并监视 CR get, list, watch create, delete
创建/更新子资源 get, list, watch, create, update, patch delete(可由所有者引用替代)
报告状态 update, patch(status 子资源) 更新整个主资源
记录事件 create, patch get, list

尤其要谨慎授予 delete。大多数子资源清理可以交给所有者引用和垃圾回收器,因此控制器无需删除权限。若不授予该权限,就能从根源上阻止遭到入侵的控制器批量删除资源。

另一个经常遗漏的是子资源权限。即使拥有更新 webservices 的权限,webservices/status 也会被视为独立资源,必须单独授权。处理终结器时,还需要 webservices/finalizers。遗漏这两项后,经常会出现“似乎已经授予权限,控制器却仍无法写入 status”的症状。

领导者选举用于防止重复执行。 启动两个控制器副本后,二者可能同时协调同一对象并发生冲突。领导者选举只允许多个副本中的一个真正工作;领导者会在 coordination.k8s.io 的 Lease 对象中写入自身身份,并定期续租。领导者死亡后,租约到期,其他副本接管。这里同样需要最小权限意识——可以使用 resourceNames 将 Lease 权限限制到唯一指定对象。

升级时不要随意修改 CRD。 提升控制器镜像版本可以通过滚动更新安全完成,但同时删除 CRD 的旧版本十分危险。如果 status.storedVersions 中仍保留该版本,删除它后就无法读取已经按该版本存储的对象。升级流程始终应遵循:“CRD 版本只增加;完成对象重存储后才删除。”

关于本实验环境的坦诚说明

本实验没有真实的控制器二进制文件,也没有检查 Pod 内部的手段(kubectl exec、真实日志)。因此,Operator 安装实验聚焦于清单的准确性:创建并验证命名空间与标签、服务账户与 ClusterRole 的动词集合、Deployment 的账户、领导者选举参数、探针、资源限制、安全上下文,以及领导者选举 Lease。权限是否真正符合意图,不通过日志,而是使用 kubectl auth can-i --as= 验证。这在实际现场也是最快、最可靠的 RBAC 调试方法。

生产现场中的常见情况

第一,只差一个字符的绑定事故。 如果 ClusterRoleBinding 中的 subject 名称与真实服务账户名称有一个字符不同,绑定仍会正常创建,不会报错,只有控制器收到 403。因为指向不存在主体的绑定,本身是完全有效的对象。

第二,通过 CR 提升权限。 如果 Operator 信任用户提交的 CR 并为其创建 RBAC 对象,用户就可以把 Operator 当作绕行路径,获得自己无法直接创建的权限。防御应当分为多层:设计上避免 Operator 动态创建 RBAC;确实需要时,将可申请的权限限制在白名单内。

第三,权限不是文档说明,而是验证目标。 “这个 Operator 不能读取 Secret”不应只写在 Wiki 中,而应通过 kubectl auth can-i get secrets --as=... 检查并得到 no。验证表中不仅要包含应当允许的操作,还要包含绝不能允许的操作,才能证明权限边界确实得到执行。

下一次实验要做什么

创建带标签的 Operator 专用命名空间,记录安装顺序,并创建不含通配符的最小权限 ClusterRole 与绑定。为控制器 Deployment 配置专用账户、领导者选举、探针、内存限制和非 root 运行,再创建领导者选举 Lease。随后升级镜像并确认 CRD 版本得到保留,诊断并修复损坏的 Operator 清单中的三个问题,最后制作同时包含“允许”和“不允许”操作的权限验证表,与实际响应逐项对照。