3. Kubernetes Pod 安全上下文配置验证

Kubernetes Pod 安全上下文配置验证 学习卡片

为什么 Pod 安全上下文是攻击面入口

Pod 的 securityContext 决定了容器进程以什么身份、拥有哪些内核能力、能否访问宿主机命名空间。以下三类配置一旦开启,容器逃逸与横向移动的门槛会显著降低:

  • privileged: true:容器获得几乎等同宿主机的设备与能力访问权,可直接挂载宿主机磁盘、加载内核模块。
  • hostNetwork: true:Pod 共享宿主机网络命名空间,可监听宿主机端口、嗅探本机流量、访问 127.0.0.1 上的管理接口。
  • hostPID: true:Pod 共享宿主机 PID 命名空间,可看到并可能向宿主机进程发送信号、读取其 /proc 信息。

本案例的任务是:先部署一个同时包含上述不安全配置的 Pod,用 kubectl 确认它确实被调度运行;再部署 OPA Gatekeeper 约束,验证同样的 Pod 被拒绝,并记录违规详情。

验证环境与前置条件

假设集群已具备以下条件(示例环境,非实测结果):

  • Kubernetes 集群可正常 kubectl apply,当前上下文具备创建 Pod 与 CRD 的权限。
  • 已安装 OPA Gatekeeper(gatekeeper-system 命名空间下 gatekeeper-controller-manager 处于 Running)。
  • 测试命名空间 sec-test 已创建:kubectl create ns sec-test。

若尚未安装 Gatekeeper,可用官方清单安装(版本以实际拉取为准):

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml
kubectl -n gatekeeper-system get pods -w

第一步:部署不安全 Pod 并确认其运行状态

下面的清单同时开启特权模式、hostNetwork、hostPID,并挂载宿主机根目录,用于暴露最典型的危险组合:

cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: insecure-pod
  namespace: sec-test
  labels:
    app: insecure-demo
spec:
  hostNetwork: true
  hostPID: true
  containers:
  - name: attacker
    image: busybox:1.36
    command: ["sleep", "3600"]
    securityContext:
      privileged: true
      runAsUser: 0
      allowPrivilegeEscalation: true
    volumeMounts:
    - name: host-root
      mountPath: /host
  volumes:
  - name: host-root
    hostPath:
      path: /
EOF

确认调度与运行状态,并读取实际生效的安全上下文字段:

kubectl -n sec-test get pod insecure-pod -o wide
kubectl -n sec-test get pod insecure-pod \
  -o jsonpath='{.spec.hostNetwork}{" "}{.spec.hostPID}{" "}{.spec.containers[0].securityContext.privileged}{"\n"}'
# 预期输出:true true true

进入容器验证宿主机可见性(仅用于授权测试环境):

kubectl -n sec-test exec insecure-pod -- ls /host/etc | head
kubectl -n sec-test exec insecure-pod -- ps -ef | head

若能看到宿主机 /etc 内容与宿主机进程列表,说明该 Pod 已具备高危能力,这正是后续策略需要拦截的对象。

第二步:用 Gatekeeper 约束拦截不安全配置

Gatekeeper 的拦截由两部分组成:ConstraintTemplate 定义可复用的 Rego 规则,Constraint 实例化规则并指定作用范围。下面模板同时检查 privileged、hostNetwork、hostPID 与 hostPath 挂载:

cat <<'EOF' | kubectl apply -f -
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sblockunsafepod
spec:
  crd:
    spec:
      names:
        kind: K8sBlockUnsafePod
  targets:
  - target: admission.k8s.gatekeeper.sh
    rego: |
      package k8sblockunsafepod

      violation[{"msg": msg}] {
        c := input.review.object.spec.containers[_]
        c.securityContext.privileged == true
        msg := sprintf("container %v uses privileged mode", [c.name])
      }

      violation[{"msg": msg}] {
        input.review.object.spec.hostNetwork == true
        msg := "pod uses hostNetwork"
      }

      violation[{"msg": msg}] {
        input.review.object.spec.hostPID == true
        msg := "pod uses hostPID"
      }

      violation[{"msg": msg}] {
        v := input.review.object.spec.volumes[_]
        v.hostPath
        msg := sprintf("volume %v mounts hostPath %v", [v.name, v.hostPath.path])
      }
EOF

等待 CRD 建立后创建约束实例,仅作用于 sec-test 命名空间,避免影响系统组件:

cat <<'EOF' | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockUnsafePod
metadata:
  name: block-unsafe-pod
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups: [""]
      kinds: ["Pod"]
    namespaces: ["sec-test"]
EOF

kubectl get constrainttemplate k8sblockunsafepod
kubectl get k8sblockunsafepod block-unsafe-pod

第三步:验证拦截效果并记录违规详情

先删除已存在的 Pod,再重新提交同一清单,观察是否被拒绝:

kubectl -n sec-test delete pod insecure-pod --ignore-not-found
kubectl apply -f insecure-pod.yaml

预期返回类似错误(具体措辞随 Gatekeeper 版本略有差异):

Error from server (Forbidden): error when creating "insecure-pod.yaml":
admission webhook "validation.gatekeeper.sh" denied the request:
[block-unsafe-pod] container attacker uses privileged mode;
[block-unsafe-pod] pod uses hostNetwork;
[block-unsafe-pod] pod uses hostPID;
[block-unsafe-pod] volume host-root mounts hostPath /

逐条记录违规详情,便于形成审计证据:

kubectl get k8sblockunsafepod block-unsafe-pod -o yaml | \
  grep -A 20 "status:"

若需确认策略是否覆盖其他命名空间,可临时把 namespaces 改为 ["*"] 后重放测试,但生产环境应谨慎扩大范围。

Pod 安全上下文验证:部署 → 拦截 → 违规记录 insecure-pod.yaml hostNetwork: true hostPID: true privileged: true runAsUser: 0 hostPath: / allowPrivilegeEscalation kube-apiserver 准入链 MutatingAdmissionWebhook ValidatingAdmissionWebhook Gatekeeper Controller Gatekeeper 策略 ConstraintTemplate k8sblockunsafepod Constraint block-unsafe-pod enforcementAction: deny kubectl apply 调用 Rego 路径 A:策略生效 → 拒绝 Forbidden: denied the request privileged mode hostNetwork / hostPID hostPath / 路径 B:策略未覆盖 → 运行 Pod Running hostNetwork=true hostPID=true privileged=true 验证要点:同一清单在策略前后应产生“运行 → 拒绝”的状态翻转,并逐条记录违规原因

结果判读与常见问题

观察项策略生效前策略生效后
kubectl get podRunning不存在(创建被拒)
hostNetwork/hostPIDtrue / true请求未进入 etcd
privilegedtrue请求未进入 etcd
错误信息无列出全部违规条目
  • 若 Pod 仍能创建,先确认 Constraint 的 match.namespaces 是否包含 sec-test,以及 kubectl get constrainttemplate 是否已建立。
  • 若错误信息为空,检查 Gatekeeper 控制器日志:kubectl -n gatekeeper-system logs deploy/gatekeeper-controller-manager。
  • 若只想告警不阻断,可把 enforcementAction 改为 warn 或 dryrun,再观察审计结果。
验证完成后,建议删除测试 Pod 与约束实例,避免影响后续实验:kubectl -n sec-test delete pod insecure-pod --ignore-not-found、kubectl delete k8sblockunsafepod block-unsafe-pod。