4. ConstraintTemplate 入门:Rego 语言基础与第一个禁止特权容器模板
ConstraintTemplate 是 OPA Gatekeeper 的核心扩展点,它使用 Rego 语言定义策略逻辑。本章将带你从零开始掌握 Rego 基础语法,并亲手编写第一个 ConstraintTemplate——禁止创建特权容器。
4.1 Rego 语言核心概念
Rego 是一种为策略即代码(Policy as Code)设计的声明式查询语言,由 Open Policy Agent 项目创建。与传统的命令式编程不同,Rego 专注于“数据是什么”而非“如何获取数据”。
| 概念 | 说明 | 类比 |
|---|---|---|
| 规则(Rule) | 定义策略逻辑的基本单元,形如 rule_name { condition } |
类似编程语言中的函数 |
| 查询(Query) | 对规则求值,返回 true/false 或数据集合 | 类似 SQL 的 SELECT |
| 文档(Document) | Rego 将输入数据视为嵌套文档,通过点号访问 | 类似 JSON 对象 |
| 迭代(Iteration) | 使用 some 关键字遍历集合 |
类似 for-each 循环 |
| 默认值(Default) | 为规则设置默认返回值,避免未定义 | 类似变量初始化 |
💡 关键理解: Rego 中所有规则默认是“与”关系(AND)。当规则体中有多个条件时,必须全部满足规则才为 true。如果需要“或”关系,需定义多条同名规则。
4.2 Rego 基础语法速览
以下是最常用的 Rego 语法模式,掌握它们即可编写 90% 的 Gatekeeper 策略。
4.2.1 变量与赋值
# 变量赋值使用 :=
name := "privileged-container"
# 使用 input 引用传入的 Kubernetes 资源
container := input.spec.containers[_]
4.2.2 条件判断
# 比较运算符:==, !=, <, >, <=, >=
container.securityContext.privileged == true
# 集合成员检查
container.securityContext.capabilities.add[_] == "SYS_ADMIN"
4.2.3 迭代与集合
# 使用 some 遍历数组
some container in input.spec.containers
# 使用下划线通配符
container.securityContext.capabilities.add[_] == "NET_ADMIN"
4.2.4 规则定义
# 完整规则:violation 是 Gatekeeper 约定的规则名
violation[{"msg": msg}] {
container := input.spec.containers[_]
container.securityContext.privileged == true
msg := "特权容器被禁止"
}
⚠️ 常见陷阱: Rego 中
= 是统一(unification)操作符,:= 是赋值操作符。在规则体内,:= 只能对变量赋值一次;== 用于比较。初学者最容易混淆这三者的用法。
4.3 ConstraintTemplate 结构解析
一个完整的 ConstraintTemplate 包含三个主要部分:
- metadata:模板名称和标签
- spec.crd.spec:定义约束(Constraint)的 OpenAPI v3 模式,即约束可以接收哪些参数
- spec.targets:指定目标平台(如 admission.k8s.gatekeeper.sh)和 Rego 策略代码
4.4 编写第一个 ConstraintTemplate:禁止特权容器
下面我们逐步构建一个完整的 ConstraintTemplate,用于拒绝所有设置了 privileged: true 的 Pod 创建请求。
4.4.1 完整模板代码
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sblockprivileged
spec:
crd:
spec:
names:
kind: K8sBlockPrivileged
validation:
openAPIV3Schema:
type: object
properties:
exemptNamespaces:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sblockprivileged
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("容器 '%v' 设置了 privileged=true,特权容器被禁止", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
container.securityContext.privileged == true
msg := sprintf("初始化容器 '%v' 设置了 privileged=true,特权容器被禁止", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.ephemeralContainers[_]
container.securityContext.privileged == true
msg := sprintf("临时容器 '%v' 设置了 privileged=true,特权容器被禁止", [container.name])
}
4.4.2 代码逐段解读
| 代码段 | 作用 |
|---|---|
package k8sblockprivileged |
声明 Rego 包名,必须与 ConstraintTemplate 名称一致(小写) |
violation[{"msg": msg}] |
Gatekeeper 约定的规则入口,返回违规消息数组 |
input.review.object.spec.containers[_] |
遍历 Pod 中的所有容器 |
container.securityContext.privileged == true |
检查特权标志是否为 true |
sprintf(...) |
格式化输出违规消息,包含容器名称 |
📌 设计要点: 我们为
containers、initContainers、ephemeralContainers 分别定义了三条规则。这是为了覆盖 Kubernetes 中所有容器类型,避免攻击者通过 init 容器或临时容器绕过策略。
4.5 创建对应的约束(Constraint)
ConstraintTemplate 只是策略模板,还需要创建具体的约束实例来激活策略:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockPrivileged
metadata:
name: block-privileged-global
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
- gatekeeper-system
parameters:
exemptNamespaces:
- istio-system
约束通过 match 字段指定作用范围,通过 parameters 传递模板中定义的参数。
4.6 验证与测试
部署完成后,尝试创建一个特权容器来验证策略是否生效:
# 测试:创建特权容器(应被拒绝)
kubectl run test-privileged --image=nginx --privileged=true
# 预期输出:
# Error: admission webhook "validation.gatekeeper.sh" denied the request:
# [block-privileged-global] 容器 'test-privileged' 设置了 privileged=true,特权容器被禁止
4.7 常见问题与排错
- 约束未生效:检查 Gatekeeper 是否已部署并配置了 ValidatingWebhookConfiguration
- Rego 语法错误:使用
kubectl describe constrainttemplate k8sblockprivileged查看错误日志 - 误拦截正常 Pod:利用
excludedNamespaces或exemptNamespaces参数添加白名单 - 容器类型遗漏:务必覆盖
containers、initContainers、ephemeralContainers
🔧 调试技巧: 使用
kubectl gatekeeper trace 命令(需安装 Gatekeeper CLI 插件)可以实时跟踪 Rego 策略的求值过程,精确定位违规原因。