動態准入控制
除了內建的准入外掛,准入外掛還可以作為擴充套件開發,並作為在執行時配置的 Webhook 執行。本頁面描述瞭如何構建、配置、使用和監控准入 Webhook。
什麼是准入 Webhook?
准入 Webhook 是接收准入請求並對其進行處理的 HTTP 回撥。你可以定義兩種型別的准入 Webhook:驗證准入 Webhook 和 變更准入 Webhook。變更准入 Webhook 首先被呼叫,可以修改傳送到 API 伺服器的物件以強制執行自定義預設值。在所有物件修改完成後,並且在 API 伺服器驗證了傳入物件之後,驗證准入 Webhook 被呼叫,可以拒絕請求以強制執行自定義策略。
注意
需要確保看到物件的最終狀態以強制執行策略的准入 Webhook 應該使用驗證准入 Webhook,因為物件在被變更 Webhook 看到後可能會被修改。准入 Webhook 實驗
准入 Webhook 本質上是叢集控制平面的一部分。你應該非常謹慎地編寫和部署它們。如果你打算編寫/部署生產級別的准入 Webhook,請閱讀使用者指南以獲取說明。下面,我們將介紹如何快速實驗准入 Webhook。
先決條件
確保已啟用 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 准入控制器。這裡是一般建議啟用的一組准入控制器。
確保已啟用
admissionregistration.k8s.io/v1
API。
編寫一個准入 Webhook 伺服器
請參閱在 Kubernetes 端到端測試中驗證的准入 Webhook 伺服器的實現。該 Webhook 處理 API 伺服器傳送的 AdmissionReview
請求,並以接收到的相同版本作為 AdmissionReview
物件返回其決定。
有關傳送到 Webhook 的資料詳情,請參閱Webhook 請求部分。
有關 Webhook 期望的資料詳情,請參閱Webhook 響應部分。
示例准入 Webhook 伺服器將 ClientAuth
欄位留空,預設為 NoClientCert
。這意味著 Webhook 伺服器不驗證客戶端(即 API 伺服器)的身份。如果你需要相互 TLS 或其他方式來驗證客戶端,請參閱如何驗證 API 伺服器。
部署准入 Webhook 服務
端到端測試中的 Webhook 伺服器透過部署 API 部署在 Kubernetes 叢集中。該測試還建立了一個服務作為 Webhook 伺服器的前端。請參閱程式碼。
你也可以在叢集外部署 Webhook。你需要相應地更新 Webhook 配置。
動態配置准入 Webhook
你可以透過 ValidatingWebhookConfiguration 或 MutatingWebhookConfiguration 動態配置哪些資源受哪些准入 Webhook 的影響。
以下是一個 ValidatingWebhookConfiguration
示例,變更 Webhook 配置類似。有關每個配置欄位的詳細資訊,請參閱Webhook 配置部分。
apiVersion: admissionregistration.k8s.io/v1
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: <CA_BUNDLE>
admissionReviewVersions: ["v1"]
sideEffects: None
timeoutSeconds: 5
注意
你必須將上述示例中的<CA_BUNDLE>
替換為有效的 CA 束,該 CA 束是 PEM 編碼的(欄位值是 Base64 編碼的)CA 束,用於驗證 Webhook 的伺服器證書。scope
欄位指定是否只有叢集範圍的資源("Cluster")或名稱空間範圍的資源("Namespaced")將匹配此規則。"*" 表示沒有範圍限制。
注意
使用clientConfig.service
時,伺服器證書必須對 <svc_name>.<svc_namespace>.svc
有效。注意
Webhook 呼叫的預設超時時間為 10 秒。你可以設定timeout
,建議對 Webhook 使用較短的超時時間。如果 Webhook 呼叫超時,請求將根據 Webhook 的失敗策略進行處理。當 API 伺服器收到與其中一個 rules
匹配的請求時,API 伺服器會將 admissionReview
請求傳送到 clientConfig
中指定的 Webhook。
建立 Webhook 配置後,系統需要幾秒鐘才能生效新配置。
驗證 API 伺服器
如果你的准入 Webhook 需要身份驗證,你可以配置 API 伺服器使用基本身份驗證、持有者令牌或證書來向 Webhook 進行身份驗證。完成配置需要三個步驟。
啟動 API 伺服器時,透過
--admission-control-config-file
標誌指定準入控制配置檔案的位置。在准入控制配置檔案中,指定 MutatingAdmissionWebhook 控制器和 ValidatingAdmissionWebhook 控制器應讀取憑據的位置。憑據儲存在 kubeConfig 檔案中(是的,與 kubectl 使用的相同模式),因此欄位名為
kubeConfigFile
。這是一個准入控制配置檔案的示例
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: WebhookAdmissionConfiguration
kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: WebhookAdmissionConfiguration
kubeConfigFile: "<path-to-kubeconfig-file>"
# Deprecated in v1.17 in favor of apiserver.config.k8s.io/v1
apiVersion: apiserver.k8s.io/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
configuration:
# Deprecated in v1.17 in favor of apiserver.config.k8s.io/v1, kind=WebhookAdmissionConfiguration
apiVersion: apiserver.config.k8s.io/v1alpha1
kind: WebhookAdmission
kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
configuration:
# Deprecated in v1.17 in favor of apiserver.config.k8s.io/v1, kind=WebhookAdmissionConfiguration
apiVersion: apiserver.config.k8s.io/v1alpha1
kind: WebhookAdmission
kubeConfigFile: "<path-to-kubeconfig-file>"
有關 AdmissionConfiguration
的更多資訊,請參閱AdmissionConfiguration (v1) 參考。有關每個配置欄位的詳細資訊,請參閱Webhook 配置部分。
在 kubeConfig 檔案中,提供憑據
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: "<pem encoded certificate>"
client-key-data: "<pem encoded key>"
# The `name` supports using * to wildcard-match prefixing segments.
- name: '*.webhook-company.org'
user:
password: "<password>"
username: "<name>"
# '*' is the default match.
- name: '*'
user:
token: "<token>"
當然,你需要設定 Webhook 伺服器來處理這些身份驗證請求。
Webhook 請求和響應
請求
Webhook 以 POST 請求傳送,Content-Type: application/json
,主體包含一個 admission.k8s.io
API 組中序列化為 JSON 的 AdmissionReview
API 物件。
Webhook 可以在其配置中使用 admissionReviewVersions
欄位指定它們接受的 AdmissionReview
物件版本
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
admissionReviewVersions: ["v1", "v1beta1"]
建立 Webhook 配置時,admissionReviewVersions
是一個必填欄位。Webhook 需要支援當前和先前 API 伺服器理解的至少一個 AdmissionReview
版本。
API 伺服器傳送它們支援的 admissionReviewVersions
列表中的第一個 AdmissionReview
版本。如果列表中沒有一個版本受到 API 伺服器的支援,則不允許建立配置。如果 API 伺服器遇到以前建立的 Webhook 配置,並且該配置不支援 API 伺服器知道如何傳送的任何 AdmissionReview
版本,則嘗試呼叫 Webhook 將失敗並受制於失敗策略。
此示例顯示了在更新 apps/v1
Deployment
的 scale
子資源請求的 AdmissionReview
物件中包含的資料
{
"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 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",
# 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
"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 contain 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
"dryRun": false
}
}
響應
Webhook 以 200 HTTP 狀態碼、Content-Type: application/json
和包含 AdmissionReview
物件(以傳送它們的相同版本)的主體進行響應,其中 response
節已填充並序列化為 JSON。
至少,response
節必須包含以下欄位
uid
,從傳送到 Webhook 的request.uid
複製allowed
,設定為true
或false
Webhook 允許請求的最小響應示例
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": "<value from request.uid>",
"allowed": true
}
}
Webhook 拒絕請求的最小響應示例
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": "<value from request.uid>",
"allowed": false
}
}
拒絕請求時,Webhook 可以使用 status
欄位自定義返回給使用者的 http 程式碼和訊息。指定的 status 物件會返回給使用者。有關 status
型別的詳細資訊,請參閱 API 文件。以下是拒絕請求的響應示例,其中自定義了呈現給使用者的 HTTP 狀態碼和訊息
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": "<value from request.uid>",
"allowed": false,
"status": {
"code": 403,
"message": "You cannot do this because it is Tuesday and your name starts with A"
}
}
}
允許請求時,變更准入 Webhook 還可以選擇性地修改傳入物件。這是透過響應中的 patch
和 patchType
欄位完成的。當前唯一支援的 patchType
是 JSONPatch
。有關更多詳細資訊,請參閱JSON patch 文件。對於 patchType: JSONPatch
,patch
欄位包含 Base64 編碼的 JSON patch 運算元組。
例如,一個設定 spec.replicas
的單個補丁操作將是 [{"op": "add", "path": "/spec/replicas", "value": 3}]
Base64 編碼後,這將是 W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0=
因此,新增該標籤的 Webhook 響應將是
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": "<value from request.uid>",
"allowed": true,
"patchType": "JSONPatch",
"patch": "W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0="
}
}
准入 Webhook 可以選擇返回警告訊息,這些訊息將透過 HTTP Warning
標頭返回給請求客戶端,警告程式碼為 299。警告可以隨允許或拒絕的准入響應一起傳送。
如果你正在實現一個返回警告的 Webhook
- 不要在訊息中包含 "Warning:" 字首
- 使用警告訊息描述 API 請求客戶端應糾正或注意的問題
- 如果可能,將警告限制在 120 個字元
注意
超過 256 個字元的單個警告訊息可能會在返回給客戶端之前被 API 伺服器截斷。如果添加了超過 4096 個字元的警告訊息(來自所有來源),則會忽略額外的警告訊息。{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": "<value from request.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"
]
}
}
Webhook 配置
要註冊准入 Webhook,請建立 MutatingWebhookConfiguration
或 ValidatingWebhookConfiguration
API 物件。MutatingWebhookConfiguration
或 ValidatingWebhookConfiguration
物件的名稱必須是有效的 DNS 子域名。
每個配置可以包含一個或多個 Webhook。如果在單個配置中指定了多個 Webhook,則每個 Webhook 都必須有一個唯一的名稱。這是必需的,以便使生成的審計日誌和指標更容易與活動配置匹配。
每個 Webhook 定義以下內容。
匹配請求:規則
每個 Webhook 必須指定一個規則列表,用於確定是否應將對 API 伺服器的請求傳送到 Webhook。每個規則指定一個或多個操作、apiGroups、apiVersions 和資源,以及一個資源範圍
operations
列出一個或多個要匹配的操作。可以是"CREATE"
、"UPDATE"
、"DELETE"
、"CONNECT"
,或"*"
以匹配所有操作。apiGroups
列出一個或多個要匹配的 API 組。""
是核心 API 組。"*"
匹配所有 API 組。apiVersions
列出一個或多個要匹配的 API 版本。"*"
匹配所有 API 版本。resources
列出一個或多個要匹配的資源。"*"
匹配所有資源,但不匹配子資源。"*/*"
匹配所有資源和子資源。"pods/*"
匹配 pods 的所有子資源。"*/status"
匹配所有狀態子資源。
scope
指定要匹配的範圍。有效值為"Cluster"
、"Namespaced"
和"*"
。子資源匹配其父資源的範圍。預設為"*"
。"Cluster"
表示只有叢集範圍的資源將匹配此規則(Namespace API 物件是叢集範圍的)。"Namespaced"
表示只有名稱空間範圍的資源將匹配此規則。"*"
表示沒有範圍限制。
如果傳入請求與 Webhook 任何一個 rules
中指定的 operations
、groups
、versions
、resources
和 scope
之一匹配,則請求將傳送到 Webhook。
以下是可用於指定應攔截哪些資源的其他規則示例。
匹配 apps/v1
和 apps/v1beta1
deployments
和 replicasets
的 CREATE
或 UPDATE
請求
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: ["apps"]
apiVersions: ["v1", "v1beta1"]
resources: ["deployments", "replicasets"]
scope: "Namespaced"
...
匹配所有 API 組和版本中所有資源(但不包括子資源)的建立請求
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
rules:
- operations: ["CREATE"]
apiGroups: ["*"]
apiVersions: ["*"]
resources: ["*"]
scope: "*"
匹配所有 API 組和版本中所有 status
子資源的更新請求
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
rules:
- operations: ["UPDATE"]
apiGroups: ["*"]
apiVersions: ["*"]
resources: ["*/status"]
scope: "*"
匹配請求:objectSelector
Webhook 可以透過指定 objectSelector
,根據要傳送給它們的物件的標籤來選擇性地限制哪些請求被攔截。如果指定了 objectSelector
,它將針對傳送給 Webhook 的 object
和 oldObject
進行評估,如果其中任何一個物件與選擇器匹配,則認為匹配。
空物件(在建立時為 oldObject
,在刪除時為 newObject
)或沒有標籤的物件(如 DeploymentRollback
或 PodProxyOptions
物件)不被視為匹配。
僅當 Webhook 是可選的才使用物件選擇器,因為終端使用者可能會透過設定標籤來跳過准入 Webhook。
此示例顯示了一個變更 Webhook,它將匹配帶有標籤 foo: bar
的任何資源(但不包括子資源)的 CREATE
請求
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
objectSelector:
matchLabels:
foo: bar
rules:
- operations: ["CREATE"]
apiGroups: ["*"]
apiVersions: ["*"]
resources: ["*"]
scope: "*"
有關標籤選擇器的更多示例,請參閱標籤概念。
匹配請求:namespaceSelector
Webhook 可以選擇性地限制哪些名稱空間資源的請求被攔截,這基於包含名稱空間的標籤,透過指定 namespaceSelector
。
namespaceSelector
根據名稱空間的標籤是否與選擇器匹配,決定是否對名稱空間資源(或 Namespace 物件)的請求執行 Webhook。如果物件本身是名稱空間,則匹配在 object.metadata.labels 上執行。如果物件是除 Namespace 之外的叢集範圍資源,則 namespaceSelector
無效。
此示例顯示了一個變更 Webhook,它匹配任何名稱空間資源的 CREATE
請求,該資源位於沒有 "runlevel" 標籤為 "0" 或 "1" 的名稱空間中
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"
此示例顯示了一個驗證 Webhook,它匹配任何名稱空間資源的 CREATE
請求,該資源位於與 "environment" 為 "prod" 或 "staging" 關聯的名稱空間中
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"
有關標籤選擇器的更多示例,請參閱標籤概念。
匹配請求:matchPolicy
API 伺服器可以透過多個 API 組或版本提供物件。
例如,如果 Webhook 僅為某些 API 組/版本(如 apiGroups:["apps"], apiVersions:["v1","v1beta1"]
)指定了一個規則,並且透過另一個 API 組/版本(如 extensions/v1beta1
)發出了修改資源的請求,則該請求將不會發送到 Webhook。
matchPolicy
允許 Webhook 定義其 rules
如何用於匹配傳入請求。允許的值為 Exact
或 Equivalent
。
Exact
表示只有當請求與指定規則精確匹配時才應攔截該請求。Equivalent
表示如果請求修改了rules
中列出的資源,即使透過另一個 API 組或版本,也應攔截該請求。
在上述示例中,僅註冊了 apps/v1
的 Webhook 可以使用 matchPolicy
matchPolicy: Exact
意味著extensions/v1beta1
請求將不會發送到 WebhookmatchPolicy: Equivalent
意味著extensions/v1beta1
請求將傳送到 Webhook(物件將轉換為 Webhook 指定的版本:apps/v1
)
建議指定 Equivalent
,並確保在升級在 API 伺服器中啟用新版本的資源時,Webhook 繼續攔截它們期望的資源。
當一個資源停止由 API 伺服器提供服務時,它不再被視為與仍在提供服務的該資源的其他版本等效。例如,extensions/v1beta1
部署首先被棄用,然後被移除(在 Kubernetes v1.16 中)。
自那次移除後,帶有 apiGroups:["extensions"], apiVersions:["v1beta1"], resources:["deployments"]
規則的 Webhook 不再攔截透過 apps/v1
API 建立的部署。因此,Webhook 應該優先註冊資源的穩定版本。
此示例顯示了一個驗證 Webhook,它攔截對部署的修改(無論 API 組或版本),並且始終傳送 apps/v1
Deployment
物件
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
matchPolicy: Equivalent
rules:
- operations: ["CREATE","UPDATE","DELETE"]
apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
scope: "Namespaced"
准入 Webhook 的 matchPolicy
預設為 Equivalent
。
匹配請求:matchConditions
Kubernetes v1.30 [stable]
(預設啟用:true)如果你需要細粒度的請求過濾,可以為 Webhook 定義 **匹配條件**。如果發現匹配規則、`objectSelectors` 和 `namespaceSelectors` 仍然無法提供所需的 HTTP 呼叫過濾,這些條件會很有用。匹配條件是 CEL 表示式。所有匹配條件都必須評估為 true 才能呼叫 Webhook。
以下示例說明了 match conditions 的幾種不同用法
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
matchPolicy: Equivalent
rules:
- operations: ['CREATE','UPDATE']
apiGroups: ['*']
apiVersions: ['*']
resources: ['*']
failurePolicy: 'Ignore' # Fail-open (optional)
sideEffects: None
clientConfig:
service:
namespace: my-namespace
name: my-webhook
caBundle: '<omitted>'
# You can have up to 64 matchConditions per webhook
matchConditions:
- name: 'exclude-leases' # Each match condition must have a unique name
expression: '!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")' # Match non-lease resources.
- name: 'exclude-kubelet-requests'
expression: '!("system:nodes" in request.userInfo.groups)' # Match requests made by non-node users.
- name: 'rbac' # Skip RBAC requests, which are handled by the second webhook.
expression: 'request.resource.group != "rbac.authorization.k8s.io"'
# This example illustrates the use of the 'authorizer'. The authorization check is more expensive
# than a simple expression, so in this example it is scoped to only RBAC requests by using a second
# webhook. Both webhooks can be served by the same endpoint.
- name: rbac.my-webhook.example.com
matchPolicy: Equivalent
rules:
- operations: ['CREATE','UPDATE']
apiGroups: ['rbac.authorization.k8s.io']
apiVersions: ['*']
resources: ['*']
failurePolicy: 'Fail' # Fail-closed (the default)
sideEffects: None
clientConfig:
service:
namespace: my-namespace
name: my-webhook
caBundle: '<omitted>'
# You can have up to 64 matchConditions per webhook
matchConditions:
- name: 'breakglass'
# Skip requests made by users authorized to 'breakglass' on this webhook.
# The 'breakglass' API verb does not need to exist outside this check.
expression: '!authorizer.group("admissionregistration.k8s.io").resource("validatingwebhookconfigurations").name("my-webhook.example.com").check("breakglass").allowed()'
注意
每個 Webhook 的matchConditions
欄位中最多可以定義 64 個元素。匹配條件可以訪問以下 CEL 變數
object
- 來自傳入請求的物件。對於 DELETE 請求,該值為 null。物件版本可能會根據匹配策略進行轉換。oldObject
- 現有物件。對於 CREATE 請求,該值為 null。request
- AdmissionReview 的請求部分,不包括object
和oldObject
。authorizer
- 一個 CEL 授權器。可用於對請求的主體(認證使用者)執行授權檢查。有關詳細資訊,請參閱 Kubernetes CEL 庫文件中的 Authz。authorizer.requestResource
- 用於配置請求資源(組、資源、(子資源)、名稱空間、名稱)的授權檢查的快捷方式。
有關 CEL 表示式的更多資訊,請參閱Kubernetes 中的通用表示式語言參考。
如果在評估匹配條件時發生錯誤,則永遠不會呼叫 Webhook。是否拒絕請求的決定如下:
- 如果 **任何** 匹配條件評估為
false
(無論其他錯誤如何),API 伺服器將跳過 Webhook。 - 否則
- 對於
failurePolicy: Fail
,拒絕請求(不呼叫 Webhook)。 - 對於
failurePolicy: Ignore
,繼續請求但跳過 Webhook。
- 對於
聯絡 Webhook
一旦 API 伺服器確定應將請求傳送到 Webhook,它就需要知道如何聯絡 Webhook。這在 Webhook 配置的 clientConfig
節中指定。
Webhook 可以透過 URL 或服務引用呼叫,並且可以選擇包含自定義 CA 束以驗證 TLS 連線。
URL
url
以標準 URL 形式(scheme://host:port/path
)給出 Webhook 的位置。
host
不應引用叢集中執行的服務;而應透過指定 service
欄位使用服務引用。某些 API 伺服器(例如,kube-apiserver
無法解析叢集內 DNS,因為這將違反分層原則)可能透過外部 DNS 解析 host
。host
也可以是 IP 地址。
請注意,使用 localhost
或 127.0.0.1
作為 host
存在風險,除非你非常小心地將此 Webhook 執行在所有需要呼叫此 Webhook 的 API 伺服器主機上。此類安裝可能無法移植或不易在新叢集中執行。
方案必須是“https”;URL 必須以“https://”開頭。
嘗試使用使用者或基本身份驗證(例如 user:password@
)是不允許的。片段(#...
)和查詢引數(?...
)也不允許。
以下是一個配置為呼叫 URL 的變更 Webhook 示例(並期望使用系統信任根驗證 TLS 證書,因此未指定 caBundle)
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
clientConfig:
url: "https://my-webhook.example.com:9443/my-webhook-path"
服務引用
clientConfig
中的 service
節是對該 Webhook 服務的引用。如果 Webhook 在叢集內執行,則應使用 service
而不是 url
。服務名稱空間和名稱是必需的。埠是可選的,預設為 443。路徑是可選的,預設為 "/"。
以下是一個變更 Webhook 的示例,它配置為在埠 "1234" 的子路徑 "/my-path" 呼叫服務,並使用自定義 CA 束驗證針對 ServerName my-service-name.my-service-namespace.svc
的 TLS 連線
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
clientConfig:
caBundle: <CA_BUNDLE>
service:
namespace: my-service-namespace
name: my-service-name
path: /my-path
port: 1234
注意
你必須將上述示例中的<CA_BUNDLE>
替換為有效的 CA 束,該 CA 束是 PEM 編碼的 CA 束,用於驗證 Webhook 的伺服器證書。副作用
Webhook 通常僅對其收到的 AdmissionReview
內容進行操作。然而,有些 Webhook 在處理准入請求時會進行帶外更改。
進行帶外更改(“副作用”)的 Webhook 還必須具有協調機制(如控制器),該機制定期確定世界的實際狀態,並調整由准入 Webhook 修改的帶外資料以反映現實。這是因為對準入 Webhook 的呼叫不能保證被准入的物件將按原樣持久化,或者根本不會持久化。後續的 Webhook 可能會修改物件的內容,在寫入儲存時可能會遇到衝突,或者伺服器在持久化物件之前可能會斷電。
此外,帶有副作用的 Webhook 在處理 dryRun: true
准入請求時必須跳過這些副作用。Webhook 必須明確指示在以 dryRun
執行時不會產生副作用,否則 dry-run 請求將不會發送到 Webhook,API 請求將失敗。
Webhook 使用 Webhook 配置中的 sideEffects
欄位指示它們是否具有副作用
None
:呼叫 Webhook 不會產生任何副作用。NoneOnDryRun
:呼叫 Webhook 可能會產生副作用,但如果傳送帶有dryRun: true
的請求到 Webhook,Webhook 將抑制副作用(WebhookdryRun
-aware)。
以下是一個驗證 Webhook 的示例,它指示對 dryRun: true
請求沒有副作用
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
sideEffects: NoneOnDryRun
超時
由於 Webhook 會增加 API 請求延遲,因此它們應儘快評估。timeoutSeconds
允許配置 API 伺服器在將呼叫視為失敗之前應等待 Webhook 響應多長時間。
如果在 Webhook 響應之前超時,Webhook 呼叫將被忽略,或者 API 呼叫將根據失敗策略被拒絕。
超時值必須介於 1 到 30 秒之間。
以下是一個具有自定義超時時間為 2 秒的驗證 Webhook 示例
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
timeoutSeconds: 2
准入 Webhook 的預設超時時間為 10 秒。
重新呼叫策略
單個變更准入外掛(包括 Webhook)的排序並不適用於所有情況(例如,請參閱 https://issue.k8s.io/64333)。變更 Webhook 可以向物件新增新的子結構(例如向 pod
新增 container
),而其他已經執行的變更外掛可能對這些新結構有自己的看法(例如設定所有容器的 imagePullPolicy
)。
為了允許變更准入外掛觀察其他外掛所做的更改,如果變更 Webhook 修改了物件,內建的變更准入外掛將重新執行,並且變更 Webhook 可以指定 reinvocationPolicy
來控制它們是否也重新呼叫。
reinvocationPolicy
可以設定為 Never
或 IfNeeded
。預設為 Never
。
Never
:在單個准入評估中,Webhook 不得被呼叫多次。IfNeeded
:如果物件在初始 Webhook 呼叫後被其他准入外掛修改,則作為準入評估的一部分,Webhook 可能會再次被呼叫。
需要注意的重要元素是
- 不能保證額外的呼叫次數正好為一次。
- 如果額外的呼叫導致物件進一步修改,不能保證再次呼叫 Webhook。
- 使用此選項的 Webhook 可能會重新排序,以最大程度地減少額外呼叫的次數。
- 要在所有變更都保證完成後驗證物件,請改用驗證准入 Webhook(建議用於有副作用的 Webhook)。
以下是一個變更 Webhook 的示例,它選擇在後續准入外掛修改物件時重新呼叫
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
reinvocationPolicy: IfNeeded
變更 Webhook 必須是冪等的,能夠成功處理已准入並可能已修改的物件。這對所有變更准入 Webhook 都是如此,因為它們可以在物件中進行的任何更改都可能已經存在於使用者提供的物件中,但對於選擇重新呼叫的 Webhook 來說,這一點至關重要。
失敗策略
failurePolicy
定義瞭如何處理來自准入 Webhook 的無法識別的錯誤和超時錯誤。允許的值為 Ignore
或 Fail
。
Ignore
表示呼叫 Webhook 發生的錯誤將被忽略,API 請求允許繼續。Fail
表示呼叫 Webhook 發生的錯誤將導致准入失敗,並且 API 請求被拒絕。
以下是一個變更 Webhook 的配置,如果呼叫准入 Webhook 時遇到錯誤,它將拒絕 API 請求
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
failurePolicy: Fail
准入 Webhook 的預設 failurePolicy
是 Fail
。
監控准入 Webhook
API 伺服器提供監控准入 Webhook 行為的方法。這些監控機制幫助叢集管理員回答以下問題:
哪個變更 Webhook 在 API 請求中修改了物件?
變更 Webhook 對物件應用了什麼更改?
哪些 Webhook 頻繁拒絕 API 請求?拒絕的原因是什麼?
變更 Webhook 審計註解
有時瞭解哪個變更 Webhook 在 API 請求中修改了物件以及 Webhook 應用了什麼更改會很有用。
Kubernetes API 伺服器對每次變更 Webhook 呼叫執行審計。每次呼叫都會生成一個審計註解,捕獲請求物件是否被呼叫修改,並可選擇生成一個註解,捕獲 Webhook 准入響應中應用的補丁。這些註解在給定請求執行的給定階段的審計事件中設定,然後根據特定策略進行預處理並寫入後端。
事件的審計級別決定了記錄哪些註解
在
Metadata
審計級別或更高,會記錄一個鍵為mutation.webhook.admission.k8s.io/round_{round idx}_index_{order idx}
的註解,其中包含 JSON 有效載荷,指示 Webhook 被呼叫以處理給定請求以及它是否修改了物件。例如,以下註解記錄了 Webhook 被重新呼叫。Webhook 在變更 Webhook 鏈中排序為第三個,並且在呼叫期間未修改請求物件。
# 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 ... }
# the annotation value deserialized { "configuration": "my-mutating-webhook-configuration.example.com", "webhook": "my-webhook.example.com", "mutated": false }
以下註解記錄了 Webhook 在第一輪中被呼叫。Webhook 在變更 Webhook 鏈中排序為第一個,並在呼叫期間修改了請求物件。
# 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 ... }
# the annotation value deserialized { "configuration": "my-mutating-webhook-configuration.example.com", "webhook": "my-webhook-always-mutate.example.com", "mutated": true }
在
Request
審計級別或更高級別,將記錄一個鍵為patch.webhook.admission.k8s.io/round_{round idx}_index_{order idx}
的註解,其中包含 JSON 有效載荷,指示 Webhook 被呼叫以處理給定請求以及應用於請求物件的補丁。例如,以下註解記錄了 Webhook 被重新呼叫。Webhook 在變更 Webhook 鏈中排序為第四個,並響應了一個 JSON 補丁,該補丁已應用於請求物件。
# 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 ... }
# 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" } ] }
准入 Webhook 指標
API 伺服器透過 /metrics
端點公開 Prometheus 指標,可用於監控和診斷 API 伺服器狀態。以下指標記錄與准入 Webhook 相關的狀態。
API 伺服器准入 Webhook 拒絕計數
有時瞭解哪些准入 Webhook 頻繁拒絕 API 請求以及拒絕原因會很有用。
API 伺服器公開一個 Prometheus 計數器指標,記錄准入 Webhook 拒絕。這些指標帶有標籤,用於識別 Webhook 拒絕的原因:
name
:拒絕請求的 Webhook 名稱。operation
:請求的操作型別,可以是CREATE
、UPDATE
、DELETE
和CONNECT
之一。type
:准入 Webhook 型別,可以是admit
和validating
之一。error_type
:識別在 Webhook 呼叫期間是否發生導致拒絕的錯誤。其值可以是以下之一:calling_webhook_error
:准入 Webhook 發生無法識別的錯誤或超時錯誤,並且 Webhook 的失敗策略設定為Fail
。no_error
:未發生錯誤。Webhook 在准入響應中將請求拒絕為allowed: false
。指標標籤rejection_code
記錄了准入響應中設定的.status.code
。apiserver_internal_error
:發生 API 伺服器內部錯誤。
rejection_code
:當 Webhook 拒絕請求時,在准入響應中設定的 HTTP 狀態碼。
拒絕計數指標示例
# HELP apiserver_admission_webhook_rejection_count [ALPHA] Admission webhook rejection count, identified by name and broken out for each admission type (validating or admit) and operation. Additional labels specify an error type (calling_webhook_error or apiserver_internal_error if an error occurred; no_error otherwise) and optionally a non-zero rejection code if the webhook rejects the request with an HTTP status code (honored by the apiserver when the code is greater or equal to 400). Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded.
# TYPE apiserver_admission_webhook_rejection_count counter
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="always-timeout-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="invalid-admission-response-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="no_error",name="deny-unwanted-configmap-data.example.com",operation="CREATE",rejection_code="400",type="validating"} 13
最佳實踐和警告
有關編寫變更准入 Webhook 時的建議和注意事項,請參閱准入 Webhook 最佳實踐。