From a6ec7d8017b2a05128abc530894694b09d8081d3 Mon Sep 17 00:00:00 2001 From: Qiming Teng Date: Fri, 1 Jul 2022 00:33:57 +0800 Subject: [PATCH] Tweak extensible admission controllers page This PR removes outdated information about `admissionregistration.v1beta1` API groups which are no longer supported in 1.24. Additional notes are added to avoid confusion when parsing the examples. --- .../extensible-admission-controllers.md | 1271 ++++++----------- 1 file changed, 405 insertions(+), 866 deletions(-) diff --git a/content/en/docs/reference/access-authn-authz/extensible-admission-controllers.md b/content/en/docs/reference/access-authn-authz/extensible-admission-controllers.md index 6f7154cc8a..05f9b8369c 100644 --- a/content/en/docs/reference/access-authn-authz/extensible-admission-controllers.md +++ b/content/en/docs/reference/access-authn-authz/extensible-admission-controllers.md @@ -16,8 +16,8 @@ In addition to [compiled-in admission plugins](/docs/reference/access-authn-auth admission plugins can be developed as extensions and run as webhooks configured at runtime. This page describes how to build, configure, use, and monitor admission webhooks. - + ## What are admission webhooks? Admission webhooks are HTTP callbacks that receive admission requests and do @@ -37,29 +37,25 @@ should use a validating admission webhook, since objects can be modified after b ## Experimenting with admission webhooks Admission webhooks are essentially part of the cluster control-plane. You should -write and deploy them with great caution. Please read the [user -guides](/docs/reference/access-authn-authz/extensible-admission-controllers/#write-an-admission-webhook-server) for -instructions if you intend to write/deploy production-grade admission webhooks. +write and deploy them with great caution. Please read the +[user guides](/docs/reference/access-authn-authz/extensible-admission-controllers/#write-an-admission-webhook-server) +for instructions if you intend to write/deploy production-grade admission webhooks. In the following, we describe how to quickly experiment with admission webhooks. ### Prerequisites -* Ensure that the Kubernetes cluster is at least as new as v1.16 (to use `admissionregistration.k8s.io/v1`), - or v1.9 (to use `admissionregistration.k8s.io/v1beta1`). - * Ensure that MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controllers are enabled. [Here](/docs/reference/access-authn-authz/admission-controllers/#is-there-a-recommended-set-of-admission-controllers-to-use) is a recommended set of admission controllers to enable in general. -* Ensure that the `admissionregistration.k8s.io/v1` or `admissionregistration.k8s.io/v1beta1` API is enabled. +* Ensure that the `admissionregistration.k8s.io/v1` API is enabled. ### Write an admission webhook server -Please refer to the implementation of the [admission webhook -server](https://github.com/kubernetes/kubernetes/blob/release-1.21/test/images/agnhost/webhook/main.go) +Please refer to the implementation of the [admission webhook server](https://github.com/kubernetes/kubernetes/blob/release-1.21/test/images/agnhost/webhook/main.go) that is validated in a Kubernetes e2e test. The webhook handles the -`AdmissionReview` request sent by the apiservers, and sends back its decision +`AdmissionReview` request sent by the API servers, and sends back its decision as an `AdmissionReview` object in the same version it received. See the [webhook request](#request) section for details on the data sent to webhooks. @@ -69,9 +65,9 @@ See the [webhook response](#response) section for the data expected from webhook The example admission webhook server leaves the `ClientAuth` field [empty](https://github.com/kubernetes/kubernetes/blob/v1.22.0/test/images/agnhost/webhook/config.go#L38-L39), which defaults to `NoClientCert`. This means that the webhook server does not -authenticate the identity of the clients, supposedly apiservers. If you need +authenticate the identity of the clients, supposedly API servers. If you need mutual TLS or other ways to authenticate the clients, see -how to [authenticate apiservers](#authenticate-apiservers). +how to [authenticate API servers](#authenticate-apiservers). ### Deploy the admission webhook service @@ -95,8 +91,6 @@ or The following is an example `ValidatingWebhookConfiguration`, a mutating webhook configuration is similar. See the [webhook configuration](#webhook-configuration) section for details about each config field. -{{< tabs name="ValidatingWebhookConfiguration_example_1" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -114,39 +108,18 @@ webhooks: service: namespace: "example-namespace" name: "example-service" - caBundle: "Ci0tLS0tQk...<`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.>...tLS0K" - admissionReviewVersions: ["v1", "v1beta1"] + caBundle: + admissionReviewVersions: ["v1"] sideEffects: None timeoutSeconds: 5 ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -metadata: - name: "pod-policy.example.com" -webhooks: -- name: "pod-policy.example.com" - rules: - - apiGroups: [""] - apiVersions: ["v1"] - operations: ["CREATE"] - resources: ["pods"] - scope: "Namespaced" - clientConfig: - service: - namespace: "example-namespace" - name: "example-service" - caBundle: "Ci0tLS0tQk...<`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate>...tLS0K" - admissionReviewVersions: ["v1beta1"] - timeoutSeconds: 5 -``` -{{% /tab %}} -{{< /tabs >}} -The scope field specifies if only cluster-scoped resources ("Cluster") or namespace-scoped +{{< note >}} +You must replace the `` in the above example by a valid CA bundle +which is a PEM-encoded CA bundle for validating the webhook's server certificate. +{{< /note >}} + +The `scope` field specifies if only cluster-scoped resources ("Cluster") or namespace-scoped resources ("Namespaced") will match this rule. "∗" means that there are no scope restrictions. {{< note >}} @@ -155,27 +128,26 @@ When using `clientConfig.service`, the server cert must be valid for {{< /note >}} {{< note >}} -Default timeout for a webhook call is 10 seconds for webhooks registered created using `admissionregistration.k8s.io/v1`, -and 30 seconds for webhooks created using `admissionregistration.k8s.io/v1beta1`. Starting in kubernetes 1.14 you -can set the timeout and it is encouraged to use a small timeout for webhooks. +Default timeout for a webhook call is 10 seconds, +You can set the `timeout` and it is encouraged to use a short timeout for webhooks. If the webhook call times out, the request is handled according to the webhook's failure policy. {{< /note >}} -When an apiserver receives a request that matches one of the `rules`, the -apiserver sends an `admissionReview` request to webhook as specified in the +When an API server receives a request that matches one of the `rules`, the +API server sends an `admissionReview` request to webhook as specified in the `clientConfig`. After you create the webhook configuration, the system will take a few seconds to honor the new configuration. -### Authenticate apiservers +### Authenticate API servers {#authenticate-apiservers} If your admission webhooks require authentication, you can configure the -apiservers to use basic auth, bearer token, or a cert to authenticate itself to +API servers to use basic auth, bearer token, or a cert to authenticate itself to the webhooks. There are three steps to complete the configuration. -* When starting the apiserver, specify the location of the admission control +* When starting the API server, specify the location of the admission control configuration file via the `--admission-control-config-file` flag. * In the admission control configuration file, specify where the @@ -228,55 +200,55 @@ For more information about `AdmissionConfiguration`, see the [AdmissionConfiguration (v1) reference](/docs/reference/config-api/apiserver-webhookadmission.v1/). See the [webhook configuration](#webhook-configuration) section for details about each config field. -* In the kubeConfig file, provide the credentials: +In the kubeConfig file, provide the credentials: - ```yaml - apiVersion: v1 - kind: Config - users: - # name should be set to the DNS name of the service or the host (including port) of the URL the webhook is configured to speak to. - # If a non-443 port is used for services, it must be included in the name when configuring 1.16+ API servers. - # - # For a webhook configured to speak to a service on the default port (443), specify the DNS name of the service: - # - name: webhook1.ns1.svc - # user: ... - # - # For a webhook configured to speak to a service on non-default port (e.g. 8443), specify the DNS name and port of the service in 1.16+: - # - name: webhook1.ns1.svc:8443 - # user: ... - # and optionally create a second stanza using only the DNS name of the service for compatibility with 1.15 API servers: - # - name: webhook1.ns1.svc - # user: ... - # - # For webhooks configured to speak to a URL, match the host (and port) specified in the webhook's URL. Examples: - # A webhook with `url: https://www.example.com`: - # - name: www.example.com - # user: ... - # - # A webhook with `url: https://www.example.com:443`: - # - name: www.example.com:443 - # user: ... - # - # A webhook with `url: https://www.example.com:8443`: - # - name: www.example.com:8443 - # user: ... - # - - name: 'webhook1.ns1.svc' - user: - client-certificate-data: "" - client-key-data: "" - # The `name` supports using * to wildcard-match prefixing segments. - - name: '*.webhook-company.org' - user: - password: "" - username: "" - # '*' is the default match. - - name: '*' - user: - token: "" - ``` +```yaml +apiVersion: v1 +kind: Config +users: +# name should be set to the DNS name of the service or the host (including port) of the URL the webhook is configured to speak to. +# If a non-443 port is used for services, it must be included in the name when configuring 1.16+ API servers. +# +# For a webhook configured to speak to a service on the default port (443), specify the DNS name of the service: +# - name: webhook1.ns1.svc +# user: ... +# +# For a webhook configured to speak to a service on non-default port (e.g. 8443), specify the DNS name and port of the service in 1.16+: +# - name: webhook1.ns1.svc:8443 +# user: ... +# and optionally create a second stanza using only the DNS name of the service for compatibility with 1.15 API servers: +# - name: webhook1.ns1.svc +# user: ... +# +# For webhooks configured to speak to a URL, match the host (and port) specified in the webhook's URL. Examples: +# A webhook with `url: https://www.example.com`: +# - name: www.example.com +# user: ... +# +# A webhook with `url: https://www.example.com:443`: +# - name: www.example.com:443 +# user: ... +# +# A webhook with `url: https://www.example.com:8443`: +# - name: www.example.com:8443 +# user: ... +# +- name: 'webhook1.ns1.svc' + user: + client-certificate-data: "" + client-key-data: "" +# The `name` supports using * to wildcard-match prefixing segments. +- name: '*.webhook-company.org' + user: + password: "" + username: "" +# '*' is the default match. +- name: '*' + user: + token: "" +``` -Of course you need to set up the webhook server to handle these authentications. +Of course you need to set up the webhook server to handle these authentication requests. ## Webhook request and response @@ -289,39 +261,17 @@ serialized to JSON as the body. Webhooks can specify what versions of `AdmissionReview` objects they accept with the `admissionReviewVersions` field in their configuration: -{{< tabs name="ValidatingWebhookConfiguration_admissionReviewVersions" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration -... webhooks: - name: my-webhook.example.com admissionReviewVersions: ["v1", "v1beta1"] - ... ``` -`admissionReviewVersions` is a required field when creating -`admissionregistration.k8s.io/v1` webhook configurations. +`admissionReviewVersions` is a required field when creating webhook configurations. Webhooks are required to support at least one `AdmissionReview` version understood by the current and previous API server. -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - admissionReviewVersions: ["v1beta1"] - ... -``` - -If no `admissionReviewVersions` are specified, the default when creating -`admissionregistration.k8s.io/v1beta1` webhook configurations is `v1beta1`. -{{% /tab %}} -{{< /tabs >}} API servers send the first `AdmissionReview` version in the `admissionReviewVersions` list they support. If none of the versions in the list are supported by the API server, the configuration will not be allowed to be created. @@ -331,154 +281,100 @@ versions the API server knows how to send, attempts to call to the webhook will This example shows the data contained in an `AdmissionReview` object for a request to update the `scale` subresource of an `apps/v1` `Deployment`: - -{{< tabs name="AdmissionReview_request" >}} -{{% tab name="admission.k8s.io/v1" %}} ```yaml -{ - "apiVersion": "admission.k8s.io/v1", - "kind": "AdmissionReview", - "request": { - # Random uid uniquely identifying this admission call - "uid": "705ab4f5-6393-11e8-b7cc-42010a800002", +apiVersion: admission.k8s.io/v1 +kind: AdmissionReview +request: + # Random uid uniquely identifying this admission call + uid: 705ab4f5-6393-11e8-b7cc-42010a800002 - # Fully-qualified group/version/kind of the incoming object - "kind": {"group":"autoscaling","version":"v1","kind":"Scale"}, - # Fully-qualified group/version/kind of the resource being modified - "resource": {"group":"apps","version":"v1","resource":"deployments"}, - # subresource, if the request is to a subresource - "subResource": "scale", + # Fully-qualified group/version/kind of the incoming object + kind: + group: autoscaling + version: v1 + kind: Scale - # Fully-qualified group/version/kind of the incoming object in the original request to the API server. - # This only differs from `kind` if the webhook specified `matchPolicy: Equivalent` and the - # original request to the API server was converted to a version the webhook registered for. - "requestKind": {"group":"autoscaling","version":"v1","kind":"Scale"}, - # Fully-qualified group/version/kind of the resource being modified in the original request to the API server. - # This only differs from `resource` if the webhook specified `matchPolicy: Equivalent` and the - # original request to the API server was converted to a version the webhook registered for. - "requestResource": {"group":"apps","version":"v1","resource":"deployments"}, - # subresource, if the request is to a subresource - # This only differs from `subResource` if the webhook specified `matchPolicy: Equivalent` and the - # original request to the API server was converted to a version the webhook registered for. - "requestSubResource": "scale", + # Fully-qualified group/version/kind of the resource being modified + resource: + group: apps + version: v1 + resource: deployments - # Name of the resource being modified - "name": "my-deployment", - # Namespace of the resource being modified, if the resource is namespaced (or is a Namespace object) - "namespace": "my-namespace", + # subresource, if the request is to a subresource + subResource: scale - # operation can be CREATE, UPDATE, DELETE, or CONNECT - "operation": "UPDATE", + # Fully-qualified group/version/kind of the incoming object in the original request to the API server. + # This only differs from `kind` if the webhook specified `matchPolicy: Equivalent` and the + # original request to the API server was converted to a version the webhook registered for. + requestKind: + group: autoscaling + version: v1 + kind: Scale - "userInfo": { - # Username of the authenticated user making the request to the API server - "username": "admin", - # UID of the authenticated user making the request to the API server - "uid": "014fbff9a07c", - # Group memberships of the authenticated user making the request to the API server - "groups": ["system:authenticated","my-admin-group"], - # Arbitrary extra info associated with the user making the request to the API server. - # This is populated by the API server authentication layer and should be included - # if any SubjectAccessReview checks are performed by the webhook. - "extra": { - "some-key":["some-value1", "some-value2"] - } - }, + # Fully-qualified group/version/kind of the resource being modified in the original request to the API server. + # This only differs from `resource` if the webhook specified `matchPolicy: Equivalent` and the + # original request to the API server was converted to a version the webhook registered for. + requestResource: + group: apps + version: v1 + resource: deployments - # object is the new object being admitted. - # It is null for DELETE operations. - "object": {"apiVersion":"autoscaling/v1","kind":"Scale",...}, - # oldObject is the existing object. - # It is null for CREATE and CONNECT operations. - "oldObject": {"apiVersion":"autoscaling/v1","kind":"Scale",...}, - # options contains the options for the operation being admitted, like meta.k8s.io/v1 CreateOptions, UpdateOptions, or DeleteOptions. - # It is null for CONNECT operations. - "options": {"apiVersion":"meta.k8s.io/v1","kind":"UpdateOptions",...}, + # subresource, if the request is to a subresource + # This only differs from `subResource` if the webhook specified `matchPolicy: Equivalent` and the + # original request to the API server was converted to a version the webhook registered for. + requestSubResource: scale - # dryRun indicates the API request is running in dry run mode and will not be persisted. - # Webhooks with side effects should avoid actuating those side effects when dryRun is true. - # See http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request for more details. - "dryRun": false - } -} + # Name of the resource being modified + name: my-deployment + + # Namespace of the resource being modified, if the resource is namespaced (or is a Namespace object) + namespace: my-namespace + + # operation can be CREATE, UPDATE, DELETE, or CONNECT + operation: UPDATE + + userInfo: + # Username of the authenticated user making the request to the API server + username: admin + + # UID of the authenticated user making the request to the API server + uid: 014fbff9a07c + + # Group memberships of the authenticated user making the request to the API server + groups: + - system:authenticated + - my-admin-group + # Arbitrary extra info associated with the user making the request to the API server. + # This is populated by the API server authentication layer and should be included + # if any SubjectAccessReview checks are performed by the webhook. + extra: + some-key: + - some-value1 + - some-value2 + + # object is the new object being admitted. + # It is null for DELETE operations. + object: + apiVersion: autoscaling/v1 + kind: Scale + + # oldObject is the existing object. + # It is null for CREATE and CONNECT operations. + oldObject: + apiVersion: autoscaling/v1 + kind: Scale + + # options contains the options for the operation being admitted, like meta.k8s.io/v1 CreateOptions, UpdateOptions, or DeleteOptions. + # It is null for CONNECT operations. + options: + apiVersion: meta.k8s.io/v1 + kind: UpdateOptions + + # dryRun indicates the API request is running in dry run mode and will not be persisted. + # Webhooks with side effects should avoid actuating those side effects when dryRun is true. + # See http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request for more details. + dryRun: False ``` -{{% /tab %}} -{{% tab name="admission.k8s.io/v1beta1" %}} -```yaml -{ - # Deprecated in v1.16 in favor of admission.k8s.io/v1 - "apiVersion": "admission.k8s.io/v1beta1", - "kind": "AdmissionReview", - "request": { - # Random uid uniquely identifying this admission call - "uid": "705ab4f5-6393-11e8-b7cc-42010a800002", - - # Fully-qualified group/version/kind of the incoming object - "kind": {"group":"autoscaling","version":"v1","kind":"Scale"}, - # Fully-qualified group/version/kind of the resource being modified - "resource": {"group":"apps","version":"v1","resource":"deployments"}, - # subresource, if the request is to a subresource - "subResource": "scale", - - # Fully-qualified group/version/kind of the incoming object in the original request to the API server. - # This only differs from `kind` if the webhook specified `matchPolicy: Equivalent` and the - # original request to the API server was converted to a version the webhook registered for. - # Only sent by v1.15+ API servers. - "requestKind": {"group":"autoscaling","version":"v1","kind":"Scale"}, - # Fully-qualified group/version/kind of the resource being modified in the original request to the API server. - # This only differs from `resource` if the webhook specified `matchPolicy: Equivalent` and the - # original request to the API server was converted to a version the webhook registered for. - # Only sent by v1.15+ API servers. - "requestResource": {"group":"apps","version":"v1","resource":"deployments"}, - # subresource, if the request is to a subresource - # This only differs from `subResource` if the webhook specified `matchPolicy: Equivalent` and the - # original request to the API server was converted to a version the webhook registered for. - # Only sent by v1.15+ API servers. - "requestSubResource": "scale", - - # Name of the resource being modified - "name": "my-deployment", - # Namespace of the resource being modified, if the resource is namespaced (or is a Namespace object) - "namespace": "my-namespace", - - # operation can be CREATE, UPDATE, DELETE, or CONNECT - "operation": "UPDATE", - - "userInfo": { - # Username of the authenticated user making the request to the API server - "username": "admin", - # UID of the authenticated user making the request to the API server - "uid": "014fbff9a07c", - # Group memberships of the authenticated user making the request to the API server - "groups": ["system:authenticated","my-admin-group"], - # Arbitrary extra info associated with the user making the request to the API server. - # This is populated by the API server authentication layer and should be included - # if any SubjectAccessReview checks are performed by the webhook. - "extra": { - "some-key":["some-value1", "some-value2"] - } - }, - - # object is the new object being admitted. - # It is null for DELETE operations. - "object": {"apiVersion":"autoscaling/v1","kind":"Scale",...}, - # oldObject is the existing object. - # It is null for CREATE and CONNECT operations (and for DELETE operations in API servers prior to v1.15.0) - "oldObject": {"apiVersion":"autoscaling/v1","kind":"Scale",...}, - # options contains the options for the operation being admitted, like meta.k8s.io/v1 CreateOptions, UpdateOptions, or DeleteOptions. - # It is null for CONNECT operations. - # Only sent by v1.15+ API servers. - "options": {"apiVersion":"meta.k8s.io/v1","kind":"UpdateOptions",...}, - - # dryRun indicates the API request is running in dry run mode and will not be persisted. - # Webhooks with side effects should avoid actuating those side effects when dryRun is true. - # See http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request for more details. - "dryRun": false - } -} -``` -{{% /tab %}} -{{< /tabs >}} ### Response @@ -492,8 +388,7 @@ At a minimum, the `response` stanza must contain the following fields: * `allowed`, either set to `true` or `false` Example of a minimal response from a webhook to allow a request: -{{< tabs name="AdmissionReview_response_allow" >}} -{{% tab name="admission.k8s.io/v1" %}} + ```json { "apiVersion": "admission.k8s.io/v1", @@ -504,55 +399,26 @@ Example of a minimal response from a webhook to allow a request: } } ``` -{{% /tab %}} -{{% tab name="admission.k8s.io/v1beta1" %}} -```json -{ - "apiVersion": "admission.k8s.io/v1beta1", - "kind": "AdmissionReview", - "response": { - "uid": "", - "allowed": true - } -} -``` -{{% /tab %}} -{{< /tabs >}} Example of a minimal response from a webhook to forbid a request: -{{< tabs name="AdmissionReview_response_forbid_minimal" >}} -{{% tab name="admission.k8s.io/v1" %}} -```json -{ - "apiVersion": "admission.k8s.io/v1", - "kind": "AdmissionReview", - "response": { - "uid": "", - "allowed": false - } -} -``` -{{% /tab %}} -{{% tab name="admission.k8s.io/v1beta1" %}} -```json -{ - "apiVersion": "admission.k8s.io/v1beta1", - "kind": "AdmissionReview", - "response": { - "uid": "", - "allowed": false - } -} -``` -{{% /tab %}} -{{< /tabs >}} -When rejecting a request, the webhook can customize the http code and message returned to the user using the `status` field. -The specified status object is returned to the user. -See the [API documentation](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#status-v1-meta) for details about the status type. +```json +{ + "apiVersion": "admission.k8s.io/v1", + "kind": "AdmissionReview", + "response": { + "uid": "", + "allowed": false + } +} +``` + +When rejecting a request, the webhook can customize the http code and message returned to the user +using the `status` field. The specified status object is returned to the user. +See the [API documentation](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#status-v1-meta) +for details about the `status` type. Example of a response to forbid a request, customizing the HTTP status code and message presented to the user: -{{< tabs name="AdmissionReview_response_forbid_details" >}} -{{% tab name="admission.k8s.io/v1" %}} + ```json { "apiVersion": "admission.k8s.io/v1", @@ -567,24 +433,6 @@ Example of a response to forbid a request, customizing the HTTP status code and } } ``` -{{% /tab %}} -{{% tab name="admission.k8s.io/v1beta1" %}} -```json -{ - "apiVersion": "admission.k8s.io/v1beta1", - "kind": "AdmissionReview", - "response": { - "uid": "", - "allowed": false, - "status": { - "code": 403, - "message": "You cannot do this because it is Tuesday and your name starts with A" - } - } -} -``` -{{% /tab %}} -{{< /tabs >}} When allowing a request, a mutating admission webhook may optionally modify the incoming object as well. This is done using the `patch` and `patchType` fields in the response. @@ -592,13 +440,13 @@ The only currently supported `patchType` is `JSONPatch`. See [JSON patch](https://jsonpatch.com/) documentation for more details. For `patchType: JSONPatch`, the `patch` field contains a base64-encoded array of JSON patch operations. -As an example, a single patch operation that would set `spec.replicas` would be `[{"op": "add", "path": "/spec/replicas", "value": 3}]` +As an example, a single patch operation that would set `spec.replicas` would be +`[{"op": "add", "path": "/spec/replicas", "value": 3}]` Base64-encoded, this would be `W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0=` So a webhook response to add that label would be: -{{< tabs name="AdmissionReview_response_modify" >}} -{{% tab name="admission.k8s.io/v1" %}} + ```json { "apiVersion": "admission.k8s.io/v1", @@ -611,27 +459,12 @@ So a webhook response to add that label would be: } } ``` -{{% /tab %}} -{{% tab name="admission.k8s.io/v1beta1" %}} -```json -{ - "apiVersion": "admission.k8s.io/v1beta1", - "kind": "AdmissionReview", - "response": { - "uid": "", - "allowed": true, - "patchType": "JSONPatch", - "patch": "W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0=" - } -} -``` -{{% /tab %}} -{{< /tabs >}} -Starting in v1.19, admission webhooks can optionally return warning messages that are returned to the requesting client +Admission webhooks can optionally return warning messages that are returned to the requesting client in HTTP `Warning` headers with a warning code of 299. Warnings can be sent with allowed or rejected admission responses. If you're implementing a webhook that returns a warning: + * Don't include a "Warning:" prefix in the message * Use warning messages to describe problems the client making the API request should correct or be aware of * Limit warnings to 120 characters if possible @@ -641,8 +474,6 @@ Individual warning messages over 256 characters may be truncated by the API serv If more than 4096 characters of warning messages are added (from all sources), additional warning messages are ignored. {{< /caution >}} -{{< tabs name="AdmissionReview_response_warning" >}} -{{% tab name="admission.k8s.io/v1" %}} ```json { "apiVersion": "admission.k8s.io/v1", @@ -657,24 +488,6 @@ If more than 4096 characters of warning messages are added (from all sources), a } } ``` -{{% /tab %}} -{{% tab name="admission.k8s.io/v1beta1" %}} -```json -{ - "apiVersion": "admission.k8s.io/v1beta1", - "kind": "AdmissionReview", - "response": { - "uid": "", - "allowed": true, - "warnings": [ - "duplicate envvar entries specified with name MY_ENV", - "memory request less than 4MB specified for container mycontainer, which will not start successfully" - ] - } -} -``` -{{% /tab %}} -{{< /tabs >}} ## Webhook configuration @@ -683,9 +496,9 @@ The name of a `MutatingWebhookConfiguration` or a `ValidatingWebhookConfiguratio [DNS subdomain name](/docs/concepts/overview/working-with-objects/names#dns-subdomain-names). Each configuration can contain one or more webhooks. -If multiple webhooks are specified in a single configuration, each should be given a unique name. -This is required in `admissionregistration.k8s.io/v1`, but strongly recommended when using `admissionregistration.k8s.io/v1beta1`, -in order to make resulting audit logs and metrics easier to match up to active configurations. +If multiple webhooks are specified in a single configuration, each must be given a unique name. +This is required in order to make resulting audit logs and metrics easier to match up to active +configurations. Each webhook defines the following things. @@ -694,27 +507,31 @@ Each webhook defines the following things. Each webhook must specify a list of rules used to determine if a request to the API server should be sent to the webhook. Each rule specifies one or more operations, apiGroups, apiVersions, and resources, and a resource scope: -* `operations` lists one or more operations to match. Can be `"CREATE"`, `"UPDATE"`, `"DELETE"`, `"CONNECT"`, or `"*"` to match all. +* `operations` lists one or more operations to match. Can be `"CREATE"`, `"UPDATE"`, `"DELETE"`, `"CONNECT"`, + or `"*"` to match all. * `apiGroups` lists one or more API groups to match. `""` is the core API group. `"*"` matches all API groups. * `apiVersions` lists one or more API versions to match. `"*"` matches all API versions. * `resources` lists one or more resources to match. - * `"*"` matches all resources, but not subresources. - * `"*/*"` matches all resources and subresources. - * `"pods/*"` matches all subresources of pods. - * `"*/status"` matches all status subresources. -* `scope` specifies a scope to match. Valid values are `"Cluster"`, `"Namespaced"`, and `"*"`. Subresources match the scope of their parent resource. Supported in v1.14+. Default is `"*"`, matching pre-1.14 behavior. - * `"Cluster"` means that only cluster-scoped resources will match this rule (Namespace API objects are cluster-scoped). - * `"Namespaced"` means that only namespaced resources will match this rule. - * `"*"` means that there are no scope restrictions. -If an incoming request matches one of the specified operations, groups, versions, resources, and scope for any of a webhook's rules, the request is sent to the webhook. + * `"*"` matches all resources, but not subresources. + * `"*/*"` matches all resources and subresources. + * `"pods/*"` matches all subresources of pods. + * `"*/status"` matches all status subresources. + +* `scope` specifies a scope to match. Valid values are `"Cluster"`, `"Namespaced"`, and `"*"`. + Subresources match the scope of their parent resource. Default is `"*"`. + + * `"Cluster"` means that only cluster-scoped resources will match this rule (Namespace API objects are cluster-scoped). + * `"Namespaced"` means that only namespaced resources will match this rule. + * `"*"` means that there are no scope restrictions. + +If an incoming request matches one of the specified `operations`, `groups`, `versions`, +`resources`, and `scope` for any of a webhook's `rules`, the request is sent to the webhook. Here are other examples of rules that could be used to specify which resources should be intercepted. Match `CREATE` or `UPDATE` requests to `apps/v1` and `apps/v1beta1` `deployments` and `replicasets`: -{{< tabs name="ValidatingWebhookConfiguration_rules_1" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -729,123 +546,56 @@ webhooks: scope: "Namespaced" ... ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - rules: - - operations: ["CREATE", "UPDATE"] - apiGroups: ["apps"] - apiVersions: ["v1", "v1beta1"] - resources: ["deployments", "replicasets"] - scope: "Namespaced" - ... -``` -{{% /tab %}} -{{< /tabs >}} Match create requests for all resources (but not subresources) in all API groups and versions: -{{< tabs name="ValidatingWebhookConfiguration_rules_2" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration -... webhooks: -- name: my-webhook.example.com - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "*" - ... + - name: my-webhook.example.com + rules: + - operations: ["CREATE"] + apiGroups: ["*"] + apiVersions: ["*"] + resources: ["*"] + scope: "*" ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "*" - ... -``` -{{% /tab %}} -{{< /tabs >}} Match update requests for all `status` subresources in all API groups and versions: -{{< tabs name="ValidatingWebhookConfiguration_rules_3" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration -... webhooks: -- name: my-webhook.example.com - rules: - - operations: ["UPDATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*/status"] - scope: "*" - ... + - name: my-webhook.example.com + rules: + - operations: ["UPDATE"] + apiGroups: ["*"] + apiVersions: ["*"] + resources: ["*/status"] + scope: "*" ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - rules: - - operations: ["UPDATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*/status"] - scope: "*" - ... -``` -{{% /tab %}} -{{< /tabs >}} ### Matching requests: objectSelector -In v1.15+, webhooks may optionally limit which requests are intercepted based on the labels of the +Webhooks may optionally limit which requests are intercepted based on the labels of the objects they would be sent, by specifying an `objectSelector`. If specified, the objectSelector is evaluated against both the object and oldObject that would be sent to the webhook, and is considered to match if either object matches the selector. -A null object (oldObject in the case of create, or newObject in the case of delete), +A null object (`oldObject` in the case of create, or `newObject` in the case of delete), or an object that cannot have labels (like a `DeploymentRollback` or a `PodProxyOptions` object) is not considered to match. -Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. +Use the object selector only if the webhook is opt-in, because end users may skip +the admission webhook by setting the labels. This example shows a mutating webhook that would match a `CREATE` of any resource with the label `foo: bar`: -{{< tabs name="objectSelector_example" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration -... webhooks: - name: my-webhook.example.com objectSelector: @@ -857,32 +607,10 @@ webhooks: apiVersions: ["*"] resources: ["*"] scope: "*" - ... ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - objectSelector: - matchLabels: - foo: bar - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "*" - ... -``` -{{% /tab %}} -{{< /tabs >}} -See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels for more examples of label selectors. +See [labels concept](/docs/concepts/overview/working-with-objects/labels) +for more examples of label selectors. ### Matching requests: namespaceSelector @@ -897,128 +625,75 @@ If the object is a cluster scoped resource other than a Namespace, `namespaceSel This example shows a mutating webhook that matches a `CREATE` of any namespaced resource inside a namespace that does not have a "runlevel" label of "0" or "1": -{{< tabs name="MutatingWebhookConfiguration_namespaceSelector_1" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration -... webhooks: -- name: my-webhook.example.com - namespaceSelector: - matchExpressions: - - key: runlevel - operator: NotIn - values: ["0","1"] - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "Namespaced" - ... + - name: my-webhook.example.com + namespaceSelector: + matchExpressions: + - key: runlevel + operator: NotIn + values: ["0","1"] + rules: + - operations: ["CREATE"] + apiGroups: ["*"] + apiVersions: ["*"] + resources: ["*"] + scope: "Namespaced" ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - namespaceSelector: - matchExpressions: - - key: runlevel - operator: NotIn - values: ["0","1"] - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "Namespaced" - ... -``` -{{% /tab %}} -{{< /tabs >}} -This example shows a validating webhook that matches a `CREATE` of any namespaced resource inside a namespace -that is associated with the "environment" of "prod" or "staging": +This example shows a validating webhook that matches a `CREATE` of any namespaced resource inside +a namespace that is associated with the "environment" of "prod" or "staging": -{{< tabs name="ValidatingWebhookConfiguration_namespaceSelector_2" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration -... webhooks: -- name: my-webhook.example.com - namespaceSelector: - matchExpressions: - - key: environment - operator: In - values: ["prod","staging"] - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "Namespaced" - ... + - name: my-webhook.example.com + namespaceSelector: + matchExpressions: + - key: environment + operator: In + values: ["prod","staging"] + rules: + - operations: ["CREATE"] + apiGroups: ["*"] + apiVersions: ["*"] + resources: ["*"] + scope: "Namespaced" ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - namespaceSelector: - matchExpressions: - - key: environment - operator: In - values: ["prod","staging"] - rules: - - operations: ["CREATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] - scope: "Namespaced" - ... -``` -{{% /tab %}} -{{< /tabs >}} -See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels for more examples of label selectors. +See [labels concept](/docs/concepts/overview/working-with-objects/labels) +for more examples of label selectors. ### Matching requests: matchPolicy API servers can make objects available via multiple API groups or versions. -For example, the Kubernetes API server may allow creating and modifying `Deployment` objects -via `extensions/v1beta1`, `apps/v1beta1`, `apps/v1beta2`, and `apps/v1` APIs. -For example, if a webhook only specified a rule for some API groups/versions (like `apiGroups:["apps"], apiVersions:["v1","v1beta1"]`), +For example, if a webhook only specified a rule for some API groups/versions +(like `apiGroups:["apps"], apiVersions:["v1","v1beta1"]`), and a request was made to modify the resource via another API group/version (like `extensions/v1beta1`), the request would not be sent to the webhook. -In v1.15+, `matchPolicy` lets a webhook define how its `rules` are used to match incoming requests. +The `matchPolicy` lets a webhook define how its `rules` are used to match incoming requests. Allowed values are `Exact` or `Equivalent`. * `Exact` means a request should be intercepted only if it exactly matches a specified rule. -* `Equivalent` means a request should be intercepted if modifies a resource listed in `rules`, even via another API group or version. +* `Equivalent` means a request should be intercepted if modifies a resource listed in `rules`, + even via another API group or version. In the example given above, the webhook that only registered for `apps/v1` could use `matchPolicy`: * `matchPolicy: Exact` would mean the `extensions/v1beta1` request would not be sent to the webhook -* `matchPolicy: Equivalent` means the `extensions/v1beta1` request would be sent to the webhook (with the objects converted to a version the webhook had specified: `apps/v1`) +* `matchPolicy: Equivalent` means the `extensions/v1beta1` request would be sent to the webhook + (with the objects converted to a version the webhook had specified: `apps/v1`) Specifying `Equivalent` is recommended, and ensures that webhooks continue to intercept the resources they expect when upgrades enable new versions of the resource in the API server. -When a resource stops being served by the API server, it is no longer considered equivalent to other versions of that resource that are still served. +When a resource stops being served by the API server, it is no longer considered equivalent to +other versions of that resource that are still served. For example, `extensions/v1beta1` deployments were first deprecated and then removed (in Kubernetes v1.16). Since that removal, a webhook with a `apiGroups:["extensions"], apiVersions:["v1beta1"], resources:["deployments"]` rule @@ -1028,12 +703,9 @@ for stable versions of resources. This example shows a validating webhook that intercepts modifications to deployments (no matter the API group or version), and is always sent an `apps/v1` `Deployment` object: -{{< tabs name="ValidatingWebhookConfiguration_matchPolicy" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration -... webhooks: - name: my-webhook.example.com matchPolicy: Equivalent @@ -1043,32 +715,9 @@ webhooks: apiVersions: ["v1"] resources: ["deployments"] scope: "Namespaced" - ... ``` -Admission webhooks created using `admissionregistration.k8s.io/v1` default to `Equivalent`. -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - matchPolicy: Equivalent - rules: - - operations: ["CREATE","UPDATE","DELETE"] - apiGroups: ["apps"] - apiVersions: ["v1"] - resources: ["deployments"] - scope: "Namespaced" - ... -``` - -Admission webhooks created using `admissionregistration.k8s.io/v1beta1` default to `Exact`. -{{% /tab %}} -{{< /tabs >}} +The `matchPolicy` for an admission webhooks defaults to `Equivalent`. ### Contacting the webhook @@ -1086,51 +735,32 @@ and can optionally include a custom CA bundle to use to verify the TLS connectio The `host` should not refer to a service running in the cluster; use a service reference by specifying the `service` field instead. -The host might be resolved via external DNS in some apiservers +The host might be resolved via external DNS in some API servers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address. Please note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts -which run an apiserver which might need to make calls to this +which run an API server which might need to make calls to this webhook. Such installations are likely to be non-portable or not readily run in a new cluster. The scheme must be "https"; the URL must begin with "https://". -Attempting to use a user or basic auth (for example "user:password@") is not allowed. -Fragments ("#...") and query parameters ("?...") are also not allowed. +Attempting to use a user or basic auth (for example `user:password@`) is not allowed. +Fragments (`#...`) and query parameters (`?...`) are also not allowed. Here is an example of a mutating webhook configured to call a URL (and expects the TLS certificate to be verified using system trust roots, so does not specify a caBundle): -{{< tabs name="MutatingWebhookConfiguration_url" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration -... webhooks: - name: my-webhook.example.com clientConfig: url: "https://my-webhook.example.com:9443/my-webhook-path" - ... ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - clientConfig: - url: "https://my-webhook.example.com:9443/my-webhook-path" - ... -``` -{{% /tab %}} -{{< /tabs >}} #### Service reference @@ -1143,43 +773,24 @@ Here is an example of a mutating webhook configured to call a service on port "1 at the subpath "/my-path", and to verify the TLS connection against the ServerName `my-service-name.my-service-namespace.svc` using a custom CA bundle: -{{< tabs name="MutatingWebhookConfiguration_service" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration -... webhooks: - name: my-webhook.example.com clientConfig: - caBundle: "Ci0tLS0tQk......tLS0K" + caBundle: service: namespace: my-service-namespace name: my-service-name path: /my-path port: 1234 - ... ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - clientConfig: - caBundle: "Ci0tLS0tQk...<`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate>...tLS0K" - service: - namespace: my-service-namespace - name: my-service-name - path: /my-path - port: 1234 - ... -``` -{{% /tab %}} -{{< /tabs >}} + +{{< note >}} +You must replace the `` in the above example by a valid CA bundle +which is a PEM-encoded CA bundle for validating the webhook's server certificate. +{{< /note >}} ### Side effects @@ -1199,46 +810,20 @@ or the dry-run request will not be sent to the webhook and the API request will Webhooks indicate whether they have side effects using the `sideEffects` field in the webhook configuration: -* `Unknown`: no information is known about the side effects of calling the webhook. -If a request with `dryRun: true` would trigger a call to this webhook, the request will instead fail, and the webhook will not be called. * `None`: calling the webhook will have no side effects. -* `Some`: calling the webhook will possibly have side effects. -If a request with the dry-run attribute would trigger a call to this webhook, the request will instead fail, and the webhook will not be called. -* `NoneOnDryRun`: calling the webhook will possibly have side effects, -but if a request with `dryRun: true` is sent to the webhook, the webhook will suppress the side effects (the webhook is `dryRun`-aware). - -Allowed values: - -* In `admissionregistration.k8s.io/v1beta1`, `sideEffects` may be set to `Unknown`, `None`, `Some`, or `NoneOnDryRun`, and defaults to `Unknown`. -* In `admissionregistration.k8s.io/v1`, `sideEffects` must be set to `None` or `NoneOnDryRun`. +* `NoneOnDryRun`: calling the webhook will possibly have side effects, but if a request with + `dryRun: true` is sent to the webhook, the webhook will suppress the side effects (the webhook + is `dryRun`-aware). Here is an example of a validating webhook indicating it has no side effects on `dryRun: true` requests: -{{< tabs name="ValidatingWebhookConfiguration_sideEffects" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration -... webhooks: -- name: my-webhook.example.com - sideEffects: NoneOnDryRun - ... + - name: my-webhook.example.com + sideEffects: NoneOnDryRun ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - sideEffects: NoneOnDryRun - ... -``` -{{% /tab %}} -{{< /tabs >}} ### Timeouts @@ -1253,35 +838,15 @@ The timeout value must be between 1 and 30 seconds. Here is an example of a validating webhook with a custom timeout of 2 seconds: -{{< tabs name="ValidatingWebhookConfiguration_timeoutSeconds" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration -... webhooks: -- name: my-webhook.example.com - timeoutSeconds: 2 - ... + - name: my-webhook.example.com + timeoutSeconds: 2 ``` -Admission webhooks created using `admissionregistration.k8s.io/v1` default timeouts to 10 seconds. -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: ValidatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - timeoutSeconds: 2 - ... -``` - -Admission webhooks created using `admissionregistration.k8s.io/v1` default timeouts to 30 seconds. -{{% /tab %}} -{{< /tabs >}} +The timeout for an admission webhook defaults to 10 seconds. ### Reinvocation policy @@ -1290,50 +855,35 @@ A single ordering of mutating admissions plugins (including webhooks) does not w to the object (like adding a `container` to a `pod`), and other mutating plugins which have already run may have opinions on those new structures (like setting an `imagePullPolicy` on all containers). -In v1.15+, to allow mutating admission plugins to observe changes made by other plugins, +To allow mutating admission plugins to observe changes made by other plugins, built-in mutating admission plugins are re-run if a mutating webhook modifies an object, and mutating webhooks can specify a `reinvocationPolicy` to control whether they are reinvoked as well. `reinvocationPolicy` may be set to `Never` or `IfNeeded`. It defaults to `Never`. -* `Never`: the webhook must not be called more than once in a single admission evaluation +* `Never`: the webhook must not be called more than once in a single admission evaluation. * `IfNeeded`: the webhook may be called again as part of the admission evaluation if the object -being admitted is modified by other admission plugins after the initial webhook call. + being admitted is modified by other admission plugins after the initial webhook call. The important elements to note are: * The number of additional invocations is not guaranteed to be exactly one. -* If additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. +* If additional invocations result in further modifications to the object, webhooks are not + guaranteed to be invoked again. * Webhooks that use this option may be reordered to minimize the number of additional invocations. -* To validate an object after all mutations are guaranteed complete, use a validating admission webhook instead (recommended for webhooks with side-effects). +* To validate an object after all mutations are guaranteed complete, use a validating admission + webhook instead (recommended for webhooks with side-effects). -Here is an example of a mutating webhook opting into being re-invoked if later admission plugins modify the object: +Here is an example of a mutating webhook opting into being re-invoked if later admission plugins +modify the object: -{{< tabs name="MutatingWebhookConfiguration_reinvocationPolicy" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration -... webhooks: - name: my-webhook.example.com reinvocationPolicy: IfNeeded - ... ``` -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - reinvocationPolicy: IfNeeded - ... -``` -{{% /tab %}} -{{< /tabs >}} Mutating webhooks must be [idempotent](#idempotence), able to successfully process an object they have already admitted and potentially modified. This is true for all mutating admission webhooks, since any change they can make @@ -1349,35 +899,15 @@ are handled. Allowed values are `Ignore` or `Fail`. Here is a mutating webhook configured to reject an API request if errors are encountered calling the admission webhook: -{{< tabs name="MutatingWebhookConfiguration_failurePolicy" >}} -{{% tab name="admissionregistration.k8s.io/v1" %}} ```yaml apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration -... webhooks: - name: my-webhook.example.com failurePolicy: Fail - ... ``` -Admission webhooks created using `admissionregistration.k8s.io/v1` default `failurePolicy` to `Fail`. -{{% /tab %}} -{{% tab name="admissionregistration.k8s.io/v1beta1" %}} -```yaml -# Deprecated in v1.16 in favor of admissionregistration.k8s.io/v1 -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -... -webhooks: -- name: my-webhook.example.com - failurePolicy: Fail - ... -``` - -Admission webhooks created using `admissionregistration.k8s.io/v1beta1` default `failurePolicy` to `Ignore`. -{{% /tab %}} -{{< /tabs >}} +The default `failurePolicy` for an admission webhooks is `Fail`. ## Monitoring admission webhooks @@ -1388,121 +918,123 @@ monitoring mechanisms help cluster admins to answer questions like: 2. What change did the mutating webhook applied to the object? -3. Which webhooks are frequently rejecting API requests? What's the reason for a - rejection? +3. Which webhooks are frequently rejecting API requests? What's the reason for a rejection? ### Mutating webhook auditing annotations Sometimes it's useful to know which mutating webhook mutated the object in a API request, and what change did the webhook apply. -In v1.16+, kube-apiserver performs [auditing](/docs/tasks/debug/debug-cluster/audit/) on each mutating webhook -invocation. Each invocation generates an auditing annotation -capturing if a request object is mutated by the invocation, and optionally generates an annotation capturing the applied -patch from the webhook admission response. The annotations are set in the audit event for given request on given stage of -its execution, which is then pre-processed according to a certain policy and written to a backend. +The Kubernetes API server performs [auditing](/docs/tasks/debug/debug-cluster/audit/) on each +mutating webhook invocation. Each invocation generates an auditing annotation +capturing if a request object is mutated by the invocation, and optionally generates an annotation +capturing the applied patch from the webhook admission response. The annotations are set in the +audit event for given request on given stage of its execution, which is then pre-processed +according to a certain policy and written to a backend. The audit level of a event determines which annotations get recorded: - At `Metadata` audit level or higher, an annotation with key -`mutation.webhook.admission.k8s.io/round_{round idx}_index_{order idx}` gets logged with JSON payload indicating -a webhook gets invoked for given request and whether it mutated the object or not. + `mutation.webhook.admission.k8s.io/round_{round idx}_index_{order idx}` gets logged with JSON + payload indicating a webhook gets invoked for given request and whether it mutated the object or not. -For example, the following annotation gets recorded for a webhook being reinvoked. The webhook is ordered the third in the -mutating webhook chain, and didn't mutated the request object during the invocation. + For example, the following annotation gets recorded for a webhook being reinvoked. The webhook is + ordered the third in the mutating webhook chain, and didn't mutated the request object during the + invocation. -```yaml -# the audit event recorded -{ - "kind": "Event", - "apiVersion": "audit.k8s.io/v1", - "annotations": { - "mutation.webhook.admission.k8s.io/round_1_index_2": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook.example.com\",\"mutated\": false}" - # other annotations - ... - } - # other fields - ... -} -``` + ```yaml + # the audit event recorded + { + "kind": "Event", + "apiVersion": "audit.k8s.io/v1", + "annotations": { + "mutation.webhook.admission.k8s.io/round_1_index_2": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook.example.com\",\"mutated\": false}" + # other annotations + ... + } + # other fields + ... + } + ``` + + ```yaml + # the annotation value deserialized + { + "configuration": "my-mutating-webhook-configuration.example.com", + "webhook": "my-webhook.example.com", + "mutated": false + } + ``` + + The following annotation gets recorded for a webhook being invoked in the first round. The webhook + is ordered the first in the mutating webhook chain, and mutated the request object during the + invocation. -```yaml -# the annotation value deserialized -{ - "configuration": "my-mutating-webhook-configuration.example.com", - "webhook": "my-webhook.example.com", - "mutated": false -} -``` - -The following annotation gets recorded for a webhook being invoked in the first round. The webhook is ordered the first in\ -the mutating webhook chain, and mutated the request object during the invocation. - -```yaml -# the audit event recorded -{ - "kind": "Event", - "apiVersion": "audit.k8s.io/v1", - "annotations": { - "mutation.webhook.admission.k8s.io/round_0_index_0": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"mutated\": true}" - # other annotations - ... - } - # other fields - ... -} -``` - -```yaml -# the annotation value deserialized -{ - "configuration": "my-mutating-webhook-configuration.example.com", - "webhook": "my-webhook-always-mutate.example.com", - "mutated": true -} -``` + ```yaml + # the audit event recorded + { + "kind": "Event", + "apiVersion": "audit.k8s.io/v1", + "annotations": { + "mutation.webhook.admission.k8s.io/round_0_index_0": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"mutated\": true}" + # other annotations + ... + } + # other fields + ... + } + ``` + + ```yaml + # the annotation value deserialized + { + "configuration": "my-mutating-webhook-configuration.example.com", + "webhook": "my-webhook-always-mutate.example.com", + "mutated": true + } + ``` - At `Request` audit level or higher, an annotation with key -`patch.webhook.admission.k8s.io/round_{round idx}_index_{order idx}` gets logged with JSON payload indicating -a webhook gets invoked for given request and what patch gets applied to the request object. + `patch.webhook.admission.k8s.io/round_{round idx}_index_{order idx}` gets logged with JSON payload indicating + a webhook gets invoked for given request and what patch gets applied to the request object. -For example, the following annotation gets recorded for a webhook being reinvoked. The webhook is ordered the fourth in the -mutating webhook chain, and responded with a JSON patch which got applied to the request object. - -```yaml -# the audit event recorded -{ - "kind": "Event", - "apiVersion": "audit.k8s.io/v1", - "annotations": { - "patch.webhook.admission.k8s.io/round_1_index_3": "{\"configuration\":\"my-other-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"patch\":[{\"op\":\"add\",\"path\":\"/data/mutation-stage\",\"value\":\"yes\"}],\"patchType\":\"JSONPatch\"}" - # other annotations - ... - } - # other fields - ... -} -``` - -```yaml -# the annotation value deserialized -{ - "configuration": "my-other-mutating-webhook-configuration.example.com", - "webhook": "my-webhook-always-mutate.example.com", - "patchType": "JSONPatch", - "patch": [ - { - "op": "add", - "path": "/data/mutation-stage", - "value": "yes" - } - ] -} -``` + For example, the following annotation gets recorded for a webhook being reinvoked. The webhook is ordered the fourth in the + mutating webhook chain, and responded with a JSON patch which got applied to the request object. + + ```yaml + # the audit event recorded + { + "kind": "Event", + "apiVersion": "audit.k8s.io/v1", + "annotations": { + "patch.webhook.admission.k8s.io/round_1_index_3": "{\"configuration\":\"my-other-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"patch\":[{\"op\":\"add\",\"path\":\"/data/mutation-stage\",\"value\":\"yes\"}],\"patchType\":\"JSONPatch\"}" + # other annotations + ... + } + # other fields + ... + } + ``` + + ```yaml + # the annotation value deserialized + { + "configuration": "my-other-mutating-webhook-configuration.example.com", + "webhook": "my-webhook-always-mutate.example.com", + "patchType": "JSONPatch", + "patch": [ + { + "op": "add", + "path": "/data/mutation-stage", + "value": "yes" + } + ] + } + ``` ### Admission webhook metrics -Kube-apiserver exposes Prometheus metrics from the `/metrics` endpoint, which can be used for monitoring and +The API server exposes Prometheus metrics from the `/metrics` endpoint, which can be used for monitoring and diagnosing API server status. The following metrics record status related to admission webhooks. #### API server admission webhook rejection count @@ -1510,7 +1042,7 @@ diagnosing API server status. The following metrics record status related to adm Sometimes it's useful to know which admission webhooks are frequently rejecting API requests, and the reason for a rejection. -In v1.16+, kube-apiserver exposes a Prometheus counter metric recording admission webhook rejections. The +The API server exposes a Prometheus counter metric recording admission webhook rejections. The metrics are labelled to identify the causes of webhook rejection(s): - `name`: the name of the webhook that rejected a request. @@ -1519,11 +1051,13 @@ metrics are labelled to identify the causes of webhook rejection(s): - `type`: the admission webhook type, can be one of `admit` and `validating`. - `error_type`: identifies if an error occurred during the webhook invocation that caused the rejection. Its value can be one of: - - `calling_webhook_error`: unrecognized errors or timeout errors from the admission webhook happened and the - webhook's [Failure policy](#failure-policy) is set to `Fail`. - - `no_error`: no error occurred. The webhook rejected the request with `allowed: false` in the admission - response. The metrics label `rejection_code` records the `.status.code` set in the admission response. - - `apiserver_internal_error`: an API server internal error happened. + + - `calling_webhook_error`: unrecognized errors or timeout errors from the admission webhook happened and the + webhook's [Failure policy](#failure-policy) is set to `Fail`. + - `no_error`: no error occurred. The webhook rejected the request with `allowed: false` in the admission + response. The metrics label `rejection_code` records the `.status.code` set in the admission response. + - `apiserver_internal_error`: an API server internal error happened. + - `rejection_code`: the HTTP status code set in the admission response when a webhook rejected a request. @@ -1553,7 +1087,8 @@ the initial application. 2. For a `CREATE` pod request, if the field `.spec.containers[].resources.limits` of a container is not set, set default resource limits. -3. For a `CREATE` pod request, inject a sidecar container with name `foo-sidecar` if no container with the name `foo-sidecar` already exists. +3. For a `CREATE` pod request, inject a sidecar container with name `foo-sidecar` if no container + with the name `foo-sidecar` already exists. In the cases above, the webhook can be safely reinvoked, or admit an object that already has the fields set. @@ -1587,21 +1122,25 @@ versions. See [Matching requests: matchPolicy](#matching-requests-matchpolicy) f ### Availability -It is recommended that admission webhooks should evaluate as quickly as possible (typically in milliseconds), since they add to API request latency. +It is recommended that admission webhooks should evaluate as quickly as possible (typically in +milliseconds), since they add to API request latency. It is encouraged to use a small timeout for webhooks. See [Timeouts](#timeouts) for more detail. -It is recommended that admission webhooks should leverage some format of load-balancing, to provide high availability and -performance benefits. If a webhook is running within the cluster, you can run multiple webhook backends behind a service -to leverage the load-balancing that service supports. +It is recommended that admission webhooks should leverage some format of load-balancing, to +provide high availability and performance benefits. If a webhook is running within the cluster, +you can run multiple webhook backends behind a service to leverage the load-balancing that service +supports. ### Guaranteeing the final state of the object is seen Admission webhooks that need to guarantee they see the final state of the object in order to enforce policy should use a validating admission webhook, since objects can be modified after being seen by mutating webhooks. -For example, a mutating admission webhook is configured to inject a sidecar container with name "foo-sidecar" on every -`CREATE` pod request. If the sidecar *must* be present, a validating admisson webhook should also be configured to intercept `CREATE` pod requests, and validate -that a container with name "foo-sidecar" with the expected configuration exists in the to-be-created object. +For example, a mutating admission webhook is configured to inject a sidecar container with name +"foo-sidecar" on every `CREATE` pod request. If the sidecar *must* be present, a validating +admisson webhook should also be configured to intercept `CREATE` pod requests, and validate that a +container with name "foo-sidecar" with the expected configuration exists in the to-be-created +object. ### Avoiding deadlocks in self-hosted webhooks @@ -1614,7 +1153,8 @@ When a node that runs the webhook server pods becomes unhealthy, the webhook deployment will try to reschedule the pods to another node. However the requests will get rejected by the existing webhook server since the `"env"` label is unset, and the migration cannot happen. -It is recommended to exclude the namespace where your webhook is running with a [namespaceSelector](#matching-requests-namespaceselector). +It is recommended to exclude the namespace where your webhook is running with a +[namespaceSelector](#matching-requests-namespaceselector). ### Side effects @@ -1636,4 +1176,3 @@ If your admission webhooks don't intend to modify the behavior of the Kubernetes plane, exclude the `kube-system` namespace from being intercepted using a [`namespaceSelector`](#matching-requests-namespaceselector). -