動態許可控制
除了編譯時內建的許可外掛程式,許可外掛程式可以開發為擴充功能,並以在執行時設定的 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 e2e 測試中驗證的 許可 Webhook 伺服器的實作。Webhook 處理 API 伺服器傳送的 AdmissionReview
請求,並以接收時相同的版本將其決策作為 AdmissionReview
物件傳回。
請參閱 Webhook 請求 章節,以取得傳送至 Webhook 的資料詳細資訊。
請參閱 Webhook 回應 章節,以取得 Webhook 預期的資料。
範例許可 Webhook 伺服器將 ClientAuth
欄位留空,預設為 NoClientCert
。這表示 Webhook 伺服器不會驗證用戶端的身份(假定為 API 伺服器)。如果您需要相互 TLS 或其他驗證用戶端的方式,請參閱如何驗證 API 伺服器。
部署許可 Webhook 服務
e2e 測試中的 Webhook 伺服器是透過 deployment 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 憑證包,該憑證包是用於驗證 Webhook 伺服器憑證的 PEM 編碼 (欄位值為 Base64 編碼) CA 憑證包。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
,主體是以 JSON 序列化的 admission.k8s.io
API 群組中的 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
清單中的第一個版本。如果清單中沒有任何版本被 API 伺服器支援,則不允許建立設定。如果 API 伺服器遇到先前建立的 webhook 設定,且不支援 API 伺服器知道如何傳送的任何 AdmissionReview
版本,則呼叫 webhook 的嘗試將會失敗,並受制於 失敗政策。
此範例顯示 AdmissionReview
物件中包含的資料,用於更新 apps/v1
Deployment
的 scale
子資源的請求
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 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
回應
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
類型的詳細資訊,請參閱 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
的單一 patch 操作將會是 [{"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/*"
比對 pod 的所有子資源。"*/status"
比對所有 status 子資源。
scope
指定要比對的範圍。有效值為"Cluster"
、"Namespaced"
和"*"
。子資源比對其父資源的範圍。預設值為"*"
。"Cluster"
表示只有叢集範圍的資源會比對此規則(命名空間 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
。如果指定了,則會針對將傳送至 webhook 的物件和 oldObject 評估 objectSelector,並且如果任一物件符合選取器,則視為符合。
空物件(在建立的情況下為 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
決定是否在命名空間資源(或命名空間物件)的請求上執行 webhook,根據命名空間的標籤是否符合選取器。如果物件本身是命名空間,則比對會在 object.metadata.labels 上執行。如果物件是命名空間以外的叢集範圍資源,則 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
,該命名空間與 "prod" 或 "staging" 的 "environment" 相關聯
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
表示即使透過另一個 API 群組或版本修改rules
中列出的資源,也應攔截請求。
在上面給出的範例中,僅註冊 apps/v1
的 webhook 可以使用 matchPolicy
matchPolicy: Exact
表示extensions/v1beta1
請求不會傳送至 webhookmatchPolicy: Equivalent
表示extensions/v1beta1
請求將會傳送至 webhook(物件已轉換為 webhook 指定的版本:apps/v1
)
建議指定 Equivalent
,並確保當升級在 API 伺服器中啟用資源的新版本時,webhook 繼續攔截它們期望的資源。
當 API 伺服器停止提供資源時,它不再被視為與仍在提供的該資源的其他版本等效。例如,extensions/v1beta1
deployments 首先被棄用,然後被移除(在 Kubernetes v1.16 中)。
自從移除以來,具有 apiGroups:["extensions"], apiVersions:["v1beta1"], resources:["deployments"]
規則的 webhook 不會攔截透過 apps/v1
API 建立的 deployments。因此,webhook 應優先註冊資源的穩定版本。
此範例顯示一個驗證性 webhook,它會攔截對 deployments 的修改(無論 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 才會被呼叫。
以下範例說明了比對條件的一些不同用途
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。物件版本可能會根據 matchPolicy 進行轉換。oldObject
- 現有的物件。對於 CREATE 請求,值為 null。request
- AdmissionReview 的請求部分,不包括object
和oldObject
。authorizer
- CEL Authorizer。可用於對請求的主體(已驗證的使用者)執行授權檢查。有關更多詳細資訊,請參閱 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 伺服器中,主機可能會透過外部 DNS 解析(例如,kube-apiserver
無法解析叢集內 DNS,因為這會違反分層)。host
也可能是 IP 位址。
請注意,除非您非常小心地在所有執行 API 伺服器的主機上執行此 webhook,否則使用 localhost
或 127.0.0.1
作為 host
是有風險的,這些主機可能需要呼叫此 webhook。此類安裝可能不具可移植性,或不容易在新叢集中執行。
scheme 必須是 "https";URL 必須以 "https://" 開頭。
不允許嘗試使用使用者或基本驗證(例如 user:password@
)。也不允許片段 (#...
) 和查詢參數 (?...
)。
以下範例是一個變更性 webhook,設定為呼叫 URL(並期望使用系統信任根目錄驗證 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
。服務命名空間和名稱是必要的。port 是選用的,預設為 443。path 是選用的,預設為 "/"。
以下範例是一個變更性 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 捆綁包,它是用於驗證 webhook 伺服器憑證的 PEM 編碼 CA 捆綁包。副作用
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 將會抑制副作用(webhook 是dryRun
感知的)。
以下範例是一個驗證性 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 秒之間。
以下範例是一個驗證性 webhook,具有 2 秒的自訂逾時
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
timeoutSeconds: 2
准入 webhook 的逾時預設為 10 秒。
重新調用策略
單一變更性准入外掛程式(包括 webhook)的排序並不適用於所有情況(例如,請參閱 https://issue.k8s.io/64333)。變更性 webhook 可以將新的子結構新增至物件(例如將 container
新增至 pod
),而已經執行的其他變更性外掛程式可能對這些新的結構有意見(例如在所有容器上設定 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 准入回應的已套用 patch。註解會在給定請求的稽核事件中設定,並在執行階段預先處理,然後寫入後端。
事件的稽核層級決定記錄哪些註解
在
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 被調用以處理給定請求,以及對請求物件套用了什麼 patch。例如,以下註解會針對重新調用的 webhook 記錄。Webhook 在變更性 webhook 鏈中排序為第四個,並以 JSON patch 回應,該 patch 已套用至請求物件。
# 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 範例
對於
CREATE
pod 請求,將 pod 的欄位.spec.securityContext.runAsNonRoot
設定為 true,以強制執行安全性最佳實務。對於
CREATE
pod 請求,如果未設定容器的欄位.spec.containers[].resources.limits
,則設定預設資源限制。對於
CREATE
pod 請求,如果名稱為foo-sidecar
的容器尚不存在,則注入名稱為foo-sidecar
的 sidecar 容器。
在上述情況下,webhook 可以安全地重新調用,或准入已設定欄位的物件。
非等冪變更性准入 webhook 範例
對於
CREATE
pod 請求,注入名稱為foo-sidecar
的 sidecar 容器,後綴為當前時間戳記(例如foo-sidecar-19700101-000000
)。對於
CREATE
/UPDATE
pod 請求,如果 pod 已設定標籤"env"
,則拒絕,否則將"env": "prod"
標籤新增至 pod。對於
CREATE
pod 請求,盲目地附加名為foo-sidecar
的 sidecar 容器,而不查看 pod 中是否已存在foo-sidecar
容器。
在上述第一種情況下,重新調用 webhook 可能會導致同一個 sidecar 多次注入到 pod 中,每次都使用不同的容器名稱。同樣地,如果使用者提供的 pod 中已存在 sidecar,則 webhook 可以注入重複的容器。
在上述第二種情況下,重新調用 webhook 將導致 webhook 在其自己的輸出上失敗。
在上述第三種情況下,重新調用 webhook 將導致 pod spec 中出現重複的容器,這會使請求無效並被 API 伺服器拒絕。
攔截物件的所有版本
建議准入 webhook 應始終透過將 .webhooks[].matchPolicy
設定為 Equivalent
來攔截物件的所有版本。也建議准入 webhook 應優先註冊資源的穩定版本。未能攔截物件的所有版本可能會導致某些版本的請求未強制執行准入政策。有關範例,請參閱 比對請求:matchPolicy。
可用性
建議准入 webhook 應盡可能快速地評估(通常在毫秒內),因為它們會增加 API 請求延遲。建議為 webhook 使用較小的逾時。有關更多詳細資訊,請參閱 逾時。
建議准入 webhook 應利用某種形式的負載平衡,以提供高可用性和效能優勢。如果 webhook 在叢集中執行,您可以在服務後端執行多個 webhook 後端,以利用該服務支援的負載平衡。
保證看到物件的最終狀態
需要保證看到物件最終狀態才能強制執行策略的許可 Webhook 應使用驗證許可 Webhook,因為物件在被變更 Webhook 查看後可能會被修改。
例如,變更性准入 webhook 設定為在每個 CREATE
pod 請求上注入名稱為 "foo-sidecar" 的 sidecar 容器。如果 sidecar 必須 存在,則還應設定驗證性准入 webhook 以攔截 CREATE
pod 請求,並驗證在要建立的物件中是否存在具有預期設定的名稱為 "foo-sidecar" 的容器。
避免自託管 webhook 中的死鎖
如果 webhook 設定為攔截啟動其自身 pod 所需的資源,則在叢集內執行的 webhook 可能會導致其自身部署的死鎖。
例如,變更性准入 webhook 設定為僅當 pod 中設定了特定標籤(例如 "env": "prod"
)時才准入 CREATE
pod 請求。Webhook 伺服器在未設定 "env"
標籤的部署中執行。當執行 webhook 伺服器 pod 的節點變得不健康時,webhook 部署將嘗試將 pod 重新排程到另一個節點。但是,由於未設定 "env"
標籤,請求將會被現有的 webhook 伺服器拒絕,並且遷移無法發生。
建議使用 namespaceSelector 排除 webhook 執行的命名空間。
副作用
建議准入 webhook 應盡可能避免副作用,這表示 webhook 僅對傳送給它們的 AdmissionReview
的內容進行操作,而不進行帶外變更。如果 webhook 沒有任何副作用,則應將 .webhooks[].sideEffects
欄位設定為 None
。
如果在准入評估期間需要副作用,則在處理 dryRun
設定為 true
的 AdmissionReview
物件時,必須抑制副作用,並且應將 .webhooks[].sideEffects
欄位設定為 NoneOnDryRun
。有關更多詳細資訊,請參閱 副作用。
避免在 kube-system 命名空間上操作
kube-system
命名空間包含 Kubernetes 系統建立的物件,例如控制平面元件的服務帳戶、pod(如 kube-dns
)。意外變更或拒絕 kube-system
命名空間中的請求可能會導致控制平面元件停止運作或引入未知的行為。如果您的准入 webhook 無意修改 Kubernetes 控制平面的行為,請使用 namespaceSelector
將 kube-system
命名空間排除在攔截範圍之外。