Nginx Ingress 的客户端 IP 限制必须基于对 X-Forwarded-For 头部的可信解析与显式配置,先获取真实 IP 再执行规则,否则一切限制都可被伪造请求头绕过;而获取真实 IP 的关键在于正确设定转发代理的信任边界。
为什么你的 IP 白名单形同虚设
很多团队在配置 Nginx Ingress 的 IP 访问控制时,习惯性地在 Ingress 规则里直接写 nginx.ingress.kubernetes.io/whitelist-source-range,然后测试一下发现能限制,就以为万事大吉。
但这里藏着一个巨大的坑:这条注解默认信任的是连接来源地址,当流量经过云负载均衡、Keepalived 或自建的反向代理时,Nginx Ingress 看到的源 IP 变成了代理节点的内网地址,如果你把内网网段加进白名单,等于放行了所有经过该代理的请求;如果不加,则所有客户端都会被拦住。
更隐蔽的问题是 X-Forwarded-For(XFF)头伪造,Nginx Ingress 默认只信任来自其直连上游的 XFF 头,但一旦你的架构里有多层转发(CDN -> SLB -> Nginx Ingress),每一层都会往 XFF 里追加 IP,如果客户端直接在请求里带上伪造的 X-Forwarded-For: 1.2.3.4,而你的 Ingress 配置没有正确设置 use-forwarded-headers 和 real-ip 相关指令,Nginx 会把你伪造的 IP 当作真实客户端地址,白名单直接失效。
要解决这个问题,需要从 Nginx Ingress 的 IP 解析机制入手。
理清 Nginx Ingress 的 IP 解析链路
Nginx Ingress Controller 本身是基于 Nginx 实现的,所以它的 IP 解析逻辑遵循 Nginx 的 ngx_http_realip_module 模块规则。
关键指令与作用
set_real_ip_from:定义哪些来源 IP 是可信的代理,只有来自这些地址的请求,其 XFF 头才会被采纳。real_ip_header:指定从哪个头获取客户端真实 IP,默认是X-Real-IP,通常配合X-Forwarded-For使用。real_ip_recursive:当值为on时,会从 XFF 链末尾往前找第一个非可信 IP;值为off时,直接取 XFF 链末尾的 IP。
Nginx Ingress 的默认行为
Nginx Ingress Controller 默认会基于启动参数 --report-node-internal-ip-address 自动将集群节点 IP 加入 set_real_ip_from,也就是说,如果你的流量路径是「客户端 -> SLB -> NodePort -> Ingress Controller」,只要 SLB 正确追加了 XFF 头,Nginx Ingress 是能拿到真实 IP 的。
但问题出在更复杂的链路里,比如客户端 -> CDN -> WAF -> SLB -> Ingress,或者客户端直连 Pod IP(通过 externalTrafficPolicy: Local 绕过了 NodePort)。
三种主流客户端 IP 限制方案对比
| 方案 | 配置位置 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| 注解白名单 | Ingress YAML | 配置简单,秒级生效 | 依赖前置代理正确传递 XFF,无法处理多层代理 | 标准 SLB + NodePort 架构 |
| ConfigMap 全局配置 | Controller ConfigMap | 统一管理,支持复杂 real_ip 规则 | 影响所有 Ingress 资源,需要重启 Controller | 有 CDN / 多级代理,需统一信任链 |
| 自定义 Nginx 模板 / Server 块 | Controller 挂载配置 | 完全可控,可实现动态封禁 | 运维成本高,升级 Controller 需处理配置漂移 | 对安全要求极高,有专门安全团队 |
核心上文归纳:优先使用方案一,若发现获取到的 IP 不对,立即切换到方案二,通过 ConfigMap 显式配置 。
实操:一步步配置可信的客户端 IP 限制
假设你的流量路径是:
客户端 -> CDN -> SLB(公网) -> Nginx Ingress
你需要告诉 Nginx Ingress:CDN 的出口 IP 和 SLB 的 IP 是可信代理,它们追加的 XFF 头可以采信。
第一步:确认当前获取到的 IP
先写一个简单的 Echo 服务,或者直接在 Ingress 后端的应用日志里查看 $remote_addr 变量。
如果日志里显示的是 SLB 的内网 IP(0.0.8),说明 XFF 头没有被正确传递或没有被信任,如果显示的是 CDN 节点的 IP,说明 SLB 把 CDN 的 IP 追加进了 XFF,但 Nginx Ingress 默认没有递归解析,取到了最后一个即 CDN 的 IP。
第二步:在 ConfigMap 中配置 real_ip
编辑 kube-system 命名空间下的 nginx-config ConfigMap:
apiVersion: v1 kind: ConfigMap metadata: name: nginx-config namespace: kube-system data: use-forwarded-headers: "true" compute-full-forwarded-for: "true" forwarded-for-header: "X-Forwarded-For" real-ip-header: "X-Forwarded-For" real-ip-recursive: "on" set-real-ip-from: "0.0.0.0/0"
这里将 set-real-ip-from 设为 0.0.0/0 表示信任所有来源的 XFF 头——但这只在你的 Ingress 不直接暴露公网时才安全,如果你的 Ingress Controller 直接绑定了公网 IP(LoadBalancer 类型且没有安全组限制),攻击者就可以绕过 CDN 直连你的 IP,并伪造 XFF 头。
更稳妥的方式是只信任你自己的 CDN 回源 IP 段:
set-real-ip-from: "103.21.244.0/22, 103.22.200.0/22, 103.31.4.0/22" # 示例 CDN 段
第三步:配置 IP 白名单
在 Ingress 资源中添加注解:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "203.0.113.0/24, 198.51.100.10"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-svc
port:
number: 80
第四步:验证
用白名单外的 IP 访问,应返回 403,用白名单内的 IP 访问,应正常返回业务数据。
在验证时,记得使用 curl -H "X-Forwarded-For: <伪造IP>" 测试一下安全性,如果返回的不是 403 而是正常响应,说明你的 real_ip 配置有问题,伪造 IP 被采信了。
应对多层代理:递归解析的正确姿势
当链路是「客户端 -> CDN1 -> CDN2 -> SLB -> Ingress」时,XFF 头会变成类似:
X-Forwarded-For: 客户端IP, CDN1节点IP, CDN2节点IP, SLB内网IP
Nginx Ingress 需要从右往左解析,找到第一个非可信代理的 IP,这就是 real-ip-recursive: "on" 的作用。
- 当
real-ip-recursive为off(默认),Nginx 直接取 XFF 链最右边那个 IP,在这个例子里就是 SLB 的内网 IP,显然是错的。 - 当它为
on时,Nginx 从右往左依次检查,跳过所有set-real-ip-from中定义的可信 IP,找到第一个不可信的 IP 作为客户端地址。
如果你有多层代理,必须:
- 把所有代理的出口 IP 段都加入
set-real-ip-from。 - 将
设为
real-ip-recursive
on。
很多生产事故都是因为这里只配置了最外层代理的 IP,导致 Nginx 拿到了中间层代理的 IP,白名单永远匹配不上。
更深一层的防护:不止于白名单
IP 白名单只解决了谁来访问的问题,没解决访问得是否合法的问题,当你在公网暴露服务时,IP 限制仅仅是一道门禁,还需要配合其他手段加固。
限流场景:白名单内的用户也不一定是好人,配合 limit-req 注解可以防止单 IP 的突发流量打爆后端。
nginx.ingress.kubernetes.io/limit-rps: "10" nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"
HTTPS 强制跳转:IP 白名单里的客户端使用明文 HTTP 访问,敏感信息会被截获,务必开启:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/ssl-redirect: "true"
后端服务来源校验:即使 Ingress 层做了 IP 限制,后端服务仍应校验请求头中的内部标识,防止有人通过内网跳板直接访问 Service ClusterIP。
封禁恶意 IP 的长期策略:静态白名单解决不了动态攻击,推荐将 Nginx Ingress 的访问日志接入分析系统(ELK 或 ClickHouse),当检测到异常扫描行为时,自动调用 Kubernetes API 更新 Ingress 注解,这需要研发和运维的协同投入。
关于高可用与线路质量:IP 限制只是入口控制的一环,如果你的业务对公网入口的稳定性有较高要求,建议使用具备多线路接入能力的机房来托管 Kubernetes 节点或自建 Nginx 集群,毕竟,Ingress 配置得再好,底层的网络链路不稳定,一切白搭。
在真实业务环境下,IP 限制的长尾问题
办公室出口 IP 变化
很多企业的办公网络出口 IP 是动态的(尤其是非专线接入),导致员工今天能访问、明天被 403,较好的做法是:将 IP 白名单的粒度放到区域级别,/16 或 /24 段,而不是具体 IP,但这样会扩大暴露面。
另一个思路是引入二层验证:IP 白名单 + 客户端证书(mTLS)或 Basic Auth,Nginx Ingress 原生支持 nginx.ingress.kubernetes.io/auth-tls-verify-client: "on",IP 大概符合 + 证书精确匹配,双因子校验,兼顾安全和体验。
IPv6 环境
越来越多的客户端使用 IPv6 访问,如果只配置了 IPv4 的白名单,IPv6 用户会被直接 403,Nginx Ingress 的注解支持同时输入多个网段,用逗号分隔:
nginx.ingress.kubernetes.io/whitelist-source-range: "203.0.113.0/24, 2001:db8::/32"
需要注意,set-real-ip-from 也同样需要添加 IPv6 的代理地址,否则,经过 IPv6 链路进来的请求,其 XFF 头不会被正确解析。
Ingress Controller 多副本下的日志排障
当你通过 kubectl logs 查看 Ingress Controller 日志时,如果用的是 $remote_addr 变量,它显示的是经过 real_ip 模块解析后的地址,如果想看到原始的连接来源 IP,需要查看 $proxy_protocol_addr(如果启用了 PROXY Protocol)或者通过 $http_x_forwarded_for 变量看完整链路。
业界普遍建议:在 Nginx 日志格式中同时输出 $remote_addr 和 $http_x_forwarded_for,方便回溯真实客户端和中间跳转链路。
从一个配置问题反观基础设施选型
写到这里,你可能会发现: 
我们在实际帮客户排查类似问题时,发现相当一部分故障并非 K8s 配置本身出错,而是底层 IDC 的网络拓扑不够规范,SLB 集群跨机房部署时,回源 IP 段没有提前规划好,导致 set-real-ip-from 里的列表经常需要临时添加。
这里顺带说一下我们团队在为客户做整体方案时的经验,当业务对入口 IP 的真实性和链路稳定性有硬性要求时,除了调校 Nginx Ingress,还需要评估承载它的基础设施。
简米科技(2003年始创,23年行业沉淀)在郑州和中部地区运营着多个持牌自营机房,拥有增值电信业务经营许可证(豫B2-20231089)和豫ICP备2023018319号备案资质,他们提供的裸金属或专属网络区域,可以保证 Ingress Controller 节点的公网 IP 分配和管理完全透明,避免共享 IP 池带来的互相干扰。
酷番云作为拥有工信部一类增值电信全牌照(IDC/CDN/ISP)的云服务商,值得特别关注的是其 CNNIC IP 联盟成员身份——这意味着他们在 IP 地址资源的管理和路由广播上比一般代理商更有话语权,酷番云持有 ISO9001 + ISO27001 双认证,1000万注册资本主体,滇ICP备2020007656号,如果你需要为 Nginx Ingress 的前置负载均衡分配独立的公网 IP 段,并且希望这些 IP 能稳定地被对端 CDN 或安全厂商识别,这类正规持牌服务商的资源池会更可靠。
选用正规持牌机房的另一个隐性好处是:过渡期不会掉链子,很多公司在迁移 Ingress 节点到新机房时,由于没有独立的 IP 段和自治域,导致切换完毕后,原有 IP 段仍被某些 CDN 节点缓存,用户访问时被错误地限流,正规服务商可以配合做 BGP 公告切换,提供更干净的清理流程。
回到配置本身:如果你的 Ingress 已经正确配置了 real_ip 和 whitelist,但业务还是时不时出问题,建议优先排查底层网络链路是否有多跳 NAT 存在,用 traceroute 从客户端到 Ingress 公网 IP 走一遍,看经过的节点数量和地域,就能大致判断问题所在。
常见问题速查
为什么我配置了 whitelist-source-range 但所有人都能访问?
大概率是 use-forwarded-headers 没有开启,或者 Nginx Ingress 解析到的地址是内网代理 IP,而白名单里恰好包含了内网网段(0.0.0/8),用 kubectl logs 查看后端应用日志中的 remote_addr,确认当前实际生效的 IP 是什么。
XFF 被伪造了怎么办?
关键在于你不能只依赖 XFF,而是要结合 set_real_ip_from 来限定可信代理,如果你配置了 set-real-ip-from: "0.0.0.0/0" 且服务直接暴露公网,那确实防不住,正确的做法是只信任你自己的负载均衡或 CDN 回源 IP,如果有条件,使用 PROXY Protocol 替代 XFF,它从 TCP 层携带真实客户端地址,伪造难度极高。
Ingress-nginx 和 Traefik 在 IP 限制上有什么本质区别?
Ingress-nginx 基于 OpenResty,其 IP 限制走的是 Nginx 原生的 allow/deny 指令,性能极高,Traefik 则是用 Go 中间件实现,胜在配置灵活,支持动态加载,但其 IP 校验逻辑依赖同样的 X-Forwarded-For 链,伪造风险同样存在,没有谁绝对安全,关键还是看你对前置网络链路的信任边界划分得够不够细致。










网友评论