From 0d862b9afe63486f18f804efa6847b19445c6844 Mon Sep 17 00:00:00 2001 From: Jiahui Feng Date: Wed, 22 Mar 2023 13:42:53 -0700 Subject: [PATCH] message expression and type checking. --- .../validating-admission-policy.md | 120 +++++++++++++++++- .../access/deployment-replicas-policy.yaml | 20 +++ 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 content/en/examples/access/deployment-replicas-policy.yaml diff --git a/content/en/docs/reference/access-authn-authz/validating-admission-policy.md b/content/en/docs/reference/access-authn-authz/validating-admission-policy.md index 86565e42e9853..ffb7f96965cbc 100644 --- a/content/en/docs/reference/access-authn-authz/validating-admission-policy.md +++ b/content/en/docs/reference/access-authn-authz/validating-admission-policy.md @@ -446,4 +446,122 @@ In this example the annotation will only be included if the `spec.replicas` of t Note that audit annotation keys are prefixed by the name of the `ValidatingAdmissionWebhook` and a `/`. If another admission controller, such as an admission webhook, uses the exact same audit annotation key, the value of the first admission controller to include the audit annotation will be included in the audit -event and all other values will be ignored. \ No newline at end of file +event and all other values will be ignored. + +### Message expression + +To return a more friendly message when the policy rejects a request, we can use a CEL expression +to composite a message with `spec.validations[i].messageExpression`. Similar to the validation expression, +a message expression has access to `object`, `oldObject`, `request`, and `params`. Unlike validations, +message expression must evaluate to a string. + +For example, to better inform the user of the reason of denial when the policy refers to a parameter, +we can have the following validation: + +{{< codenew file="access/deployment-replicas-policy.yaml" >}} + +After creating a params object that limits the replicas to 3 and setting up the binding, +when we try to create a deployment with 5 replicas, we will receive the following message. + +``` +$ 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 +``` + +This is more informative than a static message of "too many replicas". + +The message expression takes precedence over the static message defined in `spec.validations[i].message` if both are defined. +However, if the message expression fails to evaluate, the static message will be used instead. +Additionally, if the message expression evaluates to a multi-line string, +the evaluation result will be discarded and the static message will be used if present. +Note that static message is validated against multi-line strings. + +### Type checking + +When a policy definition is created or updated, the validation process parses the expressions it contains +and reports any syntax errors, rejecting the definition if any errors are found. +Afterward, the referred variables are checked for type errors, including missing fields and type confusion, +against the matched types of `spec.matchConstraints`. +The result of type checking can be retrieved from `status.typeChecking`. +The presence of `status.typeChecking` indicates the completion of type checking, +and an empty `status.typeChecking` means that no errors were detected. + +For example, given the following policy definition: + +```yaml +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" # should be "object.spec.replicas > 1" + message: "must be replicated" + reason: Invalid +``` + +The status will yield the following information: + +```yaml +status: + typeChecking: + expressionWarnings: + - fieldRef: spec.validations[0].expression + warning: |- + apps/v1, Kind=Deployment: ERROR: :1:7: undefined field 'replicas' + | object.replicas > 1 + | ......^ +``` + +If multiple resources are matched in `spec.matchConstraints`, all of matched resources will be checked against. +For example, the following policy definition + +```yaml +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" # should be "object.spec.replicas > 1" + message: "must be replicated" + reason: Invalid +``` + +will have multiple types and type checking result of each type in the warning message. + +```yaml +status: + typeChecking: + expressionWarnings: + - fieldRef: spec.validations[0].expression + warning: |- + apps/v1, Kind=Deployment: ERROR: :1:7: undefined field 'replicas' + | object.replicas > 1 + | ......^ + apps/v1, Kind=ReplicaSet: ERROR: :1:7: undefined field 'replicas' + | object.replicas > 1 + | ......^ +``` + +Type Checking has the following limitation: + +- No wildcard matching. If `spec.matchConstraints.resourceRules` contains `"*"` in any of `apiGroups`, `apiVersions` or `resources`, + the types that `"*"` matches will not be checked. +- The number of matched types is limited to 10. This is to prevent a policy that manually specifying too many types. + to consume excessive computing resources. In the order of ascending group, version, and then resource, 11th combination and beyond are ignored. +- Type Checking does not affect the policy behavior in any way. Even if the type checking detects errors, the policy will continue + to evaluate. If errors do occur during evaluate, the failure policy will decide its outcome. +- Type Checking does not apply to CRDs, including matched CRD types and reference of paramKind. The support for CRDs will come in future release. \ No newline at end of file diff --git a/content/en/examples/access/deployment-replicas-policy.yaml b/content/en/examples/access/deployment-replicas-policy.yaml new file mode 100644 index 0000000000000..23c04fff6210c --- /dev/null +++ b/content/en/examples/access/deployment-replicas-policy.yaml @@ -0,0 +1,20 @@ +apiVersion: admissionregistration.k8s.io/v1alpha1 +kind: ValidatingAdmissionPolicy +metadata: + name: "deploy-replica-policy.example.com" +spec: + paramKind: + group: rules.example.com + kind: ReplicaLimit + version: v1 + 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 +