Kyverno 策略类型总览

Kyverno 的策略主要分为五类:“校验”、“变更”、“生成”、“删除”、“镜像验证”。它们分别解决 Kubernetes 资源准入控制、配置自动修正、资源自动创建、资源生命周期管理以及容器镜像供应链安全问题 [1]。

策略类型作用apiVersion状态应用场景
ValidatingPolicy校验资源配置是否符合要求,决定允许、拒绝或审计policies.kyverno.io/v1Stable( v1.18)禁止特权容器、限制镜像仓库、要求资源配额
MutatingPolicy自动修改资源字段,使资源满足平台规范policies.kyverno.io/v1Stable( v1.18)自动添加 Label、设置默认安全上下文、补充资源限制
GeneratingPolicy根据触发条件自动创建、同步或克隆其他资源policies.kyverno.io/v1Stable( v1.18)新建 Namespace 时自动生成 NetworkPolicy、ResourceQuota
DeletingPolicy按条件和调度规则自动删除资源policies.kyverno.io/v1Stable( v1.18)清理过期 Job、临时 Pod、测试资源
ImageValidatingPolicy验证容器镜像的签名、证明材料和可信来源policies.kyverno.io/v1Stable( v1.18)Cosign 签名验证、SBOM、构建来源证明
ClusterPolicy旧版API,统一策略,支持 validate; mutate; generate; verifyImages 规则kyverno.io/v11.19 弃用,v1.20 移除旧版API
CleanupPolicy旧版API,资源定时清理策略kyverno.io/v21.19 弃用,v1.20 移除旧版API
tip
Kyverno v1.19 和 1.20 的存储版本不同: Kyverno v1.19 的 “policies.kyverno.io” 资源虽然对外使用 v1,但 etcd 存储版本仍为 v1beta1;官方计划将在 v1.20 切换存储版本至 v1。

CEL 策略详解

ValidatingPolicy

ValidatingPolicy 是 Kubernetes 官方 API “ValidatingAdmissionPolicy” 的超集,增加了 Kyverno 专有字段

核心功能:判断 Kubernetes 资源是否满足安全、合规、配置规范,并根据验证动作拒绝或审计。

常见用途包括:

  • 禁止 privileged: true;HostNetwork;HostPID 等高风险配置。
  • 限制镜像来源,只允许企业 Harbor 仓库。
  • 要求 Pod 配置 CPU/Memory Requests 和 Limits。
  • 根据 Namespace 或 CMDB 的团队归属信息校验 Label。
  • 要求生产环境 Deployment 至少运行两个副本。

实例:生产环境禁止特权容器

这里的 “all()” 要求所有普通容器都满足条件,“Deny” 表示违规时拒绝请求。

示例中仅检查 containers (”object.spec.containers“)

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
  name: disallow-privileged-containers
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  validationActions:
    - Deny
  validations:
    - expression: >-
        !has(object.spec.containers) ||
        object.spec.containers.all(c,
          !has(c.securityContext) ||
          !has(c.securityContext.privileged) ||
          c.securityContext.privileged == false
        )        
      message: "Privileged containers are not allowed"

MutatingPolicy

“MutatingPolicy” 也是 Kubernetes “MutatingAdmissionPolicy” 的超集;

功能: 在创建或更新资源时自动修改配置,也能在适当的策略机制下处理已有资源。例如

  • 自动添加 team; owner等 Label。
  • 根据 Namespace 设置环境标签。
  • 自动配置默认安全上下文。
  • 设置 NodeSelector、Tolerations 等调度配置。
  • 从 CMDB 获取负责人、运维团队、开发团队信息并写入 Pod。
tip
MutatingPolicy 支持 ApplyConfiguration和 JSONPatch两种变更方式。前者适合声明式字段合并,后者适合精确路径操作。

实例:自动添加固定标签

新建 Pod 时自动添加 “managed_by=platform” 和 “env=production”。

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
  name: add-platform-labels
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]
  mutations:
    - patchType: ApplyConfiguration
      applyConfiguration:
        expression: >
          Object{
            metadata: Object.metadata{
              labels: Object.metadata.labels{
                managed_by: "platform",
                environment: "production"
              }
            }
          }          

实例:添加动态标签

使用 GlobalContextEntry 获取 HTTP 数据

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: kyverno.io/v2
kind: GlobalContextEntry
metadata:
  name: cmdb-workloads
spec:
  apiCall:
    method: GET
    service:
      url: http://cmdb-server:8080/api/v1/k8s/workloads
    refreshInterval: 5m # 每 5 分钟刷新缓存
    retryLimit: 3

MutatingPolicy 根据 CMDB 自动添加 Label

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
  name: inject-cmdb-labels
spec:
  # 策略执行错误时拒绝准入
  failurePolicy: Fail
  # 只处理新创建的 Pod
  matchConstraints:
    resourceRules:
      - apiGroups:
          - ""
        apiVersions:
          - v1
        operations:
          - CREATE
        resources:
          - pods
  # 处理携带应用名称标签的 Pod
  # app.kubernetes.io/name
  matchConditions:
    - name: has-application-label
      expression: >-
        has(object.metadata.labels) &&
        'app.kubernetes.io/name' in object.metadata.labels        

  variables:
    - name: cmdb
      expression: >-
        # 获取从 CMDB 缓存数据
        globalContext.Get("cmdb-workloads", {})        
	
    - name: namespaceName
      expression: >-
        object.metadata.namespace        
	
    - name: moduleName
      expression: >-
        object.metadata.labels["app.kubernetes.io/name"]        
	# 判断 CMDB 是否存在 Namespace
    - name: namespaceExists
      expression: >-
        "workloads" in variables.cmdb &&
        variables.namespaceName in variables.cmdb.workloads        
	# # 判断 CMDB 是否存在对应模块
    - name: moduleExists
      expression: >-
        variables.namespaceExists &&
        variables.moduleName in
        variables.cmdb.workloads[variables.namespaceName]        

    - name: workload
      expression: >-
        variables.moduleExists ?
        variables.cmdb.workloads[variables.namespaceName][variables.moduleName] :
        {}        

  mutations:
    # 应用对应负责人相关Label
    - patchType: ApplyConfiguration
      applyConfiguration:
        expression: |
          variables.moduleExists ?
          Object{
            metadata: Object.metadata{
              labels: {
                "owner": string(variables.workload.owner),
                "module-owner": string(variables.workload.moduleOwner),
                "sre-owner": string(variables.workload.sreOwner),
                "dev-owner": string(variables.workload.devOwner),
                "cost-center": string(variables.workload.costCenter)
              }
            }
          } :
          Object{}          

GeneratingPolicy

用 CEL 创建 Kubernetes 资源

主要功能:根据触发资源的属性和策略条件,自动创建、克隆或管理目标资源。

实例:新建 Namespace 时克隆镜像拉取 Secret

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: clone-image-pull-secret
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: ['']
        apiVersions: ['v1']
        operations: ['CREATE']
        resources: ['namespaces']
  variables:
    - name: targetNs
      expression: 'object.metadata.name'
    - name: sourceSecret
      expression: resource.Get("v1", "secrets", "default", "regcred")
  generate:
    - expression: generator.Apply(variables.targetNs, [variables.sourceSecret])

与 MutatingPolicy 的对比:

  • MutatingPolicy 修改匹配资源本身;
  • GeneratingPolicy 为一个 Namespace 的创建而生成多个不同类型的资源。

核心概念:Trigger; Source; Downstream [2]

  • Trigger:谁触发策略
  • Source:从哪里复制资源
  • Downstream:最终生成什么
text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: clone-image-pull-secret
spec:
  # Trigger 触发资源
  matchConstraints:
    resourceRules:
      - apiGroups: ['']
        apiVersions: ['v1']
        operations: ['CREATE']
        resources: ['namespaces']
  variables:
    - name: targetNs
      expression: 'object.metadata.name'
      # Source 源资源
    - name: sourceSecret
      expression: resource.Get("v1", "secrets", "default", "regcred")
  # Downstream 下游资源创建
  generate:
    - expression: generator.Apply(variables.targetNs, [variables.sourceSecret])

Data Source

在 GeneratingPolicy 中定义 Kubernetes 资源,通过 CEL 自动生成,不需要从已有资源克隆。如:创建 Namespace 时,自动创建一个名为 text 的 ConfigMap。

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: generate-configmap

spec:
  # Trigger
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["namespaces"]

  variables:
    - name: nsName
      # 获取 Trigger 的名称,如 test,dev,prod 等
      # object:当前触发策略的 Kubernetes 对象,上面配置是新创建的ns
      expression: 'object.metadata.name'

    # Downstream
    - name: downstream
      expression: >-
        [
          {
            "apiVersion": dyn("v1"),
            "kind": dyn("ConfigMap"),
            "metadata": dyn({
              "name": "app-config",
              "namespace": variables.nsName
            }),
            # 将值转换为 CEL 动态类型,构造 Kubernetes 对象	dyn({"LOG_LEVEL":"info"})
            "data": dyn({
              "LOG_LEVEL": "info",
              "APP_ENV": "production"
            })
          }
        ]        
  generate:
    - expression: generator.Apply(variables.nsName, variables.downstream)

示例: 在名称空间内创建默认 “deny” 的 NetworkPolicy

tip
“dyn()” 的作用:告诉 CEL 类型检查器,这个值按动态类型处理
text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
## v1beta1 will work with Kyverno v1.16
## for v1.15, use v1alpha1
apiVersion: policies.kyverno.io/v1beta1
kind: GeneratingPolicy
metadata:
  name: generate-network-policy
spec:
  matchConstraints:
    resourceRules:
    - apiGroups:   [""]
      apiVersions: ["v1"]
      operations:  ["CREATE"]
      resources:   ["namespaces"]
  variables:
    - name: nsName
      expression: "object.metadata.name"
  generate:
    - expression: >
        generator.Apply(variables.nsName, [
          {
            "kind": dyn("NetworkPolicy"),
            "apiVersion": dyn("networking.k8s.io/v1"),
            "metadata": dyn({
              "name": "default-deny",
            }),
            "spec": dyn({
              "podSelector": dyn({}),
              "policyTypes": dyn(["Ingress", "Egress"])
            })
          }]
        )

YAML 模板

直接编写 YAML,通过 CEL 动态填充字段,无需使用 dyn() 构造复杂对象。

示例:创建 ns 时自动生成 NetworkPolicy 和 ConfigMap

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: generate-default-resources
spec:
  # Trigger:新建 Namespace
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["namespaces"]

  variables:
    - name: nsName
      expression: 'object.metadata.name'

  generate:
    - template:
        interpolate: cel
        value: |
          apiVersion: networking.k8s.io/v1
          kind: NetworkPolicy
          metadata:
            name: default-deny
            namespace: (( variables.nsName ))
          spec:
            podSelector: {}
            policyTypes:
              - Ingress
              - Egress

          ---
          apiVersion: v1
          kind: ConfigMap
          metadata:
            name: app-config
            namespace: (( variables.nsName ))
          data:
            LOG_LEVEL: info          

资源克隆 - Clone

从已有 Kubernetes 资源复制出新资源,例如将 “kube-system” 的 Secret 复制到新 Namespace。

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: clone-secrets
spec:
  # Trigger:创建 Namespace
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["namespaces"]

  variables:
    # 目标 Namespace
    - name: nsName
      expression: 'object.metadata.name'

    # Source:获取单个 Secret
    - name: source
      expression: resource.Get("v1", "secrets", "default", "regcred")

  # Downstream:生成 Secret 副本
  generate:
    - expression: generator.Apply(variables.nsName, [variables.source])

循环批量生成 - Loop

使用 CEL 循环,在多个 NS 中生成资源,而不需要为每个 NS 创建独立策略。

示例:在创建 ConfigMap 时读取 Namespace 列表,向指定 Namespace 复制 Secret。

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: clone-secret-loop
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["configmaps"]

  variables:
    # 获取目标 Namespace 列表
    - name: nsList
      expression: "object.data.namespaces.split(',')"

    # 获取源 Secret
    - name: source
      expression: resource.Get("v1", "secrets", "default", "regcred")

  generate:
    # 遍历 Namespace,逐个生成
    - expression: >
        variables.nsList.all(
          ns,
          generator.Apply(ns, [variables.source])
        )        

还可以使用 使用 filter() 排除 Namespace

text
1
2
3
variables.nsList
  .filter(ns, ns != "prod")
  .all(ns, generator.Apply(ns, [variables.source]))

资源同步 - Synchronization

让 Kyverno 持续检查 Downstream 是否符合期望状态,如果不一致,就自动修复。

示例

text
1
2
3
4
spec:
  evaluation:
    synchronize:
      enabled: true

存量资源生成 - Generating for Existing

创建策略时,对已经存在的 Trigger 资源执行一次生成逻辑。、

示例中策略将 default/regcred 克隆到 “已有” 及 “未来” 新建的 Namespace

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: generate-existing-secrets
spec:
  evaluation:
    generateExisting:
      enabled: true

  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["namespaces"]

  variables:
    - name: nsName
      expression: 'object.metadata.name'

    - name: source
      expression: resource.Get("v1", "secrets", "default", "regcred")

  generate:
    - expression: generator.Apply(variables.nsName, [variables.source])

策略作用域 - Policy Scope

“Policy Scope” 提供策略是集群范围还是 namespace 范围

示例,只对 “prod” 中创建的 Pod 触发策略,并生成一个默认 ConfigMap。

tip
上面示例都是使用 “GeneratingPolicy” 集群范围内的策略,“NamespacedGeneratingPolicy” 仅针对一个 NS 生效
yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: policies.kyverno.io/v1
kind: NamespacedGeneratingPolicy
metadata:
  name: generate-app-config
  namespace: production
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]

  generate:
    - template:
        interpolate: cel
        value: |
          apiVersion: v1
          kind: ConfigMap
          metadata:
            name: app-config
            namespace: prod
          data:
            APP_ENV: prod          

DeletingPolicy

使用 cron schedule 自动删除符合条件的资源,对应旧的 CleanupPolicy (v1.19 为 Stable)。与 admission 策略不同,它定时运行、评估集群里已有资源,条件满足即删除。

使用示例:每天 1 点清理测试 Namespace 里带 old=true 标签的 Pod

用途:清理已完成的 Job 等孤儿资源、移除过期 Secret 或 ConfigMap、给关键资源设置有时限的租约。

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: policies.kyverno.io/v1
kind: DeletingPolicy
metadata:
  name: cleanup-old-test-pods
spec:
  schedule: '0 1 * * *' 
  matchConstraints:
    resourceRules:
      - apiGroups: ['']
        apiVersions: ['v1']
        operations: ['*']
        resources: ['pods']
        scope: 'Namespaced'
    namespaceSelector:
      matchLabels:
        environment: test
  conditions:
    - name: isOld
      expression: "has(object.metadata.labels.old) && object.metadata.labels.old == 'true'"
  variables:
    - name: isEphemeral
      expression: "has(object.metadata.labels.ephemeral) && object.metadata.labels.ephemeral == 'true'"

ImageValidatingPolicy

ImageValidatingPolicy 用于校验容器镜像签名与 attestation

4个核心概念

术语作用
Image被验证的目标,如 nginx:1.31
Signature证明镜像由指定密钥或身份签署,且签名绑定的内容未被篡改
Attestor定义信任哪个公钥、证书或身份
Attestation描述镜像构建来源、SBOM、安全扫描等信息

示例1 只允许使用指定公钥签名的镜像

什么时候执行?:matchConstraints

检查哪些镜像?:matchImageReferences

信任谁?:attestors.company

验证那些镜像?:verifyImageSignatures

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
  name: verify-image
spec:
  validationActions: [Deny]
  # 匹配 Pod 创建或更新
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  # 匹配需要验证的镜像,决定哪些镜像需要执行签名校验
  matchImageReferences:
    - glob: "ghcr.io/example/*"
  # 定义可信签名公钥
  # Attestor 是信任配置,不是执行签名的服务。
  attestors:
    - name: company
      cosign:
        key:
          data: |
            -----BEGIN PUBLIC KEY-----
            <公司 Cosign 公钥>
            -----END PUBLIC KEY-----            

  validations:
    - expression: >-
        images.containers.map(image,
          verifyImageSignatures(image, [attestors.company])
        ).all(result, result > 0) # 验证每个镜像,result > 0 表示至少一个签名验证通过。        
      message: "Image signature verification failed"

示例2: 只允许 GitHub Actions 构建并签名的镜像

示例中不使用固定公钥,而是通过无密钥文件签名验证签名者身份。假设只信任 GitHub 仓库 user/repo 中配置的工作流

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
  name: verify-github
spec:
  validationActions: [Deny]

  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]

  matchImageReferences:
    - glob: "ghcr.io/nginx/*"

  attestors:
    - name: githubCI
      cosign:
        keyless:
          identities:
            - subject: "https://github.com/user/user/.github/workflows/build.yml@refs/heads/main"
              issuer: "https://token.actions.githubusercontent.com"

  validations:
    - expression: >-
        images.containers.map(image,
          verifyImageSignatures(image, [attestors.githubCI])
        ).all(e, e > 0)        
      message: "Image must be signed by trusted GitHub Actions"

Reference

[1] policy overview

[2] Core Concepts