从 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 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.hostspec.containers[*].readinessProbe.httpGet.hostspec.containers[*].startupProbe.httpGet.hostspec.containers[*].lifecycle.postStart.httpGet.hostspec.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 后的行为变化
发现违规字段后,不要立即删除。先评估以下问题:
host当前的值是什么?是127.0.0.1、localhost,还是外部域名或 IP?- 容器内应用监听在哪个地址?如果只监听
127.0.0.1,移除host后 kubelet 使用 Pod IP 探测会失败。 - 探针路径的用途是什么?是检查本地应用健康,还是检查外部依赖?
- 生命周期回调是否依赖特定主机?例如
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 就绪状态变化以及容器重启计数。如果修复后探针持续失败,暂停后续批次,回到上一个可用版本,分析监听地址与探测路径是否匹配。

回滚与复盘
回滚步骤
如果修复导致应用不可用,先执行滚动回滚:
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 字段审查纳入季度安全基线检查,而不是只在版本升级时执行一次。
- it2021
- it2021.com
- Kubernetes
- test
- 基线检查





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