WEBKT

Kubernetes准入控制器:防患于未然的Pod部署安全卫士

70 0 0 0

背景:生产环境Pod配置错误的困扰

最近,我们团队的DevOps工程师们频繁遇到生产环境Pod因配置错误导致的问题,例如:

  • 镜像拉取失败
  • 特权模式运行导致的安全告警

这些问题往往在Pod已经部署后才被发现,修复过程既被动又耗时,严重影响了生产环境的稳定性和安全性。为了改变这种被动局面,我们开始探索如何在Pod创建前就拦截这些不合规的部署请求。

解决方案:Kubernetes Admission Controller

Kubernetes Admission Controller 正是解决这类问题的利器。它是一个准入控制策略,在Pod等资源对象被创建、更新或删除之前,对请求进行拦截和验证。如果请求不符合预定义的策略,Admission Controller可以拒绝该请求,从而防止不合规的资源部署到集群中。

Admission Controller 的类型:

Kubernetes 提供了两种类型的 Admission Controller:

  • Mutating Admission Controller(变更准入控制器): 可以修改请求对象。例如,自动为Pod添加标签或注入sidecar容器。
  • Validating Admission Controller(验证准入控制器): 仅验证请求对象,不进行修改。例如,验证Pod是否符合安全策略或资源限制。

工作流程:

  1. 用户提交Pod创建请求。
  2. API Server 接收到请求。
  3. API Server 按照配置顺序调用 Admission Controller。
  4. Admission Controller 对请求进行验证或修改。
  5. 如果所有 Admission Controller 都通过,则请求被允许,Pod 被创建。否则,请求被拒绝。

实践:使用Validating Admission Webhook拦截特权模式Pod

下面,我们以一个实际案例来演示如何使用 Validating Admission Webhook 拦截特权模式运行的Pod。

1. 创建一个Webhook服务:

首先,我们需要创建一个Webhook服务,该服务接收来自 Kubernetes API Server 的 Admission Review 请求,并根据策略进行验证。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: privileged-pod-webhook
spec:
  selector:
    matchLabels:
      app: privileged-pod-webhook
  template:
    metadata:
      labels:
        app: privileged-pod-webhook
    spec:
      containers:
      - name: privileged-pod-webhook
        image: your-image:latest # 替换成你的webhook镜像
        ports:
        - containerPort: 443

Webhook服务需要实现一个 HTTPS 端点,用于接收和处理 Admission Review 请求。

2. 创建 Kubernetes Secret:

Webhook 服务需要使用 TLS 证书来保证通信安全。将证书和私钥存储在 Kubernetes Secret 中。

kubectl create secret tls webhook-tls --cert=tls.crt --key=tls.key -n your-namespace # 替换成你的namespace

3. 创建 ValidatingWebhookConfiguration:

接下来,我们需要创建一个 ValidatingWebhookConfiguration 对象,告诉 Kubernetes API Server 如何调用我们的 Webhook 服务。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: privileged-pod-webhook-configuration
webhooks:
  - name: privileged-pod.example.com
    rules:
      - apiGroups:   [""]
        apiVersions: ["v1"]
        operations:  ["CREATE"]
        resources:   ["pods"]
    clientConfig:
      service:
        name: privileged-pod-webhook
        namespace: your-namespace # 替换成你的namespace
        path: /validate
      caBundle: $(base64 -w 0 tls.crt) # 替换成你的CA证书
    admissionReviewVersions: ["v1", "v1beta1"]
    sideEffects: None
    timeoutSeconds: 5

关键配置说明:

  • rules: 定义了 Webhook 拦截的资源类型和操作。
  • clientConfig: 定义了 Webhook 服务的地址和证书信息。
  • sideEffects: None: 声明 Webhook 没有副作用,可以安全地缓存结果。

4. Webhook服务代码示例(Go):

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"

    admissionv1 "k8s.io/api/admission/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func validatePod(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    req := ar.Request
    var pod corev1.Pod
    if err := json.Unmarshal(req.Object.Raw, &pod); err != nil {
        log.Printf("Could not unmarshal raw object: %v", err)
        return &admissionv1.AdmissionResponse{
            Result: &metav1.Status{
                Message: err.Error(),
                Code:    http.StatusBadRequest,
            },
        }
    }

    allowed := true
    message := ""

    for _, container := range pod.Spec.Containers {
        if container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
            allowed = false
            message = fmt.Sprintf("Container %s is running in privileged mode, which is not allowed.", container.Name)
            break
        }
    }

    return &admissionv1.AdmissionResponse{
        Allowed: allowed,
        Result: &metav1.Status{
            Message: message,
        },
    }
}

func admissionHandler(w http.ResponseWriter, r *http.Request) {
    var body []byte
    if r.Body != nil {
        if data, err := ioutil.ReadAll(r.Body); err == nil {
            body = data
        }
    }

    if len(body) == 0 {
        log.Println("empty body")
        http.Error(w, "empty body", http.StatusBadRequest)
        return
    }

    contentType := r.Header.Get("Content-Type")
    if contentType != "application/json" {
        log.Printf("Content-Type=%s, expect application/json", contentType)
        http.Error(w, "invalid Content-Type, expect `application/json`", http.StatusBadRequest)
        return
    }

    var admissionReview admissionv1.AdmissionReview
    if err := json.Unmarshal(body, &admissionReview); err != nil {
        log.Printf("Could not unmarshal admission review: %v", err)
        http.Error(w, fmt.Sprintf("could not unmarshal admission review: %v", err), http.StatusBadRequest)
        return
    }

    response := validatePod(&admissionReview)

    resp, err := json.Marshal(admissionv1.AdmissionReview{
        Response: response,
        TypeMeta: admissionReview.TypeMeta,
    })
    if err != nil {
        log.Printf("Could not marshal admission review response: %v", err)
        http.Error(w, fmt.Sprintf("could not marshal admission review response: %v", err), http.StatusInternalServerError)
        return
    }

    log.Printf("Sending response: %v", response)
    w.Header().Set("Content-Type", "application/json")
    if _, err := w.Write(resp); err != nil {
        log.Printf("Could not write response: %v", err)
        http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError)
    }
}

func main() {
    http.HandleFunc("/validate", admissionHandler)
    log.Println("Server listening on :443")
    err := http.ListenAndServeTLS(":443", "/etc/tls/tls.crt", "/etc/tls/tls.key", nil) // 替换成你的证书路径
    if err != nil {
        log.Fatal("ListenAndServeTLS: ", err)
    }
}

5. 测试:

尝试创建一个特权模式的Pod:

apiVersion: v1
kind: Pod
metadata:
  name: privileged-pod
spec:
  containers:
  - name: privileged-container
    image: busybox
    securityContext:
      privileged: true
    command: ["sleep", "3600"]

你会发现该Pod创建请求被拒绝,并收到来自 Webhook 服务的错误信息。

总结

通过 Kubernetes Admission Controller,我们可以有效地预防不合规的Pod部署到生产环境,从而提高集群的安全性和稳定性。 除了拦截特权模式Pod,Admission Controller 还可以用于实现更复杂的策略,例如:

  • 强制资源限制
  • 验证镜像来源
  • 自动注入Sidecar容器

希望本文能够帮助你理解和使用 Kubernetes Admission Controller,构建更加安全可靠的云原生应用。

云原生架构师 KubernetesDevOps

评论点评