3. OPA Gatekeeper 核心概念回顾
在开始从 OPA Gatekeeper 向 Kyverno 迁移之前,必须对 Gatekeeper 的核心概念建立清晰、准确的理解。Gatekeeper 是 Kubernetes 的 准入控制器,它利用 OPA(Open Policy Agent) 的 Rego 语言来编写策略,并通过 约束模板(ConstraintTemplate) 和 约束(Constraint) 两个核心资源来管理策略的生命周期。本章将系统回顾这些概念,为后续的迁移适配打下坚实基础。
3.1 核心架构与组件
Gatekeeper 的架构围绕三个核心组件构建:OPA 引擎、约束模板和约束。它们之间的关系如下图所示:
3.2 约束模板(ConstraintTemplate)
约束模板是 Gatekeeper 策略体系的核心。它是一个 CRD(Custom Resource Definition),用于定义策略的 逻辑(Rego 代码) 和 参数结构(Schema)。一旦创建,Kubernetes 集群中就会生成一个新的 CRD 类型,名称与模板名称一致。
3.2.1 模板结构
一个典型的约束模板包含以下关键字段:
| 字段路径 | 类型 | 说明 |
|---|---|---|
spec.crd.spec.names.kind |
string | 生成的 CRD 类型名称,例如 K8sRequiredLabels |
spec.targets[].rego |
string | Rego 策略代码,定义 violation 规则 |
spec.targets[].libs |
[]string | 可选的 Rego 库代码,用于代码复用 |
spec.crd.spec.validation.openAPIV3Schema |
object | 约束实例的参数校验 schema(OpenAPI v3 格式) |
3.2.2 Rego 策略逻辑
Rego 代码是约束模板的灵魂。Gatekeeper 要求 Rego 代码中必须定义一个 violation 规则,该规则返回 true 表示违反策略。以下是一个要求资源必须包含 owner 标签的模板示例:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("缺少必需的标签: %v", [missing])
}
input.review:准入请求的完整对象,包含object(新资源)和oldObject(旧资源,更新时存在)。input.parameters:约束实例中传入的参数,与模板 schema 对应。violation规则必须返回一个包含msg字段的对象,描述违反详情。
3.3 约束(Constraint)
约束是约束模板的实例化资源。它指定了 策略参数 和 匹配范围(哪些资源受此策略约束)。约束的 apiVersion 和 kind 由约束模板动态生成。
3.3.1 约束示例
基于上述 K8sRequiredLabels 模板,创建一个要求所有命名空间必须包含 owner 和 env 标签的约束:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-owner-env
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels:
- "owner"
- "env"
3.3.2 匹配机制
约束的 spec.match 字段支持多种匹配条件,用于精细控制策略作用范围:
| 匹配字段 | 说明 | 示例 |
|---|---|---|
kinds |
按资源类型匹配(apiGroups + kinds) | apiGroups: [""], kinds: ["Pod"] |
namespaces |
按命名空间白名单匹配 | ["production", "staging"] |
excludedNamespaces |
排除特定命名空间 | ["kube-system"] |
labelSelector |
按资源标签选择器匹配 | matchLabels: {app: "web"} |
namespaceSelector |
按命名空间标签选择器匹配 | matchExpressions: [...] |
scope |
按资源范围匹配(Cluster/Namespaced) | Cluster |
3.4 数据(Data)
Gatekeeper 中的“数据”是指 OPA 引擎在评估策略时可以访问的 外部信息。这些数据可以来自集群状态,也可以由管理员手动注入。数据机制使得策略可以基于集群的实时状态做出决策。
3.4.1 数据来源
- 集群数据(Cluster Data):Gatekeeper 自动将集群中的资源(如 Namespace、Node、Pod)同步到 OPA 引擎,供 Rego 代码通过
data前缀访问。例如data.kubernetes.namespaces返回所有命名空间列表。 - 外部数据(External Data):通过
Config资源或Provider机制注入的自定义数据,例如来自 CMDB 的标签白名单、来自安全团队的禁止镜像列表。 - 数据注入(Data Injection):管理员可以创建
config资源,将任意 JSON 数据注入到 OPA 的data命名空间中。
3.4.2 数据在 Rego 中的使用
package data_example
# 访问集群中的命名空间列表
violation[{"msg": msg}] {
ns := input.review.object.metadata.namespace
all_ns := data.kubernetes.namespaces
# 检查命名空间是否存在
not any(all_ns, {x | x.metadata.name == ns})
msg := sprintf("命名空间 %v 不存在", [ns])
}
# 访问外部注入的数据
violation[{"msg": msg}] {
image := input.review.object.spec.containers[_].image
blocked := data.external.blocked_images
image == blocked[_]
msg := sprintf("镜像 %v 被禁止使用", [image])
}
data.kubernetes 中的大型集合(如所有 Pod)可能导致 OPA 评估延迟。建议在 Rego 中使用 count() 和集合操作来优化查询,或通过缓存机制减少重复计算。
3.5 审计(Audit)
审计是 Gatekeeper 的 被动合规检查 机制。与准入控制(实时拦截)不同,审计会定期扫描集群中已有的资源,检查它们是否违反当前生效的约束,并将结果记录在约束资源的 status 字段中。
3.5.1 审计工作流程
3.5.2 审计结果查看
审计结果存储在约束资源的 status 字段中,可以通过 kubectl describe 查看:
kubectl describe k8srequiredlabels ns-must-have-owner-env
# 输出示例(简化)
Status:
By Pod:
- Constraint UID: ...
Enforced: true
Id: gatekeeper-audit-xxxxx
Observed Generation: 1
Operations:
- audit
Violations:
- Enforcement Action: deny
Kind: Namespace
Message: 缺少必需的标签: {"owner"}
Name: default
- Enforcement Action: deny
Kind: Namespace
Message: 缺少必需的标签: {"env"}
Name: kube-public
Total Violations: 2
backgroundScan 机制对应 Gatekeeper 的审计功能。
3.6 Rego 语法基础
Rego 是 OPA 的声明式查询语言,受 Datalog 启发。理解 Rego 的核心语法是编写和迁移 Gatekeeper 策略的前提。以下列出迁移过程中最常遇到的 Rego 语法要素:
3.6.1 核心语法对照
| Rego 语法 | 说明 | 迁移到 Kyverno 的对应概念 |
|---|---|---|
package <name> |
定义策略包,用于命名空间隔离 | Kyverno 使用 YAML 规则,无包概念 |
violation[{"msg": msg}] { ... } |
定义违反规则,返回消息 | Kyverno 使用 validate.deny.conditions 或 validate.pattern |
input.review.object |
访问被审查的 Kubernetes 资源 | Kyverno 使用 {{ request.object }} 或 @ 引用 |
input.parameters |
访问约束实例传入的参数 | Kyverno 使用 params 对象或直接写在规则中 |
data.kubernetes |
访问集群状态数据 | Kyverno 使用 context 和 APICall 获取外部数据 |
some ... in ... |
迭代集合,类似 for 循环 | Kyverno 使用 foreach 或 JMESPath 表达式 |
count() |
计算集合元素数量 | Kyverno 使用 length() 函数 |
set / object / array |
集合、对象、数组类型 | Kyverno 使用 YAML 结构,类型由 JMESPath 处理 |
3.6.2 常见 Rego 模式示例
以下展示几个在 Gatekeeper 策略中高频出现的 Rego 模式,以及它们在 Kyverno 中的对应写法(预览):
// Rego: 检查容器镜像是否来自允许的仓库
package allowed_registry
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not startswith(container.image, "gcr.io/my-company/")
msg := sprintf("容器镜像 %v 必须来自 gcr.io/my-company/", [container.image])
}
// Rego: 检查资源是否设置了 resource limits
package require_limits
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits
msg := sprintf("容器 %v 必须设置 resource limits", [container.name])
}
// Rego: 使用外部数据检查禁止的端口
package block_ports
violation[{"msg": msg}] {
port := input.review.object.spec.ports[_]
blocked_ports := data.external.blocked_ports
port.containerPort == blocked_ports[_]
msg := sprintf("端口 %v 被禁止使用", [port.containerPort])
}
- Rego 的
violation规则本质是一个 集合推导:当条件满足时,向集合中添加一条违规消息。Kyverno 的deny规则则是 条件判断:当条件为真时拒绝请求。 - Rego 中的
not和some是逻辑核心,迁移时需要转换为 Kyverno 的anyPattern或foreach+deny组合。 - Rego 的
data查询在 Kyverno 中通过context和apiCall实现,需要显式定义数据源。