准入控制器概述
准入控制器是指当用户请求通过认证和授权之后,对象被持久化之前拦截 apiserver 的请求。
准入控制器可以可以执行”验证”和”变更”操作。
其中,”变更”操作是指可以修改被接收的对象的meta信息。
默认的控制器代码是编译进入 kube-apiserver 二进制文件的,只能由集群管理员配置。
准入控制过程分为两个阶段。
第一阶段,运行”变更”准入控制器。
第二阶段,运行”验证”准入控制器。
Ps: 某些控制器既是变更准入控制器又是验证准入控制器。
如果任何一个阶段的任何控制器拒绝了该请求,则整个请求将立即被拒绝,并向终端用户返回一个错误。
启用/关闭一个准入控制器
kube-apiserver 的 enable-admission-plugins 参数接受一个以逗号分隔的准入控制插件顺序列表。
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger
上述命令中启用了 NamespaceLifecycle 和 LimitRanger 准入控制插件。
同样,disable-admission-plugins 参数也可以将传入的(以逗号分隔的) 准入控制插件列表禁用,即使是默认启用的插件也会被禁用。
kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny
我们可以查询哪些准入控制插件是默认启用的:
kube-apiserver -h | grep enable-admission-plugins
这些准入控制器都是 kubernetes 内置推荐的准入控制器。
内置准入控制器功能概述
自定义准入控制器
除了上述我们提到的 Kubernetes 中内置的准入控制器插件外,Kubernetes 还提供了一种可以自定义开发的准入控制插件,
它是通过在运行时所配置的 webhook 的形式来运行的。
准入 Webhook 是一种用于接收准入请求并对其进行处理的 HTTP 回调机制。
我们可以定义两种类型的准入 webhook:
- “验证”性质的准入 Webhook: ValidatingWebhookConfiguration
- “修改”性质的准入 Webhook: MutatingWebhookConfiguration
修改性质的准入 Webhook 会先被调用。
它们可以更改发送到 API 服务器的对象以执行自定义的设置默认值操作。
在完成了所有对象修改并且 API 服务器也验证了所传入的对象之后,
验证性质的 Webhook 会被调用,并通过拒绝请求的方式来强制实施自定义的策略。
准入 Webhook 实战
准入 Webhook 本质上是集群控制平面的一部分。
因此,对于准入 Webhook 的编写,应该非常是谨慎的编写和部署。
下面,我们来看看如何快速编写一个 Webhook 。
准入 Webhook 生效需要具备如下条件:
- Kubernetes 集群版本至少为 v1.16
- 确保启用 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 控制器。
示例准入 webhook 应用
对于一个准入 webhook 而言,需要包含如下三个步骤:
- 编写一个准入 webhook 应用。
- 部署 webhook 应用。
- 配置对应的 WebhookConfiguration 。
在如下代码中,包含了 admission webhook 服务器 的示例实现。
具体来说,webhook 处理由 apiserver 发送的 AdmissionReview 请求,并且将其决定作为 AdmissionResponse 对象以相同版本发送回去。
下面,我们来一个示例的 ValidatingWebhookConfiguration 的配置:
apiVersion: admissionregistration.k8s.io/v1kind: ValidatingWebhookConfigurationmetadata:name: "pod-policy.example.com"webhooks:- name: "pod-policy.example.com"rules: # webhook 生效范围- apiGroups: [""] # "" 表示核心组,*匹配所有组apiVersions: ["v1"] # * 匹配所有组operations: ["CREATE"] # 支持 CREATE, UPDATE, DELETE, CONNECT, *resources: ["pods"] # "*" 匹配所有资源,但不包括子资源。"*/*" 匹配所有资源,包括子资源。"pods/*" 匹配 pod 的所有子资源。"*/status" 匹配所有 status 子资源。scope: "Namespaced" # 表示 namespace 级别生效, Cluster表示集群有效, * 表示全局生效clientConfig: # Webhook 的地址service:namespace: "example-namespace"name: "example-service"caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate>...tLS0K"admissionReviewVersions: ["v1", "v1beta1"]sideEffects: NonetimeoutSeconds: 5 # 超时时间默认为 10s
当 apiserver 收到与 rules 相匹配的请求时,apiserver 按照 clientConfig 中指定的方式向 webhook 发送一个 admissionReview 请求。
创建 webhook 配置后,系统将花费几秒钟使新配置生效。
那么,apiserver 发送给 webhook 服务器的请求的格式是什么样的呢?一个示例如下:
{"apiVersion": "admission.k8s.io/v1","kind": "AdmissionReview","request": {# 唯一标识此准入回调的随机 uid"uid": "705ab4f5-6393-11e8-b7cc-42010a800002",# 传入完全正确的 group/version/kind 对象"kind": {"group":"autoscaling","version":"v1","kind":"Scale"},# 修改 resource 的完全正确的的 group/version/kind"resource": {"group":"apps","version":"v1","resource":"deployments"},# subResource(如果请求是针对 subResource 的)"subResource": "scale",# 在对 API 服务器的原始请求中,传入对象的标准 group/version/kind# 仅当 webhook 指定 `matchPolicy: Equivalent` 且将对 API 服务器的原始请求转换为 webhook 注册的版本时,这才与 `kind` 不同。"requestKind": {"group":"autoscaling","version":"v1","kind":"Scale"},# 在对 API 服务器的原始请求中正在修改的资源的标准 group/version/kind# 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为 webhook 注册的版本时,这才与 `resource` 不同。"requestResource": {"group":"apps","version":"v1","resource":"deployments"},# subResource(如果请求是针对 subResource 的)# 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为该 webhook 注册的版本时,这才与 `subResource` 不同。"requestSubResource": "scale",# 被修改资源的名称"name": "my-deployment",# 如果资源是属于名字空间(或者是名字空间对象),则这是被修改的资源的名字空间"namespace": "my-namespace",# 操作可以是 CREATE、UPDATE、DELETE 或 CONNECT"operation": "UPDATE","userInfo": {# 向 API 服务器发出请求的经过身份验证的用户的用户名"username": "admin",# 向 API 服务器发出请求的经过身份验证的用户的 UID"uid": "014fbff9a07c",# 向 API 服务器发出请求的经过身份验证的用户的组成员身份"groups": ["system:authenticated","my-admin-group"],# 向 API 服务器发出请求的用户相关的任意附加信息# 该字段由 API 服务器身份验证层填充,并且如果 webhook 执行了任何 SubjectAccessReview 检查,则应将其包括在内。"extra": {"some-key":["some-value1", "some-value2"]}},# object 是被接纳的新对象。# 对于 DELETE 操作,它为 null。"object": {"apiVersion":"autoscaling/v1","kind":"Scale",...},# oldObject 是现有对象。# 对于 CREATE 和 CONNECT 操作,它为 null。"oldObject": {"apiVersion":"autoscaling/v1","kind":"Scale",...},# options 包含要接受的操作的选项,例如 meta.k8s.io/v CreateOptions、UpdateOptions 或 DeleteOptions。# 对于 CONNECT 操作,它为 null。"options": {"apiVersion":"meta.k8s.io/v1","kind":"UpdateOptions",...},# dryRun 表示 API 请求正在以 `dryrun` 模式运行,并且将不会保留。# 带有副作用的 Webhook 应该避免在 dryRun 为 true 时激活这些副作用。# 有关更多详细信息,请参见 http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request"dryRun": false}}
Webhook 使用 HTTP 200 状态码、Content-Type: application/json 和一个包含 AdmissionReview 对象的 JSON 序列化格式来发送响应。
该 AdmissionReview 对象与发送的版本相同,且其中包含的 response 字段已被有效填充。
response 至少必须包含以下字段:
- uid,从发送到 webhook 的 request.uid 中复制而来
- allowed,设置为 true 或 false
一个最简单允许请求的示例如下:
{"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 响应码和返回给用户的消息。示例如下:
{"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"}}}
当允许请求时,mutating准入 Webhook 也可以选择修改传入的对象。
这是通过在响应中使用 patch 和 patchType 字段来完成的。
当前唯一支持的 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="}}
