#作者:曹付江
文章目录
- 1、什么是 Admission Controllers?
- 2、如何创建 Admission Controllers?
- 3、Admission 控制器的最佳实践
K8s 中的操作与安全标准执行机制:

1、什么是 Admission Controllers?
Admission controllers 是一段拦截请求的代码,它会在请求被持久化之前验证或更改(变更)这些请求。Admission controllers 与外部 WebHook 连接,处理入驻请求。因此,它们也被称为 Admission WebHook。该 WebHook 在对象创建、修改或删除最终确定之前接收 K8s 的入驻请求。
我们可以定义两种类型的 Admission controllers:
- MutatingAdmissionWebhook:该控制器既可以验证也可以更改请求。
- ValidatingAdmissionWebhook:该控制器只能验证请求。
Admission Controller 是在需要强制执行特定组织政策时最有效的选项之一,例如: - 确保所有容器都有资源限制,以避免资源占用过多。
- 阻止某些常见的镜像标签。
- 强制执行资源的命名规范。
- 限制仅允许来自受信任注册表的批准容器镜像。
检查内建的 Admission controllers:
2、如何创建 Admission Controllers?
要创建一个 Mutating Admission Webhook,我们需要以下内容:
- 用于 Webhook 的 Web 应用程序
- 运行 Web 应用程序的服务帐户
- 用于托管 Web 应用程序的 Deployment
- 用于将流量路由到 Web 应用程序的 Service
- 一个 ClusterRole,定义 API 级别的访问权限(此博客未详细介绍)
- 一个 ClusterRoleBinding,将服务帐户与 ClusterRole 关联
- 一个 Secret,包含 Web 应用程序用于提供 TLS 的证书。(K8s 会向我们的服务发起 HTTPS 调用,因此需要有效的 SSL/TLS 证书)
WebHook 服务器
在本文章中,我将使用基于 Python 的 Webhook,使用 Sanic 框架。
FROM sanicframework/sanic:LTS
RUN pip install jsonpatch
COPY apps.py apps.py
ENTRYPOINT ["sanic", "apps:app", "--host=0.
上面的 Dockerfile 将创建用于运行 Web 服务器的镜像。这里我们挂载了一个卷(来自 secret),该卷将用于 TLS 证书。
apps.py 的内容:
import base64
import json as nativejson
from copy import deepcopy
from pprint import pformat
import jsonpatch
from sanic import Request, Sanic
from sanic.log import logger
from sanic.response import json
app = Sanic(name=__name__)
@app.post("/validate")
async def validate(request: Request):
allowed = True
message = ""
try:
pass
# add logic to validate th
# update allowed & message accordingly
except KeyError:
pass
return json(
{
"response": {
"allowed": allowed,
"uid": request.json["request"]["uid"],
"status": {"message": message},
}
}
)
@app.post("/mutate")
async def mutate(request: Request):
logger.info(f"Obtained Request \n {pformat(request.json)}")
original_spec = request.json["request"]["object"]
modified_spec = deepcopy(spec)
try:
pass
# add logic to update
# update the modified_spec
except KeyError:
pass
patch = jsonpatch.JsonPatch.from_diff(spec, modified_spec)
return json(
{
"response": {
"allowed": True,
"uid": request.json["request"]["uid"],
"patch": base64.b64encode(nativejson.dumps(patch).encode()).decode(),
"patchtype": "JSONPatch",
}
}
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=443)
Kubernetes 对验证的期望是返回一个简单的响应,值为 False 或 True。因此,在上述代码中,我们返回 allowed 为 True 或 False。在变更 Webhook 的情况下,我们还返回补丁(新旧对象的差异)。
创建 Webhook 的清单:
apiVersion: v1
kind: ServiceAccount
metadata:
name: admission-controller
namespace: admission-webhook-ns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admission-demo-rbac
subjects:
- kind: ServiceAccount
name: admission-controller
namespace: admission-webhook-ns
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Service
metadata:
name: admission-webhook-svc
namespace: admission-webhook-ns
labels:
app: admission-webhook-demo
spec:
ports:
- port: 443
targetPort: 3030
selector:
app: admission-webhook-demo
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: admission-controller-cert
namespace: admission-webhook-ns
spec:
dnsNames:
- admission-webhook-svc
- admission-webhook-svc.admission-webhook-ns
- admission-webhook-svc.admission-webhook-ns.svc
issuerRef:
kind: ClusterIssuer
name: ca-issuer
secretName: admission-controller-secret
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: admission-webhook-deployment
namespace: admission-webhook-ns
labels:
app: admission-webhook-demo
spec:
replicas: 1
selector:
matchLabels:
app: admission-webhook-demo
template:
metadata:
labels:
app: admission-webhook-demo
spec:
serviceAccountName: admission-controller
containers:
- name: admission-webhook-demo
image: asrathore08/admission-controller:latest
imagePullPolicy: Always
resources:
limits:
cpu: 1000m
memory: 512Mi
volumeMounts:
- name: webhook-certs
mountPath: /mnt/certs
readOnly: true
- name: admission-controller-conf
mountPath: /mnt/conf
volumes:
- name: webhook-certs
secret:
secretName: admission-controller-secret
- name: admission-controller-conf
configMap:
name: admission-controller-configmap
在 Kubernetes 中注册 Webhook:
创建一个 ValidatingWebhookConfiguration,指向我们的 webhook 服务器。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: admission-service-delete-pod-validate
namespace: admission-webhook-ns
labels:
app: admission-webhook-demo
annotations:
cert-manager.io/inject-ca-from: admission-webhook-ns/admission-controller-cert
webhooks:
- name: validate.webhook.demo
clientConfig:
service:
name: admission-webhook-svc
namespace: admission-webhook-ns
path: "/validate"
admissionReviewVersions: ["v1"]
sideEffects: None
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1", "v1beta1"]
resources: ["configmaps"]
scope: "Namespaced"
namespaceSelector:
matchLabels:
demo-admission-validation: enabled
failurePolicy: Ignore
注意:这里我使用了 cert-manager CRD 来生成 TLS/SSL 证书。注解 cert-manager.io/inject-ca-from 有助于获取证书。
创建一个 MutatingWebhookConfiguration,指向我们的 webhook 服务器:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: admission-service-configmap-mutate
namespace: admission-webhook-ns
labels:
app: mutating-admission-webhook
annotations:
cert-manager.io/inject-ca-from: admission-webhook-ns/admission-controller-cert
webhooks:
- name: mutate.webhook.demo
clientConfig:
service:
name: admission-webhook-svc
namespace: admission-webhook-ns
path: "/mutate"
admissionReviewVersions: ["v1"]
sideEffects: None
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1", "v1beta1"]
resources: ["configmaps"]
scope: "Namespaced"
namespaceSelector:
matchLabels:
demo-admission-validation: enabled
failurePolicy: Ignore
上面,我们定义了一个 mutating 和一个 validating webhook。我们只启用了这些 webhooks 处理在具有标签 demo-admission-validation 设置为 enabled 的命名空间中创建或更新的对象。此功能允许我们控制 webhook 的作用范围。
- MutatingAdmissionWebhook — 转换器
- ValidatingAdmissionWebhook — 审核员
注意:在上述 webhook 中,我将资源限制为 configmap。K8s 上的 Apache Spark 使用 configmap 来存储 Spark 配置。这种 webhook 可以控制用户为某些配置提供的值,并且可以为某些配置注入特定的默认值。
一旦部署,Webhook 将接收以下请求体的请求。
#
{
"kind": "AdmissionReview",
"request": {
"kind": {
"kind": "Pod",
"version": "v1",
"group": ""
},
"resource": {
"resource": "pods",
"version": "v1",
"group": ""
},
"uid": "b06b6ec2-681d-11e9-a645-06b44ed6a042",
"object": {
"status": {},
"spec": {
"dnsPolicy": "ClusterFirst",
"securityContext": {},
"serviceAccountName": "",
"schedulerName": "default-scheduler",
"serviceAccount": "",
"priority": 0,
"terminationGracePeriodSeconds": 30,
"restartPolicy": "Always",
"containers": [
{
"name": "",
"image": "",
"imagePullPolicy": "Always",
"ports": [
{
"protocol": "TCP",
"containerPort": 80
}
],
"resources": {}
}
]
},
"metadata": {
}
},
"namespace": "",
"userInfo": {
"username": "",
"groups": [
"system:masters",
"system:authenticated"
]
},
"oldObject": null,
"dryRun": false,
"operation": "CREATE"
},
"apiVersion": "admission.k8s.io/v1beta1"
}
控制器将以特定格式返回响应。响应格式如下所述。
# Response
{
"body": {
"kind": "AdmissionReview",
"apiVersion": "admission.k8s.io/v1",
"response": {
"uid": "request.uid",
"allowed": "True",
"patch": "patch_base64",
"patchType": "JSONPatch"
},
"headers": {
"Content-Type": "application/json"
},
"statusCode": 200
}
# patch_base64
[
{
"op": "replace",
"path": "/spec/containers/0/image",
"value": "xxxx.dkr.ecr.us-west-2.amazonaws.com/nginx:latest"
}
]
3、Admission 控制器的最佳实践
- 我们应该确保 webhook 尽可能轻量化。
- 我们应该在开发环境中彻底测试我们的 webhook,以确保它们不会无意中阻止合法的请求。
- 我们应该详细记录 admission 控制决策,并对被拒绝的请求进行故障排除。