Kubernetes 准入控制基础:Admission Webhook 原理与配置

在 Kubernetes 集群中,准入控制(Admission Control)是 API 请求到达 etcd 持久化之前的最后一道内置防线。对于多集群门禁场景,原生内置的准入插件(如 PodSecurity、ResourceQuota)无法满足自定义策略的灵活性和动态性,因此 Admission Webhook 成为扩展准入逻辑的核心机制。本章将深入剖析 Webhook 的工作原理、两种类型(Mutating vs Validating)的差异,并给出可直接复用的配置示例。

2.1 Admission Webhook 工作原理

Admission Webhook 本质上是一个由集群外部 HTTP 回调服务(通常部署在集群内)处理的准入插件。当用户通过 kubectl、API 或控制器发起资源变更请求时,Kubernetes API Server 会按照以下流程执行准入控制:

  1. 认证与授权:验证请求者身份和权限。
  2. Mutating Admission:依次执行所有 Mutating Webhook,允许修改请求对象(如注入 Sidecar、设置默认值)。
  3. 对象验证:对修改后的对象进行 Schema 校验。
  4. Validating Admission:依次执行所有 Validating Webhook,检查对象是否符合业务策略(如禁止特权容器、检查镜像来源)。
  5. 持久化:通过所有准入检查后,对象写入 etcd。
关键时序点:Mutating Webhook 在对象 Schema 校验之前执行,因此可以修改请求内容;Validating Webhook 在 Schema 校验之后执行,只能读取最终对象并决定接受或拒绝。
Admission Webhook 请求处理流程 Admission Webhook 请求处理流程 用户/控制器 API Server Mutating Webhook Validating Webhook etcd 持久化 拒绝请求(HTTP 4xx)

2.2 Mutating 与 Validating Webhook 区别

两种 Webhook 在职责、执行顺序和副作用控制上有本质区别。下表从多个维度进行对比:

对比维度 Mutating Webhook Validating Webhook
执行顺序 先执行(在 Schema 校验前) 后执行(在 Schema 校验后)
核心能力 修改请求对象(注入、默认值、补丁) 只读检查,拒绝不符合策略的请求
副作用风险 高:多个 Mutating Webhook 可能冲突 低:不修改对象,仅返回 allow/deny
典型场景 Sidecar 注入、资源标签标准化、默认资源限制 安全策略检查、镜像来源白名单、合规审计
失败策略 failurePolicy: Fail 或 Ignore 通常设为 Fail 防止绕过策略
超时控制 默认 30 秒,建议 ≤10 秒 默认 30 秒,建议 ≤5 秒
最佳实践:在多集群门禁场景中,Validating Webhook 更适合覆盖高风险变更(如删除 Namespace、修改集群角色绑定),因为其只读特性不会引入副作用;Mutating Webhook 则用于自动修复不合规配置(如为未设置资源限制的 Pod 注入默认值)。

2.3 Webhook 配置示例

以下是一个完整的 Validating Webhook 配置示例,用于拒绝创建特权容器(SecurityContext.Privileged=true)。该配置可直接应用于多集群门禁场景,作为高风险变更的拦截层。

2.3.1 部署 Webhook 服务(Deployment + Service)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: policy-webhook
  namespace: kube-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app: policy-webhook
  template:
    metadata:
      labels:
        app: policy-webhook
    spec:
      containers:
      - name: webhook
        image: registry.example.com/policy-webhook:v1.0.0
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: tls-certs
          mountPath: /etc/webhook/certs
          readOnly: true
      volumes:
      - name: tls-certs
        secret:
          secretName: webhook-tls
---
apiVersion: v1
kind: Service
metadata:
  name: policy-webhook
  namespace: kube-system
spec:
  ports:
  - port: 443
    targetPort: 8443
  selector:
    app: policy-webhook

2.3.2 创建 ValidatingWebhookConfiguration

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: deny-privileged-pods
webhooks:
- name: deny-privileged.policy.example.com
  clientConfig:
    service:
      name: policy-webhook
      namespace: kube-system
      path: /validate
    caBundle: <base64-encoded-ca-cert>
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]
    scope: "Namespaced"
  failurePolicy: Fail
  matchPolicy: Equivalent
  timeoutSeconds: 5
  sideEffects: None
  admissionReviewVersions: ["v1"]
  objectSelector:
    matchExpressions:
    - key: security-tier
      operator: NotIn
      values:
      - system-critical
关键配置说明:
  • failurePolicy: Fail:当 Webhook 服务不可用时,拒绝所有请求,防止策略绕过。
  • timeoutSeconds: 5:超时时间设为 5 秒,避免 API Server 等待过久。
  • sideEffects: None:声明 Webhook 无副作用,允许 API Server 在 dry-run 模式下调用。
  • objectSelector:通过标签选择器排除系统关键命名空间,避免误拦截。

2.3.3 Webhook 服务端核心逻辑(伪代码)

// 处理 AdmissionReview 请求
func handleValidate(w http.ResponseWriter, r *http.Request) {
    var review admissionv1.AdmissionReview
    json.NewDecoder(r.Body).Decode(&review)

    pod := &corev1.Pod{}
    json.Unmarshal(review.Request.Object.Raw, pod)

    allowed := true
    message := ""

    // 检查特权容器
    for _, container := range pod.Spec.Containers {
        if container.SecurityContext != nil &&
           container.SecurityContext.Privileged != nil &&
           *container.SecurityContext.Privileged {
            allowed = false
            message = "Privileged containers are not allowed"
            break
        }
    }

    review.Response = &admissionv1.AdmissionResponse{
        UID:     review.Request.UID,
        Allowed: allowed,
        Result:  &metav1.Status{Message: message},
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(review)
}
Webhook 配置与请求交互架构 Webhook 配置与请求交互架构 API Server 加载 Webhook 配置 ValidatingWebhook Configuration Webhook 服务 Deployment + Service 交互流程说明 1. API Server 启动时读取 ValidatingWebhookConfiguration → 2. 收到 Pod 创建请求 → 3. 构造 AdmissionReview 并 POST 到 Webhook 服务 → 4. 服务返回 Allowed=true/false

2.4 多集群门禁场景下的 Webhook 设计要点

  • 集中式 Webhook 服务:在管理集群部署 Webhook 服务,通过 ValidatingWebhookConfiguration 的 namespaceSelector 或 objectSelector 控制生效范围,避免每个集群重复部署。
  • 高可用与容灾:Webhook 服务至少 2 副本,配置 failurePolicy: Fail 时需确保服务 SLA ≥ 99.9%,否则建议使用 Ignore 并配合审计日志。
  • TLS 证书管理:使用 cert-manager 自动签发证书,CA 证书注入到 ValidatingWebhookConfiguration 的 caBundle 字段。
  • 性能优化:Webhook 处理逻辑应轻量,避免调用外部数据库;设置合理的 timeoutSeconds(建议 5-10 秒),并启用 matchPolicy: Equivalent 减少重复调用。
多集群门禁核心原则:Validating Webhook 应作为“断路器”拦截高风险变更(如删除集群角色、修改网络策略),而 Mutating Webhook 作为“自动修复器”处理低风险合规问题。两者配合使用,形成分层防御体系。