用于重用和智能决策的数据驱动策略
变量通过启用对策略定义、准入审查请求和外部数据源(如 ConfigMaps、Kubernetes API Server 甚至 OCI image registries)中的数据的引用,使策略更加智能和可重用。
变量存储为 JSON,Kyverno 支持使用 JMESPath(发音为“James path”)来选择和转换 JSON 数据。使用 JMESPath,以 {{key1.key2.key3}} 的格式引用来自数据源的值。例如,要在 kubectl apply 操作(如命名空间)期间引用传入资源的名称,您可以将其写为变量引用:{{request.object.metadata.name}}。在处理规则之前,策略引擎将使用变量值替换任何格式为 {{ <JMESPath> }} 的值。有关专门探讨 JMESPath 在 Kyverno 中的使用的页面,请参见此处。
注意:目前在 match 或 exclude 语句或 patchesJson6902.path 中不允许使用变量。
预定义变量
Kyverno 自动创建一些有用的变量并在规则中使它们可用:
-
serviceAccountName: “userName”,它是 service account 的最后一部分(即没有前缀 system:serviceaccount:<namespace>:)。例如,在处理来自 system:serviceaccount:nirmata:user1 的请求时,Kyverno 会将值 user1 存储在变量 serviceAccountName 中。
-
serviceAccountNamespace: serviceAccount 的 “namespace” 部分。例如,在处理来自 system:serviceaccount:nirmata:user1 的请求时,Kyverno 会将 nirmata 存储在变量 serviceAccountNamespace 中。
-
request.roles: 存储在数组中的、给定账户可能具有的 role 列表。如["foo:dave"]。
-
request.clusterRoles: 存储在数组中的 cluster role 列表。如 ["dave-admin","system:basic-user","system:discovery","system:public-info-viewer"]
-
images: a map of container image information, if available. See Variables from container images for more information.
注意:request.roles 或 request.clusterRoles 之一将被替换为变量,但不能同时替换两者。
策略定义中的变量
Kyverno 策略定义可以以“快捷方式”的形式引用策略定义中的其他字段。这可能是一种分析和比较值的有用方法,而无需显式定义它们。
为了让 Kyverno 在清单中引用这些现有值,它使用符号 $(./../key_1/key_2)。这可能看起来很熟悉,因为它本质上与 Linux/Unix 系统引用相对路径的方式相同。 例如,可以看下面的策略清单片段。
validationFailureAction: enforce
rules:
- name: check-tcpSocket
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Port number for the livenessProbe must be less than that of the readinessProbe."
pattern:
spec:
^(containers):
- livenessProbe:
tcpSocket:
port: "$(./../../../readinessProbe/tcpSocket/port)"
readinessProbe:
tcpSocket:
port: "3000"
在上面的示例中,Pod Spec 中所有 container 的 readinessProbe.tcpSocket.port 字段必须为 3000,livenessProbe.tcpSocket.port 字段值也必须是相同的值。查找表达式可以被认为是一个 cd 返回三个级别并向下进入 readinessProbe 对象。
可以在查找清单变量中使用运算符,因此可以修改前面的代码片段。
- livenessProbe:
tcpSocket:
port: "$(<./../../../readinessProbe/tcpSocket/port)"
readinessProbe:
tcpSocket:
port: "3000"
在这个示例中,livenessProbe.tcpSocket.port 的字段值必须小于 readinessProbe.tcpSocket.port 的值。
更多信息请参考Operators章节。
转义变量
在某些情况下,您希望编写一个包含变量的规则,以供另一个程序或流程执行操作,而不是供 Kyverno 使用。例如,使用 $() 表示法中的变量,从 Kyverno 1.5.0 开始,这些变量可以使用前导反斜杠 () 进行转义,并且 Kyverno 不会尝试替换值。用 JMESPath 表示法编写的变量也可以使用相同的语法进行转义,例如 {{ request.object.metadata.name }}。
下面的策略中,OTEL_RESOURCE_ATTRIBUTES 的值包含另一个环境变量的引用,例如,$(POD_NAMESPACE)。
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: add-otel-resource-env
spec:
background: false
rules:
- name: imbue-pod-spec
match:
any:
- resources:
kinds:
- v1/Pod
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "?*"
env:
- name: NODE_NAME
value: "mutated_name"
- name: POD_IP_ADDRESS
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
- name: OTEL_RESOURCE_ATTRIBUTES
value: >-
k8s.namespace.name=\$(POD_NAMESPACE),
k8s.node.name=\$(NODE_NAME),
k8s.pod.name=\$(POD_NAME),
k8s.pod.primary_ip_address=\$(POD_IP_ADDRESS),
k8s.pod.service_account.name=\$(POD_SERVICE_ACCOUNT),
rule_applied=$(./../../../../../../../../name)
使用如下 Pod 定义,可以对此进行测试。
apiVersion: v1
kind: Pod
metadata:
name: test-env-vars
spec:
containers:
- name: test-container
image: busybox
command: ["sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv OTEL_RESOURCE_ATTRIBUTES;
sleep 10;
done;
env:
- name: NODE_NAME
value: "node_name"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP_ADDRESS
valueFrom:
fieldRef:
fieldPath: status.podIP
restartPolicy: Never
结果是 OTEL_RESOURCE_ATTRIBUTES 环境变量会被添加到 Pod 中,如下所示:
- name: OTEL_RESOURCE_ATTRIBUTES
value: k8s.namespace.name=$(POD_NAMESPACE), k8s.node.name=$(NODE_NAME), k8s.pod.name=$(POD_NAME),
k8s.pod.primary_ip_address=$(POD_IP_ADDRESS), k8s.pod.service_account.name=$(POD_SERVICE_ACCOUNT),
rule_applied=imbue-pod-spec
AdmissionReview 请求变量
Kyverno 以 webhook 的方式在 Kubernetes 中运行。当 Kubernetes API Server 收到新的请求时(如创建 Pod),API Server将该信息发送给已注册的、用于监听 Pod 创建事件的 webhook。传入的数据会以 AdmissionReview 对象传给 webhook。有四种常用于获取 AdmissionReview 请求中数据属性的方式:
-
{{request.operation}}:正在执行的 API 操作的类型(CREATE、UPDATE、DELETE 或 CONNECT)。
-
{{request.object}}:正在被创建或修改的新对象。对于 DELETE 请求,该对象是null。
-
{{request.oldObject}}:被修改的旧对象。对于 CREATE 和 CONNECT 请求,该对象是null。
-
{{request.userInfo}} :包含有关谁/什么提交了请求的信息,其中包括组和用户名键。
-
{{request.namespace}}:被操作对象的命名空间。
以下是查找此数据的一些示例:
- 引用资源名称(字符串类型)
{{request.object.metadata.name}}
- 引用元数据(对象类型)
{{request.object.metadata}}
- 引用正在创建的新命名空间的名称
{{request.object.name}}
- 引用提交请求的用户名
{{request.userInfo.username}}
AdmissionReview 中的变量也可以和用户定义的字符串相结合,用于 messages 或其它字段。
- 从多个变量构建一个名称(字符串类型)
"ns-owner-{{request.namespace}}-{{request.userInfo.username}}-binding"
让我们看一个如何在 Kyverno 政策中使用此 AdmissionReview 数据的示例。
在下面的 ClusterPolicy 中,我们想知道哪个账户创建了一个给定的 Pod 资源。我们可以使用 AdmissionReview 包含的信息,特别是 username,将这个信息写入 label 中。如下所示:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: who-created-this
spec:
background: false
rules:
- name: who-created-this
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
created-by: "{{request.userInfo.username}}"
这个示例会修改传入的 Pod,并将 kubeconfig 中授权的用户信息写入 created-by 标签中。
创建一个简单的 Pod 资源:
kubectl run busybox --image busybox:1.28
查看新创建的 Pod busybox:
kubectl get po busybox --show-labels
NAME READY STATUS RESTARTS AGE LABELS
busybox 0/1 Pending 0 25m created-by=kubernetes-admin,run=busybox
在输出信息中,我们可以看到 Pod 上有一个标签 created-by=kubernetes-admin,kubernetes-admin 就是创建该 Pod 的用户。
来自容器镜像的变量
Kyverno 能从 AdmissionReview 请求中提取镜像数据,并能够在规则上下文中用 map 类型的变量 images 引用这些数据。例如:
{
"containers": {
"tomcat": {
"registry": "https://ghcr.io",
"name": "tomcat",
"tag": "9"
}
},
"initContainers": {
"vault": {
"registry": "https://ghcr.io",
"name": "vault",
"tag": "v3"
}
}
}
如果一个 AdmissionReview 请求中定义了 containers 或 initContainers,就可以用下面的方式通过 images 引用这些数据。
引用 tomcat 容器中镜像的属性:
- 引用 registry URL
{{images.containers.tomcat.registry}}
- 引用镜像名称
{{images.containers.tomcat.name}}
- 引用镜像tag
{{images.containers.tomcat.tag}}
- 引用摘要
{{images.containers.tomcat.digest}}
引用 vault initContainer 中镜像的属性:
- 引用 registry URL
{{images.initContainers.vault.registry}}
- 引用镜像名称
{{images.initContainers.vault.name}}
- 引用镜像tag
{{images.initContainers.vault.tag}}
- 引用摘要
{{images.initContainers.vault.digest}}
这种相同的模式和镜像变量编排也适用于临时容器。
Kyverno 默认将一个空 registry 设置为 docker.io,并将一个空 tag 设置为 latest。
注意
请注意,对于 JMESPath 中某些字符必须进行转义(如容器名中的-),可以通过使用双引号和双转义字符 \ 来完成转义,例如 {{images.containers."my-container".tag}}。更多信息请参考 JMESPath 格式化。
您还可以获取所有容器的镜像属性以进行进一步处理。例如,{{ images.containers.*.name }} 创建了一个包含所有容器名称的列表。
内联变量
可以在 context 中定义变量以供 Kyverno 规则使用。这可以像静态值、另一个变量或嵌套对象一样简单。也可以使用相同的变量名重新定义变量,将使用最后设置的值。下面设置了一个值为 foo 的 context 变量。
context:
# 唯一的变量名称
# 如果用户重新声明一个名称相同的变量,该变量会被重分配
- name: foodata
variable:
# value 定义了变量的值,它可能包含 jmespath 变量或者任何可以用 JSON 对象表示的 YAML 对象。
# value、default 和 jmespath 是可选的,但必须定义 value 或 jmespath。
value: "foo"
这个片段给 context 变量设置的值为 request.object.metadata.name。如果未定义 value 字段,jmesPath 的内容将作用于整个上下文。
context:
- name: objName
variable:
jmesPath: request.object.metadata.name
变量可以引用其他变量,如下所示。
context:
- name: jpExpression
variable:
value: name
- name: objName
variable:
value:
name: "{{ request.object.metadata.name }}"
jmesPath: "{{ jpExpression }}"
来自外部数据源的变量
一些策略决策需要访问由其他 Kubernetes 控制器或外部应用程序管理的集群资源和数据。 对于这些类型的策略,Kyverno 允许对 Kubernetes API Server进行 HTTP 调用并使用 ConfigMap。
从外部来源获取的数据存储在按规则处理的上下文中,用于由策略引擎评估变量。一旦来自外部源的数据存储在上下文中,就可以像任何其他变量数据一样引用它。
要了解有关 ConfigMap 查找和 API Server 调用的更多信息,请参考 外部数据源。
嵌套查找
也可以将 JMESPath 表达式嵌套在另一个表达式中,例如,当混合来自 ConfigMap 和 AdmissionReview 的数据时。通过在另一个中包含一个 JMESPath 表达式,Kyverno 将首先替换内部表达式,然后再构建外部表达式,如下例所示。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: resource-annotater
spec:
background: false
rules:
- name: add-resource-annotations
context:
- name: LabelsCM
configMap:
name: resource-annotater-reference
namespace: default
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
annotations:
foo: "{{LabelsCM.data.{{ request.object.metadata.labels.app }}}}"
在这个示例中,首先以 {{request.object.metadata.labels.app}} 的形式将 AdmissionReview 中的数据收集在内部表达式中,而外部表达式是从名为 LabelsCM 的 ConfigMap 上下文构建的。
评估顺序
Kyverno 策略可以在以下位置使用变量:
-
规则上下文(context)
-
规则前置条件(preconditions)
-
规则定义
-
Validation patterns
-
Validation deny rules
-
Mutate strategic merge patches (patchesStrategicMerge)
-
Generate resource data definitions
-
match 和 exclude 元素中不支持使用变量。这样规则就可以快速匹配,而无需加载和处理数据。patchesJson6902.path 中也不支持使用变量。
由于变量可以嵌套,因此了解变量的计算顺序很重要。 在准入控制期间,引擎处理规则的方式如下:
-
匹配规则集是通过从请求信息创建散列来确定的,以根据规则和资源类型检索所有匹配规则。
-
每个匹配的规则都经过进一步处理,以全面评估匹配和检索条件。
-
然后评估规则上下文并从数据源加载变量。
-
然后检查前置条件。
-
处理规则体。
这种排序使得在定义 context 时可以使用请求数据,并在前置条件中使用上下文变量。在 context 本身中,每个变量都按照定义的顺序进行评估。因此,如果需要,变量可以引用先前的变量,但尝试使用后续定义将导致错误。
JMESPath 自定义函数
除了 JMESPath 提供的内置功能列表之外,Kyverno 通过添加其他几个功能来增强这些功能,这使得制定 Kyverno 策略更加容易。
通用
base64_decode(string) string
base64_encode(string) string
compare(string, string) integer
equal_fold(string, string) bool
label_match(object, object) bool (object arguments must be enclosed in backticks; ex. `{{request.object.spec.template.metadata.labels}}`)
parse_json(string) any (decodes a valid JSON encoded string to the appropriate type. Opposite of `to_string` function)
parse_yaml(string) any
path_canonicalize(string) string
pattern_match(pattern string, string|number) bool ('*' matches zero or more alphanumeric characters, '?' matches a single alphanumeric character)
regex_match(string, string|number) bool
regex_replace_all(regex string, src string|number, replace string|number) string (converts all parameters to string)
regex_replace_all_literal(regex string, src string|number, replace string|number) string (converts all parameters to string)
replace(str string, old string, new string, n float64) string
replace_all(str string, old string, new string) string
semver_compare(string, string) bool (Use operators [>, <, etc] with string inputs for comparison logic)
split(str string, sep string) []string
time_since(<layout>, <time1>, <time2>) string (all inputs as string)
to_upper(string) string
to_lower(string) string
trim(str string, cutset string) string
truncate(str string, length float64) string (length argument must be enclosed in backticks; ex. "{{request.object.metadata.name | truncate(@, `9`)}}")
计算
add(number, number) number
add(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
add(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
subtract(number, number) number
subtract(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
subtract(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
multiply(number, number) number
multiply(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
multiply(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
divide(quantity|number, quantity|number) quantity|number (returns a quantity if exactly one of the parameters is a quantity, else a number; the divisor must be non-zero)
divide(duration|number, duration|number) duration|number (returns a duration if exactly one of the parameters is a duration, else a number; the divisor must be non-zero)
modulo(number, number) number
modulo(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity; the divisor must be non-zero)
modulo(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration; the divisor must be non-zero)
注意
JMESPath 算术函数适用于标量(例如,10)、资源数量(例如,10Mi)和持续时间(例如,10h)。如果输入是标量,则必须将其括在反引号中,以便将参数视为数字。资源数量和持续时间用单引号括起来,被视为字符串。
特殊变量 {{@}} 可用于引用给定字段中的当前值,这对于源值很有用。
要查找其中一些函数的实际示例,请参阅 Kyverno 策略库。有关每个自定义过滤器的更完整信息以及示例,请参阅此处的 JMESPath 页面。
网友评论