根本原因是 kube-proxy 默认开启 SNAT,将用户真实 IP 替换为节点内网 IP;需配置 externalTrafficPolicy: Local 的 Service 并启用 Ingress Controller 的 use-forwarded-headers,再在应用中按信任链安全解析 X-Real-IP 或 X-Forwarded-For。

为什么 Flask/Django 拿到的总是节点 IP?
根本原因是 kube-proxy 默认开启 SNAT:所有进来的流量,源 IP 都被替换成转发节点的内网 IP(比如 10.233.70.42),Pod 里看到的 request.remote_addr 或 request.environ["REMOTE_ADDR"] 就是这个节点地址,不是用户真实 IP。
常见错误现象包括:
- 日志里所有请求都显示同一个内网 IP(如
192.168.94.97) - 白名单校验失效,因为
X-Forwarded-For为空或被覆盖 - 应用代码读取
request.headers.get("X-Real-IP")返回None
必须配 externalTrafficPolicy: Local 的 Service 场景
仅适用于 NodePort 或 LoadBalancer 类型 Service,且你控制着访问入口(比如直连节点 IP + NodePort)。
配置要点:
立即学习“Python免费学习笔记(深入)”;
- 在 Service YAML 中显式设置
externalTrafficPolicy: Local - 确保 Pod 确实运行在接收请求的节点上(否则请求直接被丢弃)
- 配合
podAntiAffinity防止单点堆积,例如用topologyKey: kubernetes.io/hostname
不配这个字段,哪怕 Ingress Controller 正确透传了头,kube-proxy 仍会在转发前做 SNAT —— 头没用,IP 已失真。
Ingress Controller 必须启用 use-forwarded-headers
以 Nginx Ingress Controller 为例,use-forwarded-headers: "true" 不是可选开关,而是透传前提。它让控制器信任上游(如云 LB、WAF)塞进来的 X-Forwarded-For,并正确拼接链路。
关键配置项(写在 Ingress Controller 的 ConfigMap 或 Helm values 中):
-
use-forwarded-headers: "true"(必须) -
compute-full-forwarded-for: "true"(推荐,避免只取第一个 IP) -
forwarded-for-header: "X-Forwarded-For"(默认值,不建议改)
如果用的是 Traefik 或 ALB Ingress Controller,对应配置是 forwardedHeaders.trustedIPs 或 alb.ingress.kubernetes.io/enable-x-forwarded-for,原理一致:不显式声明可信来源,头会被忽略。
Python 应用里怎么安全读取真实 IP?
别只依赖单个 header;也别无脑信 X-Forwarded-For —— 它可能被客户端伪造。正确做法是结合信任链和代理跳数。
Flask 示例(需提前知道你的 Ingress 前有几层可信代理,比如 1 层):
from flask import request
<p>def get_client_ip():</p><h1>优先取 X-Real-IP(由 Ingress Controller 直接设为 $remote_addr)</h1><pre class="brush:php;toolbar:false;">ip = request.headers.get("X-Real-IP")
if ip and ip != "127.0.0.1":
return ip
# fallback:取 X-Forwarded-For 最左非私有 IP(跳过 1 层不可信代理)
xff = request.headers.get("X-Forwarded-For", "")
if xff:
for ip in xff.split(","):
ip = ip.strip()
if not (ip.startswith("10.") or ip.startswith("172.16.") or ip.startswith("192.168.")):
return ip
return request.remote_addr
注意:request.remote_addr 在 externalTrafficPolicy: Local 下才可能是真实 IP;否则它只是节点 IP —— 这就是为什么不能跳过 Service 层配置直接写应用逻辑。


















