Kyverno验证资源

检查资源配置是否符合策略。

验证规则可能是您将使用的最常见和最实用的规则类型,也是 Kyverno 等准入控制器的主要用例。在一个典型的验证规则中,定义了创建给定资源时应强制的属性。当用户或进程创建新资源时,Kyverno 会根据验证规则检查该资源的属性。如果这些属性得到验证,意味着达成一致,则允许创建资源。如果这些属性不同,则阻止创建。Kyverno 如何响应失败的验证检查的行为由 validationFailureAction 字段决定。它可以被阻止(enforce)或在策略报告中记录(audit)。无论是在初始创建时还是在 Kyverno 启动 Kubernetes 资源的定期扫描时,在 audit 模式中,验证规则也可用于获取资源匹配时有关违反规则的情况报告。在 audit 模式中,违反现有规则的说明也将出现在相关资源的事件中。

要验证资源数据,请在验证规则中定义一个pattern。要拒绝某些 API 请求,请在验证规则中定义一个deny元素以及一组控制何时允许或拒绝请求的条件。

基本验证

下面的 ClusterPolicy 是验证新创建的 Namespace 是否有标签 purpose=production。

apiVersion: kyverno.io/v1
# ClusterPolicy 类型应用于整个集群
kind: ClusterPolicy
metadata:
  name: require-ns-purpose-label
# `spec` 定义了策略的属性
spec:
  # `validationFailureAction` 告知 Kyverno 当资源验证失败是,应该中断(enforce)还是报告(audit)
  validationFailureAction: enforce
  # `rules` 是一个或多个必须为 true 的规则。
  rules:
  - name: require-ns-purpose-label
    # `match` 设置了要检查的范围。在这个case中,要检查所有的 `Namespace`
    match:
      any:
      - resources:
          kinds:
          - Namespace
    # The `validate` statement tries to positively check what is defined. If the statement, when compared with the requested resource, is true, it is allowed. If false, it is blocked.
    validate:
      # `message` 在规则验证失败并中断后,给用户展示的提示信息
      message: "You must have label `purpose` with value `production` set on all new namespaces."
      # `pattern` 对象定义了资源检查的模式。在这个 case 中,会在 `metadata.labels` 中查找`purpose=production`
      pattern:
        metadata:
          labels:
            purpose: production

如果将具有以下定义的新命名空间提交给 Kyverno,则根据上面的 ClusterPolicy,它将被允许(有效)。这是因为它包含标签 purpose=production,这是规则中唯一验证的模式。

apiVersion: v1
kind: Namespace
metadata:
  name: prod-bus-app1
  labels:
    purpose: production

相比之下,如果提交了具有以下定义的新命名空间,鉴于上面的 ClusterPolicy,它将被阻止(无效)。如您所见,其 purpose 标签的值与策略中要求的值不同。但这并不是验证失败的唯一方式。 例如,如果请求了相同的命名空间,但没有定义任何标签,它也会因为同样的原因被阻止。

apiVersion: v1
kind: Namespace
metadata:
  name: prod-bus-app1
  labels:
    purpose: development

将上述清单另存为 ns.yaml 并尝试使用示例 ClusterPolicy 创建它。

$ kubectl create -f ns.yaml
Error from server: error when creating "ns.yaml": admission webhook "validate.kyverno.svc" denied the request:

resource Namespace//prod-bus-app1 was blocked due to the following policies

require-ns-purpose-label:
  require-ns-purpose-label: 'Validation error: You must have label `purpose` with value `production` set on all new namespaces.; Validation rule require-ns-purpose-label failed at path /metadata/labels/purpose/'

development 改为 production,然后重试。 Kyverno 允许创建新的命名空间资源。

验证失败操作

validationFailureAction 属性控制不符合策略的资源的准入控制行为。如果将该值设置为 enforce,则资源创建或更新会在资源不符合要求时被阻止。当值设置为 audit 时,策略违规会记录在 PolicyReportClusterPolicyReport 中,但允许创建或更新资源。对于违反新创建的 enforce 模式策略的现有资源,Kyverno 将允许对继续违反策略的资源进行后续更新,以确保现有资源不会受到影响。但是,如果对违规资源的后续更新使其符合要求,则会阻止任何会产生违规的进一步更新。

验证失败操作覆盖

使用 validationFailureActionOverrides,您可以指定每个命名空间应用哪些操作。 此属性仅适用于 ClusterPolicy

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-label-app
spec:
  validationFailureAction: audit
  validationFailureActionOverrides:
    - action: enforce   # 采取的操作
      namespaces:       # 受影响的命名空间列表
        - default
    - action: audit
      namespaces:
        - test
  rules:
    - name: check-label-app
      match:
        any:
        - resources:
            kinds:
            - Pod
      validate:
        message: "The label `app` is required."
        pattern:
          metadata:
            labels:
              app: "?*"

上述策略,对于 default 命名空间,validationFailureAction 被设置为 enforce,而对于 test 命名空间,被设置为 audit。对于其它的命名空间,使用 validationFailureAction 中设置的操作。

Patterns

检查资源数据的验证规则被定义为提供所需配置的覆盖模式。资源配置必须匹配模式中定义的字段和表达式才能通过验证规则。处理覆盖模式时遵循以下规则:

  1. 如果在模式中定义了字段而配置中不存在该字段,则验证将失败。

  2. 未定义的字段被视为通配符。

  3. 带有通配符值“*”的验证模式字段将匹配零个或多个字母数字字符。也匹配空值,但字段缺失时不匹配。

  4. 带有通配符值“?”的验证模式字段将匹配任何单个字母数字字符。 空值或缺失字段不匹配。

  5. 具有通配符值“?*”的验证模式字段将匹配任何字母数字字符,并要求该字段存在非空值。

  6. 值为 null 或 ""(空字符串)的验证模式字段要求该字段未定义或没有值。

  7. 仅当字段值之一与模式中定义的值匹配时,才执行同级的验证。您可以使用条件锚来显式指定必须匹配的字段值。 这允许编写诸如“如果字段A等于 X,则字段B必须等于 Y”之类的规则。

  8. 仅当父项与模式匹配时才执行子值的验证。

通配符

  1. * - 匹配零个或多个字母数字字符

  2. ? - 匹配单个字母数字字符

有关通配符如何在规则中工作的几个示例,请参阅以下内容。

该策略要求所有 Pod 中的所有容器都定义了资源请求和限制(故意省略了 CPU 限制):

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: all-containers-need-requests-and-limits
spec:
  validationFailureAction: enforce
  rules:
  - name: check-container-resources
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "All containers must have CPU and memory resource requests and limits defined."
      pattern:
        spec:
          containers:
          # 选择 Pod 中的所有容器。 `name` field here is not specifically required but serves
          # as a visual aid for instructional purposes.
          - name: "*"
            resources:
              limits:
                # '?' requires 1 alphanumeric character and '*' means that
                # there can be 0 or more characters. Using them together
                # e.g. '?*' requires at least one character.
                memory: "?*"
              requests:
                memory: "?*"
                cpu: "?*"

以下验证规则检查 Deployment、StatefulSet 和 DaemonSet 资源中的标签:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-label-app
spec:
  validationFailureAction: enforce
  rules:
    - name: check-label-app
      match:
        any:
        - resources:
            kinds:
            - Deployment
            - StatefulSet
            - DaemonSet
      validate:
        message: "The label `app` is required."
        pattern:
          spec:
            template:
              metadata:
                labels:
                  app: "?*"

要将通配符等特殊字符视为字面值,请参阅 JMESPath 页面中的此部分

运算符

除了标量值之外,从 Kyverno 1.3.6 开始支持以下列表值的运算符。一些运算符也支持持续时间(如 12h)和语义化版本(如 1.4.1)检查。

运算符 含义
> 大于
< 小于
>= 大于等于
<= 小于等于
! 不等于
| 逻辑或
& 逻辑与
- 区间内
!- 区间外

注意

注意:- 运算符提供了一种更简单的方法来验证目标值是否落在闭合区间 [a,b] 内。 因此,条件 a-b 等效于 value >= a & value <= b

注意:!- 运算符提供了一种更简单的方法来验证目标值是否落在闭区间 [a,b] 之外。 因此,条件 a!-b 等效于编写 value < a | value > b

注意:没有用于 equals 的运算符,因为在模式中提供字段值需要与该值相等。

锚点

锚点允许条件处理(即“if-then-else”)和验证模式中的其他逻辑检查。

支持以下类型的锚点:

锚点 Tag 行为
Conditional () 如果指定了具有给定值的标签(包括子元素),则将处理对等元素。
例如,如果镜像具有最新标签,则imagePullPolicy不能为IfNotPresent。
(image): “*:latest”
imagePullPolicy:
“!IfNotPresent”
Equality =() 如果指定了标签,则处理继续。对于具有标量值的标签,该值必须匹配。对于具有子元素的标签,子元素将进一步评估为验证模式。
例如,如果定义了hostPath,则路径不能为 /var/lib

=(hostPath):
path: “!/var/lib”
Existence ^() 仅适用于列表/数组类型。列表中至少一个元素是否满足该模式。相反,条件锚将验证列表中的所有元素均与模式匹配。
例如,必须至少有一个容器使用镜像 nginx:latest。

^(containers):
- image: nginx:latest
Negation X() 无法指定标签。不评估标记的值(使用感叹号来否定值)。理想情况下,该值应设置为 null。

例如,不能定义 Hostpath。
X(hostPath):
Global <() 这个条件的内容如果为假,会导致整个规则被跳过。适用于验证和战略合并补丁。

anyPattern

在某些情况下,可以在不同级别定义内容。例如,security context 可以被定义为 Pod 或 Container 级别。如果满足任一条件,则验证规则应通过。

anyPattern 是对多个验证 pattern 的逻辑,可用于检查列表中的任何一种模式是否匹配。

注意:规则中允许使用 patternanyPattern 之一;它们不能在同一规则中声明。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-non-root
spec:
  background: true
  validationFailureAction: enforce
  rules:
  - name: check-containers
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: >-
        Running as root is not allowed. The fields spec.securityContext.runAsNonRoot,
        spec.containers[*].securityContext.runAsNonRoot, and
        spec.initContainers[*].securityContext.runAsNonRoot must be `true`.                
      anyPattern:
      # spec.securityContext.runAsNonRoot must be set to true. If containers and/or initContainers exist which declare a securityContext field, those must have runAsNonRoot also set to true.
      - spec:
          securityContext:
            runAsNonRoot: true
          containers:
          - =(securityContext):
              =(runAsNonRoot): true
          =(initContainers):
          - =(securityContext):
              =(runAsNonRoot): true
      # All containers and initContainers must define (not optional) runAsNonRoot=true.
      - spec:
          containers:
          - securityContext:
              runAsNonRoot: true
          =(initContainers):
          - securityContext:
              runAsNonRoot: true

anyPattern 方法最适合不使用否定条件的验证案例。在上面的例子中,只有一个 spec 内容必须是有效的。

注意:由于 Kubernetes v1.23 中的一个 bug 已在 v1.23.3 中修复,因此在 v1.23 版本中使用 anyPattern 至少需要 v1.23.3。

Deny 规则

除了使用 pattern 来检查资源之外,验证规则还可以基于以表达式形式编写的一组条件来拒绝请求。

你可以使用 matchexclude 什么时候应用策略,然后在 deny 声明中用额外的条件进行更细粒度的控制。

注意:使用 deny 声明时,validationFailureAction 必须被设置为 enforce 以中断请求。

请参阅使用前置条件基于变量匹配规则。deny 可以像 preconditions 一样使用 anyall 块。

除了准入审查请求数据、用户信息和内置变量之外,deny 规则和前置条件还可以对 ConfigMap 数据、来自 API 服务器查找的数据等进行操作

根据标签拒绝 DELETE 请求

这个策略会拒绝所有没有 cluster-admin 角色的用户对于包含 pp.kubernetes.io/managed-by: kyverno 对象的 delete 操作。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: deny-deletes
spec:
  validationFailureAction: enforce
  background: false
  rules:
  - name: block-deletes-for-kyverno-resources
    match:
      any:
      - resources:
          selector:
            matchLabels:
              app.kubernetes.io/managed-by: kyverno
    exclude:
      any:
      - clusterRoles:
        - cluster-admin
    validate:
      message: "Deleting {{request.oldObject.kind}}/{{request.oldObject.metadata.name}} is not allowed"
      deny:
        conditions:
          any:
          - key: "{{request.operation}}"
            operator: Equals
            value: DELETE

阻止对自定义资源的更改

这个策略对拒绝所有对该用户自定义资源的更新或删除操作,除非是来自指定 ServiceAccount 或 Roles 的请求。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-updates-to-custom-resource
spec:
  validationFailureAction: enforce
  background: false
  rules:
  - name: block-updates-to-custom-resource
    match:
      any:
      - resources:
          kinds:
          - SomeCustomResource
    exclude:
      any:
      - subjects:
        - kind: ServiceAccount
          name: custom-controller
      - clusterRoles:
        - custom-controller:*
        - cluster-admin
    validate:
      message: "Modifying or deleting this custom resource is forbidden."
      deny: {}

防止更改 NetworkPolicy 资源

该策略拒绝用户修改名称以 -default 为后缀的 NetworkPolicy 资源。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: deny-netpol-changes
spec:
  validationFailureAction: enforce
  background: false
  rules:
  - name: deny-netpol-changes
    match:
      any:
      - resources:
          kinds:
          - NetworkPolicy
          name: "*-default"
    exclude:
      any:
      - clusterRoles:
        - cluster-admin
    validate:
      message: "Changing default network policies is not allowed."
      deny: {}

foreach

foreach 声明简化了资源声明中子元素的验证,例如 Pod 中的容器。

一个 foreach 声明可以包含多个条目来处理不同的子元素,例如一个处理容器列表,另一个处理 Pod 中的 initContainers 列表。

每个 foreach 条目必须包含一个 list 属性,写为不带大括号的 JMESPath 表达式,它定义了它处理的子元素。例如,使用此 list 声明执行对 Pod 中容器列表的迭代:

list: request.object.spec.containers

处理 foreach 时,Kyverno 引擎会将 list 作为 JMESPath 表达式来处理,以检索零个或多个子元素以进行进一步处理。list 字段的值也可以解析为一个简单的字符串数组,例如在 context 变量中定义的字符串。list 字段的值不应包含在大括号中,即使它是 JMESPath 表达式。

每次迭代时,会将一个 element 变量加入到上下文中。可以使用 element.<name> 引用元素中的数据,这里的 name 是属性名称。例如,当 request.object 是一个 Pod 时,遍历 request.object.spec.containers 列表时,可以在 foreach 里用 element.image 引用容器镜像。

在 foreach 中允许以下子声明:

此外,每个 foreach 声明都可以包含以下声明:

  • Context: 添加仅在每个循环迭代中可用的额外外部数据。

  • Preconditions: 控制何时跳过循环迭代。

  • elementScope: 控制是否使用当前列表元素作为验证范围。如果未指定,则默认为“true”。

这是一个完整的示例,用于强制所有容器映像都来自受信任的注册表:

apiVersion : kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-images
spec:
  validationFailureAction: enforce
  background: false
  rules:
  - name: check-registry
    match:
      any:
      - resources:
          kinds:
          - Pod
    preconditions:
      any:
      - key: "{{request.operation}}"
        operator: NotEquals
        value: DELETE
    validate:
      message: "unknown registry"  
      foreach:
      - list: "request.object.spec.initContainers"
        pattern:
          image: "trusted-registry.io/*"      
      - list: "request.object.spec.containers"
        pattern:
          image: "trusted-registry.io/*"

请注意,pattern 应用于 element,因此不需要指定 spec.containers 并且可以直接引用 element 的属性,在上面的示例中它是一个 container。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容