美文网首页Kyverno
Kyverno验证资源

Kyverno验证资源

作者: 王勇1024 | 来源:发表于2023-01-08 13:08 被阅读0次

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

    验证规则可能是您将使用的最常见和最实用的规则类型,也是 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。

    相关文章

      网友评论

        本文标题:Kyverno验证资源

        本文链接:https://www.haomeiwen.com/subject/pspscdtx.html