10.4 K8s 策略引擎入门——Kyverno 与 OPA Gatekeeper
预计阅读时间:14 分钟
📖 目录
K8s RBAC 控制「谁能做什么」,但不控制「资源必须满足什么条件」。策略引擎(Policy Engine)在 Admission 阶段拦截不符合规则的资源创建——比如禁止用 latest 标签、必须设置资源限制、镜像必须来自可信仓库。
学习目标
学完本章,你将能够:
- 理解 Kubernetes Admission 控制机制与策略引擎的拦截原理
- 掌握 Kyverno ClusterPolicy 的 Validate / Mutate / Generate 策略编写
- 掌握 OPA Gatekeeper 的 ConstraintTemplate 与 Rego 策略语法
- 理解 Enforce 与 Audit 两种执行模式的差异及切换策略
- 掌握 PolicyReport 审计结果的查看与策略排错方法
- 理解「策略即代码」的 Git 管理与 CI/CD 部署方式
前置知识
- 6.2:Kubernetes 入门 Kubernetes 容器编排入门——Pod、Deployment 与 API 资源
- 5.6:SELinux 与 AppArmor SELinux 与 AppArmor——强制访问控制与安全基线思路
- k8s04 K8s NetworkPolicy 网络策略——零信任网络策略
- 5.1:Linux 安全加固 Linux 安全加固——服务器安全基线清单
方案对比
| Kyverno | OPA Gatekeeper | |
|---|---|---|
| 语言 | YAML(声明式) | Rego(策略语言) |
| 学习曲线 | 低(K8s 原生 YAML) | 高(需学 Rego) |
| 策略类型 | Validate / Mutate / Generate / VerifyImages | Validate / Mutate(Audit / Enforce) |
| 生态 | K8s 原生,CNCF 毕业项目 | CNCF 毕业项目,多平台通用 |
| 适用 | K8s 为主的团队 | 多平台(K8s + API 网关 + 数据库) |
准入控制原理——策略引擎在哪里生效
Kubernetes API Server 对每个写请求(create/update/delete)都要经过 Admission 阶段。策略引擎以 Admission Webhook 方式注册到 API Server,请求在资源持久化之前被拦截检查:
用户 / kubectl / 控制器
│
▼
API Server ──► 认证(Authentication) ──► 授权(Authorization) ──► 准入(Admission)
│
MutatingAdmissionWebhook(Kyverno 变更/默认值注入)
│
ValidatingAdmissionWebhook(Kyverno / OPA 校验)
│
┌─ 通过 ──► 持久化到 etcd ──► 返回响应
└─ 拒绝 ──► 返回 403(含策略名与消息)
Webhook 收到的是 AdmissionReview 请求:包含 request.object(待创建资源的完整 JSON)、request.userInfo(操作者身份)、request.operation(CREATE/UPDATE/DELETE)。策略引擎据此返回 allowed: true/false。关键认知:策略只在写入时生效,运行中的容器被改造(如进入 privileged 模式)不会触发复核;对存量资源需靠 Audit 定期扫描。
1. Kyverno 安装与配置
# 安装 Kyverno
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
# 验证
kubectl get pods -n kyverno
Validate Policy——拦截不合规资源
# 禁止使用 latest 标签
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: Enforce # Enforce=拦截 / Audit=仅记录
rules:
- name: require-image-tag
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "镜像标签不能使用 'latest',请指定具体版本"
pattern:
spec:
containers:
- image: "!*:latest"
---
# 所有容器必须设置资源限制
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: Enforce
rules:
- name: check-resource-limits
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "每个容器必须设置 resources.limits"
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"
Mutate Policy——自动修改资源
# 自动为所有 Pod 注入安全上下文
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-security-context
spec:
rules:
- name: set-security-context
match:
any:
- resources:
kinds: ["Pod"]
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
Generate Policy——自动生成资源
# 为每个新命名空间自动创建默认 NetworkPolicy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: default-network-policy
spec:
rules:
- name: generate-default-deny
match:
any:
- resources:
kinds: ["Namespace"]
generate:
synchronize: true
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
name: default-deny
namespace: "{{request.object.metadata.name}}"
data:
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
VerifyImages——校验镜像签名与来源
# 校验镜像必须来自内网仓库(Validate + VerifyImages 组合)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-trusted-registry
spec:
validationFailureAction: Enforce
rules:
- name: only-internal-registry
match:
any:
- resources:
kinds: ["Pod"]
verifyImages:
- imageReferences: ["harbor.internal.example/*"]
required: false
validate:
message: "镜像必须来自 harbor.internal.example"
pattern:
spec:
containers:
- image: "harbor.internal.example/*"
# 用 cosign 公钥校验镜像签名(与 cosign01 的签名流程联动)
# CI 中 cosign sign 签名 → 准入时校验签名 + 仓库白名单双保险
# verifyImages:
# - imageReferences: ["*"]
# required: true
# mutateDigest: true
# attestors:
# - entries:
# - keys:
# publicKeys: |-
# -----BEGIN PUBLIC KEY-----
# MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
# -----END PUBLIC KEY-----
排除系统命名空间
# 在每条策略的 rules 中声明 exclude,避免拦截集群自身组件
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged
spec:
validationFailureAction: Enforce
rules:
- name: check-privileged
match:
any:
- resources:
kinds: ["Pod"]
exclude:
any:
- resources:
namespaces: ["kube-system", "kube-public", "kyverno", "gatekeeper-system"]
策略排错:从 webhook 日志看拦截详情
策略不生效或误拦时,先看 webhook 端到端链路:请求是否到达引擎、引擎是否匹配、匹配后结果如何。三个排查点:
# 1. 确认 webhook 已注册且健康
kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg
kubectl get mutatingwebhookconfiguration kyverno-resource-mutating-webhook-cfg
# 2. 看引擎日志中的 AdmissionReview 处理记录
kubectl logs -n kyverno -l app.kubernetes.io/name=kyverno -f | grep -i reject
# 拦截时日志含 "REJECT" 或 "allow=false";误放行时查 "did not match"
# 3. 用审计模式复现问题(不改任何集群状态)
# Kyverno:策略切 Audit 后 apply 一个测试资源,看 PolicyReport 结果
# Gatekeeper:直接本地跑 Rego,与引擎判定完全一致
opa eval -i pod.yaml -d template.rego "data.k8sallowedrepos.violation"
# -i 输入资源、-d 策略文件,输出 violation 数组
opa eval / opa test 验证,YAML 类策略先用 kyverno test 验证——能本地确认的不要反复 apply 到集群(每一次 Enforce 误拦都会卡住业务发布)。2. OPA Gatekeeper
# 安装 Gatekeeper
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper gatekeeper/gatekeeper \
-n gatekeeper-system --create-namespace
ConstraintTemplate——定义策略逻辑
# 禁止 latest 标签的 ConstraintTemplate
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdisallowlatesttag
spec:
crd:
spec:
names:
kind: K8sDisallowLatestTag
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdisallowlatesttag
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
endswith(container.image, ":latest")
msg := sprintf("容器 '%v' 使用了 latest 标签,请指定具体版本", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not contains(container.image, ":")
msg := sprintf("容器 '%v' 未指定镜像标签,请使用具体版本", [container.name])
}
---
# 应用策略
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowLatestTag
metadata:
name: pod-no-latest
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
带参数的 ConstraintTemplate
把可变值(如允许的仓库前缀)声明为 parameters,同一模板可被多个 Constraint 以不同参数复用:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sallowedrepos
spec:
crd:
spec:
names:
kind: K8sAllowedRepos
validation:
openAPIV3Schema:
type: object
properties:
repos:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sallowedrepos
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
repo := container.image
not startswith(repo, input.parameters.repos[_])
msg := sprintf("镜像 '%v' 不在允许的仓库列表中", [repo])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: pod-allowed-repos
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces: ["prod"]
parameters:
repos:
- "harbor.internal.example"
enforcementAction: deny # deny=拦截 / dryrun=仅审计
enforcementAction: deny 拦截、dryrun 只记录(对应 Kyverno 的 Enforce/Audit)。审计结果可通过 kubectl get constraint pod-allowed-repos -o yaml 查看 status.violations 字段。Rego 速查:五分钟读懂策略语言
| 概念 | 写法 | 说明 |
|---|---|---|
| 包 | package k8sallowedrepos | 每个 .rego 文件第一行,与模板名对应 |
| 输入 | input.review.object | AdmissionReview 中的资源对象 |
| 迭代 | container := input.review.object.spec.containers[_] | [_] 遍历数组,命中即绑定 |
| 违规输出 | violation[{"msg": msg}] | 引擎收集所有 violation 并拒绝(deny 模式) |
| 取反 | not startswith(...) | 条件不成立时继续匹配 |
| 参数 | input.parameters.repos | Constraint 传入的参数 |
# 读 Rego 的套路:violation 里是一组"必须同时成立的子句"
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_] # 1. 遍历容器
repo := container.image # 2. 取镜像
not startswith(repo, input.parameters.repos[_]) # 3. 不在白名单
msg := sprintf("镜像 '%v' 不在允许列表", [repo]) # 4. 组装消息
}
# 任何子句失败 → 这条 violation 不成立 → 策略放行
# 所有容器都满足 → 无 violation → 策略放行
3. 策略审计与报告
# Kyverno:查看策略违规
kubectl get policyreport -A
kubectl get clusterpolicyreport
# 查看特定策略的违规详情
kubectl describe clusterpolicy disallow-latest-tag
# OPA Gatekeeper:查看审计结果
kubectl get constraints
kubectl get constrainttemplates
# Gatekeeper 审计模式(不拦截,仅记录)
# 在 ConstraintTemplate 的 spec.targets[0].rego 中添加:
# annotation[{"msg": msg}] { ... } # 用 annotation 替代 violation
审计结果接入监控
PolicyReport 只躺在 etcd 里没有价值,把违规计数接进监控才有人看:
# 按策略统计违规数(Kyverno)
kubectl get clusterpolicyreport -o json \
| jq -r '.items[0].results[] | "\(.policy) \(.result)"' \
| sort | uniq -c
# 巡检脚本定期执行,配合 cron + 告警:
# Enforce 策略出现违规 → 策略被绕过(误配或新版绕行),立即人工处理
# 违规数持续增长 → 策略覆盖了错误的资源集合,回退审计模式复查
# Gatekeeper 审计指标(/metrics 端点直接暴露 Prometheus 格式)
gatekeeper_constraints_violations{enforcement_action="deny"}
# 接 Prometheus + Grafana 看板,按 Constraint 分维度展示
4. 生产策略清单
| 策略 | 类型 | 说明 |
|---|---|---|
| 禁止 latest 标签 | Validate | 强制镜像版本可追溯 |
| 必须设置资源限制 | Validate | 防止资源耗尽 |
| 必须设置安全上下文 | Mutate | 自动注入 runAsNonRoot 等 |
| 镜像必须来自可信仓库 | Validate | 只允许内网 registry |
| 禁止 privileged 容器 | Validate | 安全基线 |
| Pod 必须有标签 app | Validate | 便于监控和管理 |
| 新命名空间自动创建 NetworkPolicy | Generate | 零信任网络 |
5. 策略测试——上线前验证
策略必须像代码一样可测试。Kyverno 官方提供 kyverno test,OPA 生态使用 conftest 与 opa test:
# Kyverno:目录结构(策略 + 用例)
policies/
├── disallow-latest-tag.yaml # 被测策略
└── test/
└── disallow-latest-tag/
├── good-pod.yaml # 应通过的资源
└── bad-pod.yaml # 应被拦截的资源
# test/bad-pod.yaml——声明期望结果
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: disallow-latest-tag
annotations:
pod-policies.kyverno.io/autogen-controllers: none
results:
- policy: disallow-latest-tag
rule: require-image-tag
resource: bad-pod
kind: Pod
result: fail
# 运行测试
kyverno test policies/
# OPA:conftest 直接检查 YAML/JSON 文件
conftest test pod.yaml -p policy.rego --namespace k8sallowedrepos
# OPA 原生单元测试(测试函数直接在 Rego 中编写)
opa test policy.rego policy_test.rego -v
kyverno test / opa test,策略合并到 main 前必须全部用例通过,否则改坏策略会静默放行恶意资源。6. 策略发布与 GitOps 管理
生产集群的策略应纳入 Git 仓库,走与代码相同的评审与发布流程:
# 仓库目录结构
policy-repo/
├── cluster/ # 集群级策略(ClusterPolicy / Constraint)
│ ├── baseline/ # 基线级——所有集群强制执行
│ │ └── disallow-latest-tag.yaml
│ ├── medium/ # 中等——大部分业务命名空间
│ │ └── require-resource-limits.yaml
│ └── high/ # 高级——合规敏感命名空间
│ └── verify-image-signature.yaml
├── namespaces/ # 命名空间级策略(按团队隔离)
│ ├── payments/...
│ └── checkout/...
└── tests/ # kyverno test 用例
# CI 流水线(GitHub Actions 片段)
# 1. kyverno test 策略测试
# 2. kubectl diff --server-side -f cluster/(预览集群差异)
# 3. 人工评审后合并到 main
# 4. ArgoCD / Flux 自动同步到集群(与 GitOps 章节一致)
策略分级管理
| 级别 | 示例策略 | 执行模式 | 适用命名空间 |
|---|---|---|---|
| 基线(Baseline) | 禁止 privileged、必须 runAsNonRoot | 先 Audit 4 周,后 Enforce | 全部 |
| 中等(Medium) | 资源限制、可信镜像仓库 | Audit 观察后 Enforce | 大部分业务 |
| 高级(High) | 镜像签名校验、禁止 hostPath | 直接 Enforce + 异常审批 | 支付、合规敏感业务 |
7. 策略引擎的韧性与性能
Admission Webhook 是写入路径上的「串行检查」,它的可用性与延迟直接影响整个集群的写入能力:
- Webhook 超时:API Server 等待 webhook 响应的默认超时为 10s,策略执行超时会直接拒绝请求——把
timeoutSeconds调大到 20-30s,避免大资源在复杂策略下超时。 - failurePolicy:
Fail时 webhook 不可用 = 相关写操作全部失败;Ignore时 webhook 挂掉 = 策略形同虚设。生产建议保持 Fail,但给引擎配 HA(多副本 + leader election)与监控告警。 - 策略数量:每个请求要执行所有匹配的策略,数百条策略时写入延迟明显上升——用
match的 namespace 范围限制覆盖面,定期清理冗余策略。 - Mutation 顺序:Mutate 在 Validate 之前执行,Kyverno 的 mutate 结果会再触发一次 admission 检查——保证 mutate 注入的值能通过自己的 validate,否则形成死循环拦截。
常见错误
- 策略不生效(Enforce 模式下资源仍可创建):检查
validationFailureAction是否设为Enforce而非Audit;确认策略match的kinds和apiGroups正确匹配目标资源类型。 - Kyverno 策略导致所有 Pod 创建被拦截:策略范围过广或匹配了系统命名空间。添加
exclude规则排除kube-system等系统命名空间,或先用Audit模式观察。 - OPA Gatekeeper Rego 语法报错:
violation规则必须返回msg字段,注意 Rego 缩进和引号匹配。用opa test本地调试策略逻辑。 - Generate 策略未自动创建资源:确认
synchronize: true已设置,检查目标命名空间是否存在标签冲突,查看 Kyverno Pod 日志中的 webhook 错误。 - Audit 模式下 PolicyReport 为空:新部署的策略需要等待审计周期(默认几分钟),可手动触发审计或检查
background: true是否开启。 - Mutate 注入的值被自己的 Validate 拦截:Mutate 先于 Validate 执行,注入值必须满足本策略的校验规则(如注入 runAsNonRoot 的同时 validate 也要求它),否则形成死循环拦截。
- Webhook 超时导致大资源写入失败:复杂策略 + 大对象超出 webhook 响应时限,调大
timeoutSeconds,并避免对超大资源(如大型 ConfigMap)做全量遍历检查。
最佳实践
- 先 Audit 后 Enforce:新策略先用
Audit模式运行一段时间,确认无误后再切换到Enforce拦截模式。 - 排除系统命名空间:为所有策略添加
exclude.namespaces: [kube-system, kube-public],避免影响集群核心组件。 - 策略即代码:将策略 YAML 纳入 Git 仓库管理,通过 CI/CD 流水线自动部署,确保变更可追溯。
- 分层策略管理:集群级策略用
ClusterPolicy,命名空间级用Policy,实现不同团队的策略隔离。 - 定期审查策略:使用
PolicyReport和Constraint审计结果定期清理过时或冗余的策略。 - 给策略引擎配 HA 与监控:Kyverno / Gatekeeper 多副本部署,把拒绝率、webhook 延迟接入 Prometheus 告警。
- 控制策略数量与覆盖面:用 namespace 范围限制 match 覆盖面,每季度用 PolicyReport 清理冗余策略,保持写入路径低延迟。
练习题
- 编写一个 Kyverno ClusterPolicy,要求所有 Pod 必须设置
app标签,且标签值不能为空。先用Audit模式验证,再切换到Enforce。 - 使用 OPA Gatekeeper 创建一个 ConstraintTemplate,禁止创建 CPU 资源限制超过 4 核的 Pod,并应用到生产命名空间。
- 对比 Kyverno 和 OPA 的 Mutate 能力:分别为同一个 Pod 编写自动注入
securityContext的策略,记录两种方案的配置复杂度差异。 - 故意把一条 Validate 策略写错(如 pattern 语法错误),部署到测试集群,用 webhook 日志与本地
kyverno test对比定位问题,记录排错路径。 - 设计「策略上线五步流程」:Audit 观察 → 试点命名空间 → 全量 Enforce → 监控拒绝率 → 定期审查,写成一页纸 SOP。
学习检查点
学完本章后,请检验自己是否掌握以下内容:
| 检查项 | 自测问题 | 验证方法 |
|---|---|---|
| 概念理解 | 能用自己的话解释 OPA/Rego 的策略即代码原理 | 尝试向他人讲解 |
| 命令操作 | 能不查文档完成 OPA 安装和 Rego 策略编写 | 在终端实际执行 |
| 原理掌握 | 能说出 OPA 的策略评估引擎和数据模型原理 | 画出流程图 |
| 故障排查 | 能独立排查 OPA 策略评估结果不正确的问题 | 模拟故障并修复 |
| 最佳实践 | 能说明为什么需要为基础设施即代码配置策略准入控制 | 对比不同方案 |
本章总结
RBAC 控制「谁能做什么」,策略引擎控制「资源必须满足什么条件」,二者在 Admission 阶段互补。Kyverno 用 YAML 贴近 K8s 生态,OPA Gatekeeper 用 Rego 服务多平台。无论选哪个,都应先 Audit 观察再 Enforce 拦截,并将策略纳入版本管理。
延伸阅读
- container_sec01 Trivy 镜像漏洞扫描(构建时安全)
- container_sec02 Falco 运行时安全监控(运行时安全)
- k8s04 K8s NetworkPolicy 网络策略
- k8s06 Service Mesh 入门(Istio mTLS)