Kubernetes v1.34+ 探针与生命周期回调 Host 字段安全审查:识别违规配置并验证修复

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
388
文章
0
粉丝
云原生安全1 11字数 2288阅读7分37秒阅读模式
AI智能摘要
Kubernetes v1.34起,Restricted策略新增探针与生命周期回调Host字段限制,但内置PSA不会拦截,启用restricted的命名空间仍可能创建违规Pod。httpGet.host若指向外部地址,可能形成SSRF路径或绕过网络策略。文章给出盘点运行中Pod与静态清单的方法,并提醒先评估移除host后探测行为是否匹配,再借助准入策略引擎强制修复。你的集群里还有多少这类配置?
— AI 生成的文章内容摘要

从 Kubernetes v1.34 开始,Pod Security Standards 的 Restricted 策略在探针与生命周期回调中新增了针对 Host 字段的限制。该条目要求存活探针、就绪探针、启动探针的 httpGet.host,以及 postStart、preStop 生命周期回调中的 httpGet.host 不得设置。需要立即注意的是,这一策略虽然被写入 Pod Security Standards,但内置的 Pod Security Admission 控制器并不执行它;命名空间上的 pod-security.kubernetes.io/enforce=restricted 标签不会阻止带 host 字段的 Pod 被创建。因此,如果你的集群运行在 v1.34 或更高版本,并且你依赖 Restricted 策略作为安全基线,就需要单独盘点并修复这类配置,同时评估移除 host 字段后探测行为和应用监听地址是否仍然匹配。

Kubernetes 集群中探针与生命周期回调 Host 字段的安全审查示意图

适用条件与受限字段

版本边界

这一限制仅出现在 Kubernetes v1.34 及更高版本的 Pod Security Standards 中。v1.33 及更早版本的 Restricted 策略不包含 Host Probes / Lifecycle Hooks 条目。Kubernetes 官方文档的 Pod Security Standards 页面将它列在 Known list(内置 Pod Security Admission 控制器不支持)之下,这意味着即使命名空间启用了 Restricted 模式,PSA 也不会拦截此类 Pod。你需要使用其他策略引擎(如 ValidatingAdmissionPolicy、Kyverno、OPA Gatekeeper)或自定义准入逻辑来强制该规则,不能把它与 hostNetwork、hostPort 等已被 PSA 支持的策略混为一谈。

具体字段与风险

受影响的字段包括:

  • spec.containers[*].livenessProbe.httpGet.host
  • spec.containers[*].readinessProbe.httpGet.host
  • spec.containers[*].startupProbe.httpGet.host
  • spec.containers[*].lifecycle.postStart.httpGet.host
  • spec.containers[*].lifecycle.preStop.httpGet.host

同样的路径也适用于 initContainers 和 ephemeralContainers。注意,tcpSocket、exec 和 gRPC 探针没有 host 字段,因此不在本次限制范围内。

探针的 httpGet.host 允许指定探测请求的目标主机名或 IP。如果该字段被设置为外部地址,kubelet 的探测请求可能被引导到集群外,形成 SSRF 路径;如果设置为 localhost,则可能绕过 Pod IP 层面的网络策略。生命周期回调中的 httpGet.host 具有类似效果,可能在容器生命周期关键节点触发对外请求。能够创建或修改 Pod 模板的攻击者可以利用这些字段探测内网或触发非预期行为。

准备:盘点工作负载与评估影响

确认集群版本与 PSA 配置

先确认集群版本,判断该限制是否在适用范围内:

kubectl version --short

接着查看哪些命名空间启用了 Pod Security Admission 以及采用的级别:

kubectl get ns -o json | jq -r '
  .items[] |
  select(.metadata.labels["pod-security.kubernetes.io/enforce"] != null) |
  "(.metadata.name) (.metadata.labels["pod-security.kubernetes.io/enforce"])"
'

如果输出中包含 restricted,说明这些命名空间的 Pod 已经受到 Restricted 策略的约束,但如前所述,Host Probes / Lifecycle Hooks 不会由 PSA 自动执行,仍需要手动审查。

扫描运行中的 Pod 与清单

扫描所有运行中 Pod 的探针 host 字段:

kubectl get pods -A -o json | jq -r '
  .items[] as $p |
  (($p.spec.containers // []) + ($p.spec.initContainers // []) + ($p.spec.ephemeralContainers // []))[] as $c |
  ["livenessProbe","readinessProbe","startupProbe"][] as $probe |
  select($c[$probe] != null and $c[$probe].httpGet.host != null) |
  "($p.metadata.namespace)/($p.metadata.name) container=($c.name) ($probe).httpGet.host=($c[$probe].httpGet.host)"
'

扫描生命周期回调中的 host 字段:

kubectl get pods -A -o json | jq -r '
  .items[] as $p |
  (($p.spec.containers // []) + ($p.spec.initContainers // []) + ($p.spec.ephemeralContainers // []))[] as $c |
  ["postStart","preStop"][] as $hook |
  select($c.lifecycle != null and $c.lifecycle[$hook] != null and $c.lifecycle[$hook].httpGet.host != null) |
  "($p.metadata.namespace)/($p.metadata.name) container=($c.name) lifecycle.($hook).httpGet.host=($c.lifecycle[$hook].httpGet.host)"
'

对于静态清单,可以用 conftest、kube-linter 或 Polaris 在 CI 阶段做同样的字段匹配。如果使用 GitOps,先扫描仓库中的 YAML,再决定是否直接修复。

评估移除 Host 后的行为变化

发现违规字段后,不要立即删除。先评估以下问题:

  1. host 当前的值是什么?是 127.0.0.1、localhost,还是外部域名或 IP?
  2. 容器内应用监听在哪个地址?如果只监听 127.0.0.1,移除 host 后 kubelet 使用 Pod IP 探测会失败。
  3. 探针路径的用途是什么?是检查本地应用健康,还是检查外部依赖?
  4. 生命周期回调是否依赖特定主机?例如 preStop 是否通过 httpGet 调用本地管理端口执行优雅下线?

如果 host 用于访问本地管理端点,且应用只绑定回环地址,那么修复方案需要同时调整应用监听地址,或者改用 exec 探针和 exec 生命周期回调。

实施:移除 Host 字段与替代方案

修复示例

修复前:

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
    host: 127.0.0.1
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
    host: 127.0.0.1
lifecycle:
  preStop:
    httpGet:
      path: /shutdown
      port: 8080
      host: 127.0.0.1

修复后,直接删除 host 字段:

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
lifecycle:
  preStop:
    httpGet:
      path: /shutdown
      port: 8080

如果应用只监听 127.0.0.1,可以把监听地址改为 0.0.0.0 或 Pod IP,再重新部署。若无法修改应用,则改用 exec 探针:

livenessProbe:
  exec:
    command:
      - /bin/sh
      - -c
      - "wget -qO- http://127.0.0.1:8080/healthz || exit 1"

exec 探针在 Restricted 策略下通常允许,但会消耗更多资源,并且要求镜像内存在 wget、curl 或类似工具。tcpSocket 探针可以替代端口连通性检查,但不能验证 HTTP 路径。生命周期回调也可以改为 exec,在容器内执行本地命令。

替代方案与变更影响

移除 httpGet.host 后,kubelet 默认使用 Pod IP 发起探测。对于 hostNetwork: true 的 Pod,Pod IP 可能与节点 IP 相同,行为会有所不同;不过 Restricted 策略通常禁止 hostNetwork,所以这类情况较少。真正需要关注的是应用监听地址:如果应用只绑定回环接口,探测会失败,Pod 可能反复重启。

生命周期回调的变更影响类似。如果 preStop 原来通过 httpGet.host 调用本地管理端点,改为 exec 后需要确保命令在容器内可用,并且执行时间不超过 terminationGracePeriodSeconds。建议在低峰期逐工作负载滚动更新,不要一次性批量修改所有 Deployment。

验证:部署前后确认策略结果

部署前检查

Pod Security Admission 不会拦截此限制,因此 kubectl apply --dry-run=server 不会因为 host 字段而报错。部署前验证需要依赖策略引擎。如果集群已经使用 Kyverno 或 OPA Gatekeeper,可以编写一条拒绝规则,匹配所有容器类型的探针和生命周期回调,检查 httpGet.host 是否存在。例如在 CI 中用 conftest 编写 Rego 策略,对渲染后的 YAML 做静态检查,失败则阻断合并。

如果使用 ValidatingAdmissionPolicy,可以定义一条策略,在 Pod 创建和更新时检查这些字段,匹配到非空 host 就拒绝。由于这是 v1.34+ 的新条目,建议先在审计模式下运行一段时间,确认不会误伤合法工作负载,再切换到拒绝模式。

部署后观察

修复后的 Pod 进入 Running 状态后,检查实际生效的 Pod spec:

kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[*].livenessProbe.httpGet.host}'

如果输出为空,说明字段已经移除。接着观察探针事件和重启情况:

kubectl get events -A --field-selector reason=Unhealthy
kubectl get pods -A -o wide | awk '$5 > 0'

如果出现 Liveness probe failed 或 Readiness probe failed,优先检查应用日志和监听地址:

kubectl logs <pod-name> -n <namespace> --tail=100

对于生命周期回调,查看 Pod 事件中是否出现 PreStop hook failed 或 PostStart hook failed。监控层面,可以关注 kubelet 探针失败指标、Pod 就绪状态变化以及容器重启计数。如果修复后探针持续失败,暂停后续批次,回到上一个可用版本,分析监听地址与探测路径是否匹配。

探针与生命周期回调 Host 字段审查修复的闭环流程图

回滚与复盘

回滚步骤

如果修复导致应用不可用,先执行滚动回滚:

kubectl rollout undo deployment/<deployment-name> -n <namespace>

回滚会重新引入带 host 的配置,因此这只是临时措施。在回滚期间,应限制该命名空间的 Pod 创建权限,避免新工作负载继续使用违规字段。更稳妥的做法是同步修复应用监听地址,而不是长期依赖回滚。对于 StatefulSet,回滚需要逐个 Pod 处理,并确认探针恢复后不会触发级联重启。

复盘与长期加固

修复完成后,统计违规工作负载的数量、类型和 host 取值分布。常见原因是应用只监听 127.0.0.1,或者探针被误用于检查外部依赖。把这次检查加入 CI 流水线:在渲染清单阶段用 conftest、kube-linter 或 Polaris 扫描 httpGet.host,在准入阶段用 ValidatingAdmissionPolicy 或 Kyverno 拦截。定期对照 Kubernetes 官方文档的 Pod Security Standards 更新,确认 v1.34+ 的限制是否已被内置 PSA 支持,以及是否有新的受限字段加入。对于平台运维人员,建议将探针和生命周期回调的 Host 字段审查纳入季度安全基线检查,而不是只在版本升级时执行一次。

 
枫少@KillBoy
    • 紫宸郡主
      紫宸郡主 1

      PSA 不拦这个字段,确实容易漏

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证