Kubernetes中Pod获取客户端真实IP失败的根本原因是SNAT和代理头未透传:Service默认启用SNAT使remote_addr变为节点IP,而Ingress等代理未设置X-Forwarded-For头,且Hyperf的TrustProxies中间件未正确配置可信代理网段。

Hyperf 服务在 Kubernetes 容器中拿到的 $request->getServerParams()['remote_addr'] 是节点 IP 或 Service VIP,不是客户端真实 IP —— 这不是代码写错了,而是请求链路里默认没透传、没解析、没信任。
为什么 remote_addr 总是节点 IP 或 10.0.0.x?
根本原因在于:Kubernetes 的 Service(尤其是 NodePort 或 LoadBalancer)默认开启 SNAT,流量到达 Pod 时源 IP 已被 kube-proxy 替换为节点 IP;即使绕过 Service 直连 Pod,前面还有 Ingress/Nginx/HAProxy 等反向代理,它们不主动注入 X-Forwarded-For,Hyperf 就永远看不到原始 IP。
常见错误现象:
- 日志里所有访问都显示为
10.244.0.1或某个固定节点 IP -
TrustProxies中间件启用后仍返回节点 IP,说明代理头未生效或网段未匹配 - 用
curl -H "X-Forwarded-For: 1.2.3.4" http://pod-ip:9501/手动测试能拿到1.2.3.4,但真实流量不行 → 证明是前端代理没加头
TrustProxies 中间件必须配对三件事
Hyperf 的 TrustProxies 不是“开了就灵”,它只做两件事:解析 X-Forwarded-For 头、校验该头来源是否可信。漏掉任一环都会失效。
- 前端反向代理(如 Nginx Ingress、Traefik、自建 Nginx)必须设置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;和proxy_set_header X-Real-IP $remote_addr; - Hyperf 配置里
trusted_proxies必须填对真实代理的 CIDR,不能写0.0.0.0/0(不安全),也不能只写127.0.0.1(K8s 里代理不在本地) - 确认你的 Pod 所在节点的 CNI 网段(如 Flannel 默认
10.244.0.0/16)、Ingress Controller Pod IP 段(kubectl get pods -n ingress-nginx -o wide查看)、以及宿主机 Docker 网桥(如172.17.0.0/16)——这些都要加入trusted_proxies
示例配置(config/autoload/middlewares.php):
return [
Hyperf\HttpServer\Middleware\TrustProxies::class => [
'trusted_proxies' => ['10.244.0.0/16', '172.17.0.0/16', '192.168.83.0/24'],
'headers' => \Hyperf\HttpServer\Router\Router::HEADER_X_FORWARDED_FOR,
],
];
为什么改了 externalTrafficPolicy: Local 还是不行?
这个设置只对 NodePort 或 LoadBalancer 类型 Service 生效,且仅保证「流量不经过 SNAT」,但它不解决代理头缺失问题。如果你的流量路径是:Client → Cloudflare → Nginx Ingress → Hyperf Pod,那 externalTrafficPolicy 根本不参与其中 —— 它只管 Service 到 Pod 这一段。
- Cloudflare 场景:必须在 Nginx 配置里加
set_real_ip_from声明 CF 可信 IP 段(官方列表要定期更新),否则$remote_addr就是 CF 节点地址,X-Forwarded-For也白搭 - Ingress Controller 自身运行在 Pod 里,它的 IP 是集群内网 IP,所以
trusted_proxies必须包含其所在网段(比如10.244.1.100所在子网) - Local 模式会丢弃发往无 Pod 节点的流量,如果副本数
验证和调试最有效的三步
别猜,用命令直击链路各环节:
- 进 Hyperf Pod 执行:
curl -v http://localhost:9501/health,看响应头有没有X-Real-IP或日志是否打印正确 IP - 查 Ingress Controller 日志:
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller | grep "your-service-name",确认它是否成功添加了X-Forwarded-For - 临时在 Hyperf 控制器里 dump:
var_dump($request->getHeader('x-forwarded-for'), $request->getServerParams()['remote_addr']);,区分是头没来,还是来了但没被信任
最容易被忽略的是:Ingress Controller 升级后默认行为可能变更(比如 Traefik v2.10+ 对 forwardedHeaders 默认关闭),或者 Helm Chart 模板覆盖了你手动加的 proxy header 配置 —— 这类隐性覆盖,比代码逻辑更难排查。


















