检查资源配置是否符合策略。
验证规则可能是您将使用的最常见和最实用的规则类型,也是 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
时,策略违规会记录在 PolicyReport
或 ClusterPolicyReport
中,但允许创建或更新资源。对于违反新创建的 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
检查资源数据的验证规则被定义为提供所需配置的覆盖模式。资源配置必须匹配模式中定义的字段和表达式才能通过验证规则。处理覆盖模式时遵循以下规则:
-
如果在模式中定义了字段而配置中不存在该字段,则验证将失败。
-
未定义的字段被视为通配符。
-
带有通配符值“*”的验证模式字段将匹配零个或多个字母数字字符。也匹配空值,但字段缺失时不匹配。
-
带有通配符值“?”的验证模式字段将匹配任何单个字母数字字符。 空值或缺失字段不匹配。
-
具有通配符值“?*”的验证模式字段将匹配任何字母数字字符,并要求该字段存在非空值。
-
值为 null 或 ""(空字符串)的验证模式字段要求该字段未定义或没有值。
-
仅当字段值之一与模式中定义的值匹配时,才执行同级的验证。您可以使用条件锚来显式指定必须匹配的字段值。 这允许编写诸如“如果字段A等于 X,则字段B必须等于 Y”之类的规则。
-
仅当父项与模式匹配时才执行子值的验证。
通配符
-
*
- 匹配零个或多个字母数字字符 -
?
- 匹配单个字母数字字符
有关通配符如何在规则中工作的几个示例,请参阅以下内容。
该策略要求所有 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
的逻辑或,可用于检查列表中的任何一种模式是否匹配。
注意:规则中允许使用 pattern
或 anyPattern
之一;它们不能在同一规则中声明。
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
来检查资源之外,验证规则还可以基于以表达式形式编写的一组条件来拒绝请求。
你可以使用 match
和 exclude
什么时候应用策略,然后在 deny
声明中用额外的条件进行更细粒度的控制。
注意:使用 deny
声明时,validationFailureAction
必须被设置为 enforce
以中断请求。
请参阅使用前置条件基于变量匹配规则。deny
可以像 preconditions
一样使用 any
和 all
块。
除了准入审查请求数据、用户信息和内置变量之外,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。
网友评论