你正在查看的文档所针对的是 Kubernetes 版本: v1.27
Kubernetes v1.27 版本的文档已不再维护。你现在看到的版本来自于一份静态的快照。如需查阅最新文档,请点击 最新版本。
验证准入策略(ValidatingAdmissionPolicy)
Kubernetes v1.26 [alpha]
本页面提供验证准入策略(Validating Admission Policy)的概述。
什么是验证准入策略?
验证准入策略提供一种声明式的、进程内的替代方案来验证准入 Webhook。
验证准入策略使用通用表达语言 (Common Expression Language,CEL) 来声明策略的验证规则。 验证准入策略是高度可配置的,使配置策略的作者能够根据集群管理员的需要, 定义可以参数化并限定到资源的策略。
哪些资源构成策略
策略通常由三种资源构成:
-
ValidatingAdmissionPolicy
描述策略的抽象逻辑(想想看:“这个策略确保一个特定标签被设置为一个特定值”)。 -
一个
ValidatingAdmissionPolicyBinding
将上述资源联系在一起,并提供作用域。 如果你只想为Pods
设置一个owner
标签,你就需要在这个绑定中指定这个限制。 -
参数资源为
ValidatingAdmissionPolicy
提供信息,使其成为一个具体的声明 (想想看:“owner
标签必须被设置为以.company.com
结尾的形式")。 参数资源的模式(Schema)使用诸如 ConfigMap 或 CRD 这类原生类型定义。ValidatingAdmissionPolicy
对象指定它们期望参数资源所呈现的类型。
至少要定义一个 ValidatingAdmissionPolicy
和一个相对应的 ValidatingAdmissionPolicyBinding
才能使策略生效。
如果 ValidatingAdmissionPolicy
不需要参数配置,不设置 ValidatingAdmissionPolicy
中的
spec.paramKind
即可。
准备开始
- 确保
ValidatingAdmissionPolicy
特性门控被启用。 - 确保
admissionregistration.k8s.io/v1alpha1
API 被启用。
开始使用验证准入策略
验证准入策略是集群控制平面的一部分。你应该非常谨慎地编写和部署它们。下面介绍如何快速试验验证准入策略。
创建一个 ValidatingAdmissionPolicy
以下是一个 ValidatingAdmissionPolicy 的示例:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= 5"
spec.validations
包含使用通用表达式语言 (CEL)
来验证请求的 CEL 表达式。
如果表达式的计算结果为 false,则根据 spec.failurePolicy
字段强制执行验证检查处理。
要配置一个在某集群中使用的验证准入策略,需要一个绑定。 以下是一个 ValidatingAdmissionPolicyBinding 的示例:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "demo-binding-test.example.com"
spec:
policyName: "demo-policy.example.com"
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: test
尝试创建副本集合不满足验证表达式的 Deployment 时,将返回包含以下消息的错误:
ValidatingAdmissionPolicy 'demo-policy.example.com' with binding 'demo-binding-test.example.com' denied request: failed expression: object.spec.replicas <= 5
上面提供的是一个简单的、无配置参数的 ValidatingAdmissionPolicy。
验证操作
每个 ValidatingAdmissionPolicyBinding
必须指定一个或多个
validationActions
来声明如何执行策略的 validations
。
支持的 validationActions
包括:
Deny
: 验证失败会导致请求被拒绝。Warn
: 验证失败会作为警告报告给请求客户端。Audit
: 验证失败会包含在 API 请求的审计事件中。
例如,要同时向客户端发出验证失败的警告并记录验证失败的审计记录,请使用以下配置:
validationActions: [Warn, Audit]
Deny
和 Warn
不能一起使用,因为这种组合会不必要地将验证失败重复输出到
API 响应体和 HTTP 警告头中。
如果 validation
求值为 false,则始终根据这些操作执行。
由 failurePolicy
定义的失败仅在 failurePolicy
设置为 Fail
(或未设置)时根据这些操作执行,
否则这些失败将被忽略。
有关验证失败审计注解的详细信息,请参见 审计注解:验证失败。
参数资源
参数资源允许策略配置与其定义分开。 一个策略可以定义 paramKind,给出参数资源的 GVK, 然后一个策略绑定可以通过名称(通过 policyName)将某策略与某特定的参数资源(通过 paramRef)联系起来。
如果需要参数配置,下面是一个带有参数配置的 ValidatingAdmissionPolicy 的例子:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "replicalimit-policy.example.com"
spec:
failurePolicy: Fail
paramKind:
apiVersion: rules.example.com/v1
kind: ReplicaLimit
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= params.maxReplicas"
reason: Invalid
ValidatingAdmissionPolicy 的 spec.paramKind
字段指定用于参数化此策略的资源类型。
在这个例子中,它是由自定义资源 ReplicaLimit 配置的。
在这个例子中请注意 CEL 表达式是如何通过 CEL params 变量引用参数的,如:params.maxReplicas
。
spec.matchConstraints
指定此策略要检查哪些资源。
请注意,诸如 ConfigMap
之类的原生类型也可以用作参数引用。
spec.validations
字段包含 CEL 表达式。
如果表达式的计算结果为 false,则根据 spec.failurePolicy
字段强制执行验证检查操作。
验证准入策略的作者负责提供 ReplicaLimit 参数 CRD。
要配置一个在某集群中使用的验证准入策略,需要创建绑定和参数资源。 以下是 ValidatingAdmissionPolicyBinding 的示例:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "replicalimit-binding-test.example.com"
spec:
policyName: "replicalimit-policy.example.com"
validationActions: [Deny]
paramRef:
name: "replica-limit-test.example.com"
matchResources:
namespaceSelector:
matchLabels:
environment: test
参数资源可以如下:
apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
name: "replica-limit-test.example.com"
maxReplicas: 3
此策略参数资源将限制测试环境所有名字空间中的 Deployment 最多有 3 个副本。 一个准入策略可以有多个绑定。 要绑定所有的其他环境,限制 maxReplicas 为 100,请创建另一个 ValidatingAdmissionPolicyBinding:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "replicalimit-binding-nontest"
spec:
policyName: "replicalimit-policy.example.com"
validationActions: [Deny]
paramRef:
name: "replica-limit-clusterwide.example.com"
matchResources:
namespaceSelector:
matchExpressions:
- key: environment
operator: NotIn
values:
- test
并有一个参数资源,如下:
apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
name: "replica-limit-clusterwide.example.com"
maxReplicas: 100
绑定可以包含相互重叠的匹配条件。策略会针对每个匹配的绑定进行计算。
在上面的例子中,nontest
策略绑定可以被定义为一个全局策略:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "replicalimit-binding-global"
spec:
policyName: "replicalimit-policy.example.com"
validationActions: [Deny]
params: "replica-limit-clusterwide.example.com"
matchResources:
namespaceSelector:
matchExpressions:
- key: environment
operator: Exists
如果参数资源尚未被绑定,代表参数资源的 params 对象将不会被设置, 所以对于需要参数资源的策略,添加一个检查来确保参数资源被绑定,这会很有用。
对于需要参数配置的场景,我们建议在 spec.validations[0].expression
中添加一个参数检查:
- expression: "params != null"
message: "params missing but required to bind to this policy"
将可选参数作为参数资源的一部分,并且只在参数存在时执行检查操作,这样做会比较方便。
CEL 提供了 has()
方法,它检查传递给它的键是否存在。CEL 还实现了布尔短路逻辑。
如果逻辑 OR 的前半部分计算为 true,则不会计算另一半(因为无论如何整个 OR 的结果都为真)。
结合这两者,我们可以提供一种验证可选参数的方法:
!has(params.optionalNumber) || (params.optionalNumber >= 5 && params.optionalNumber <= 10)
在这里,我们首先用 !has(params.optionalNumber)
检查可选参数是否存在。
- 如果
optionalNumber
没有被定义,那么表达式就会短路,因为!has(params.optionalNumber)
的计算结果为 true。 - 如果
optionalNumber
被定义了,那么将计算 CEL 表达式的后半部分, 并且optionalNumber
将被检查以确保它包含一个 5 到 10 之间的值(含 5 到 10)。
鉴权检查
我们为参数资源引入了鉴权检查。
用户应该对 ValidatingAdmissionPolicy
中的 paramKind
和 ValidatingAdmissionPolicyBinding
中的 paramRef
所引用的资源有 read
权限。
请注意,如果 paramKind
中的资源没能通过 restmapper 解析,则用户需要拥有对组的所有资源的
read
访问权限。
失效策略
failurePolicy
定义了如何处理错误配置和准入策略的 CEL 表达式取值为 error 的情况。
允许的值是 Ignore
或 Fail
。
Ignore
意味着调用 ValidatingAdmissionPolicy 的错误被忽略,允许 API 请求继续。Fail
意味着调用 ValidatingAdmissionPolicy 的错误导致准入失败并拒绝 API 请求。
请注意,failurePolicy
是在 ValidatingAdmissionPolicy
中定义的:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
spec:
...
failurePolicy: Ignore # 默认值是 "Fail"
validations:
- expression: "object.spec.xyz == params.x"
检查表达式
spec.validations[i].expression
代表将使用 CEL 来计算表达式。
要了解更多信息,请参阅 CEL 语言规范。
CEL 表达式可以访问按 CEL 变量来组织的 Admission 请求/响应的内容,以及其他一些有用的变量 :
- 'object' - 来自传入请求的对象。对于 DELETE 请求,该值为 null。
- 'oldObject' - 现有对象。对于 CREATE 请求,该值为 null。
- 'request' - 准入请求的属性。
- 'params' - 被计算的策略绑定引用的参数资源。如果未设置
paramKind
,该值为 null。 authorizer
- 一个 CEL 鉴权组件。可以用来为请求的主体(经过身份验证的用户)执行鉴权检查。 更多细节可以参考 Kubernetes CEL 库的文档中的 Authz。authorizer.requestResource
- 针对请求资源(组、资源、(子资源)、命名空间、名称)所配置的鉴权检查的快捷方式。
总是可以从对象的根访问的属性有 apiVersion
、kind
、metadata.name
和 metadata.generateName
。
其他元数据属性不能访问。
只有符合 [a-zA-Z_.-/][a-zA-Z0-9_.-/]*
形式的属性名称是可访问的。
可访问的属性名称在表达式中被访问时,根据以下规则进行转义:
转义序列 | 属性名称等效 |
---|---|
__underscores__ |
__ |
__dot__ |
. |
__dash__ |
- |
__slash__ |
/ |
__{keyword}__ |
CEL 保留关键字 |
CEL 保留关键字仅在字符串与保留关键字完全匹配时才需要转义。
例如,单词 “sprint” 中的 int
不需要被转义。
转义示例:
属性名 | 具有转义属性名称的规则 |
---|---|
namespace | self.__namespace__ > 0 |
x-prop | self.x__dash__prop > 0 |
redact__d | self.redact__underscores__d > 0 |
string | self.startsWith('kube') |
列表类型为 "set" 或 "map" 的数组上的等价关系比较会忽略元素顺序,即 [1, 2] == [2, 1]。 使用 x-kubernetes-list-type 连接数组时使用列表类型的语义:
- 'set':
X + Y
执行并集,其中X
中所有元素的数组位置被保留,Y
中不相交的元素被追加,保留其元素的偏序关系。 - 'map':
X + Y
执行合并,保留X
中所有键的数组位置,但是当X
和Y
的键集相交时,其值被Y
的值覆盖。Y
中键值不相交的元素被追加,保留其元素之间的偏序关系。
检查表达式示例
表达式 | 目的 |
---|---|
object.minReplicas <= object.replicas && object.replicas <= object.maxReplicas |
检查定义副本的三个字段是否大小关系正确 |
'Available' in object.stateCounts |
检查映射中是否存在键为 Available 的条目 |
(size(object.list1) == 0) != (size(object.list2) == 0) |
检查两个列表是否有且只有一个非空 |
!('MY_KEY' in object.map1) || object['MY_KEY'].matches('^[a-zA-Z]*$') |
检查映射中存在特定的键时其取值符合某规则 |
object.envars.filter(e, e.name == 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$') |
验证 listMap 中所有键名为 "MY_ENV" 的条目的 “value” 字段,确保其符合规则 |
has(object.expired) && object.created + object.ttl < object.expired |
检查 expired 日期在 create 日期加上 ttl 时长之后 |
object.health.startsWith('ok') |
检查 health 字符串字段的取值有 “ok” 前缀 |
object.widgets.exists(w, w.key == 'x' && w.foo < 10) |
对于 listMap 中键为 “x” 的条目,检查该条目的 "foo" 属性的值是否小于 10 |
type(object) == string ? object == '100%' : object == 1000 |
对于 int-or-string 字段,分别处理类型为 int 或 string 的情况 |
object.metadata.name.startsWith(object.prefix) |
检查对象名称是否以另一个字段值为前缀 |
object.set1.all(e, !(e in object.set2)) |
检查两个 listSet 是否不相交 |
size(object.names) == size(object.details) && object.names.all(n, n in object.details) |
检查映射 “details” 所有的键和 listSet “names” 中的条目是否一致 |
size(object.clusters.filter(c, c.name == object.primary)) == 1 |
检查 “primary” 字段的值在 listMap “clusters” 中只出现一次 |
了解关于 CEL 规则的更多信息, 请阅读 CEL 支持的求值表达式。
spec.validation[i].reason
表示一个机器可读的描述,说明为什么这次检查失败。
如果这是列表中第一个失败的检查,其原因以及相应的 HTTP 响应代码会被用在给客户端的 HTTP 响应中。
目前支持的原因有:Unauthorized
、Forbidden
、Invalid
、RequestEntityTooLarge
。
如果未设置,将在对客户端的响应中使用 StatusReasonInvalid
。
匹配请求:matchConditions
如果需要进行精细的请求过滤,可以为 ValidatingAdmissionPolicy
定义 匹配条件(match conditions)。
如果发现匹配规则、objectSelectors
和 namespaceSelectors
仍无法提供所需的过滤功能,则使用这些条件非常有用。
匹配条件是 CEL 表达式。
所有匹配条件必须求值为 true 时才会对资源进行评估。
以下示例说明了匹配条件的几个不同用法:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["*"]
apiVersions: ["*"]
operations: ["CREATE", "UPDATE"]
resources: ["*"]
matchConditions:
- name: 'exclude-leases' # 每个匹配条件必须有一个唯一的名称
expression: '!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")' # 匹配非租约资源
- name: 'exclude-kubelet-requests'
expression: '!("system:nodes" in request.userInfo.groups)' # 匹配非节点用户发出的请求
- name: 'rbac' # 跳过 RBAC 请求
expression: 'request.resource.group != "rbac.authorization.k8s.io"'
validations:
- expression: "!object.metadata.name.contains('demo') || object.metadata.namespace == 'demo'"
这些匹配条件可以访问与验证表达式相同的 CEL 变量。
在评估匹配条件时出现错误时,将不会评估策略。根据以下方式确定是否拒绝请求:
-
如果任何一个匹配条件求值结果为
false
(不管其他错误),API 服务器将跳过 Webhook。 -
否则:
- 对于
failurePolicy: Fail
,拒绝请求(不调用 Webhook)。 - 对于
failurePolicy: Ignore
,继续处理请求但跳过 Webhook。
- 对于
审计注解
auditAnnotations
可用于在 API 请求的审计事件中包括审计注解。
例如,以下是带有审计注解的准入策略:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- key: "high-replica-count"
expression: "object.spec.replicas > 50"
messageExpression: "'Deployment spec.replicas set to ' + string(object.spec.replicas)"
当使用此准入策略验证 API 请求时,生成的审计事件将如下所示:
# 记录的审计事件
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"annotations": {
"demo-policy.example.com/high-replica-count": "Deployment spec.replicas set to 128"
# 其他注解
...
}
# 其他字段
...
}
在此示例中,只有 Deployment 的 spec.replicas
大于 50 时才会包含注解,
否则 CEL 表达式将求值为 null,并且不会包含注解。
请注意,审计注解键以 ValidatingAdmissionWebhook
的名称和 /
为前缀。
如果另一个准入控制器(例如准入 Webhook)使用完全相同的审计注解键,
则第一个包括审计注解值的准入控制器将出现在审计事件中,而所有其他值都将被忽略。
消息表达式
为了在策略拒绝请求时返回更友好的消息,我们在 spec.validations[i].messageExpression
中使用 CEL 表达式来构造消息。
与验证表达式类似,消息表达式可以访问 object
、oldObject
、request
和 params
。
但是,与验证不同,消息表达式必须求值为字符串。
例如,为了在策略引用参数时更好地告知用户拒绝原因,我们可以有以下验证:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "deploy-replica-policy.example.com"
spec:
paramKind:
apiVersion: rules.example.com/v1
kind: ReplicaLimit
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= params.maxReplicas"
messageExpression: "'object.spec.replicas must be no greater than ' + string(params.maxReplicas)"
reason: Invalid
在创建限制副本为 3 的 Params 对象并设置绑定之后,当我们尝试创建具有 5 个副本的 Deployment 时,我们将收到以下消息:
$ kubectl create deploy --image=nginx nginx --replicas=5
error: failed to create deployment: deployments.apps "nginx" is forbidden: ValidatingAdmissionPolicy 'deploy-replica-policy.example.com' with binding 'demo-binding-test.example.com' denied request: object.spec.replicas must be no greater than 3
这比静态消息 "too many replicas" 更具说明性。
如果既定义了消息表达式,又在 spec.validations[i].message
中定义了静态消息,
则消息表达式优先于静态消息。
但是,如果消息表达式求值失败,则将使用静态消息。
此外,如果消息表达式求值为多行字符串,则会丢弃求值结果并使用静态消息(如果存在)。
请注意,静态消息也要检查是否存在多行字符串。
类型检查
创建或更新策略定义时,验证过程将解析它包含的表达式,在发现错误时报告语法错误并拒绝该定义。
之后,引用的变量将根据 spec.matchConstraints
的匹配类型检查类型错误,包括缺少字段和类型混淆。
类型检查的结果可以从 status.typeChecking
中获得。
status.typeChecking
的存在表示类型检查已完成,而空的 status.typeChecking
表示未发现错误。
例如,给定以下策略定义:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "deploy-replica-policy.example.com"
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.replicas > 1" # 应该是 object.spec.replicas > 1
message: "must be replicated"
reason: Invalid
status 字段将提供以下信息:
status:
typeChecking:
expressionWarnings:
- fieldRef: spec.validations[0].expression
warning: |-
apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas'
| object.replicas > 1
| ......^
如果在 spec.matchConstraints
中匹配了多个资源,则所有匹配的资源都将进行检查。
例如,以下策略定义:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "replica-policy.example.com"
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments","replicasets"]
validations:
- expression: "object.replicas > 1" # 应该是 object.spec.replicas > 1
message: "must be replicated"
reason: Invalid
将具有多个类型,并在警告消息中提供每种类型的类型检查结果。
status:
typeChecking:
expressionWarnings:
- fieldRef: spec.validations[0].expression
warning: |-
apps/v1, Kind=Deployment: ERROR: <input>:1:7: undefined field 'replicas'
| object.replicas > 1
| ......^
apps/v1, Kind=ReplicaSet: ERROR: <input>:1:7: undefined field 'replicas'
| object.replicas > 1
| ......^
类型检查具有以下限制:
- 没有通配符匹配。
如果
spec.matchConstraints.resourceRules
中的任何一个apiGroups
、apiVersions
或resources
包含 "*",则不会检查与 "*" 匹配的类型。 - 匹配的类型数量最多为 10 种。这是为了防止手动指定过多类型的策略消耗过多计算资源。 按升序处理组、版本,然后是资源,忽略第 11 个及其之后的组合。
- 类型检查不会以任何方式影响策略行为。即使类型检查检测到错误,策略也将继续评估。 如果在评估期间出现错误,则失败策略将决定其结果。
- 类型检查不适用于 CRD(自定义资源定义),包括匹配的 CRD 类型和 paramKind 的引用。 对 CRD 的支持将在未来发布中推出。