我们都知道,在Nginx中,想获取来源的IP,我们可以通过$remote_addr
来获取,但是很多时候,请求会经过多个中间代理或CDN,从而导致我们根本无法准确的获取客户端的真实IP, 为解决这种问题,Nginx提供了realip模块来实现客户端IP的获取,详细信息参见:ngx_http_realip_module
此模块提供了三个指令:set_real_ip_from
, real_ip_header
,real_ip_recursive
,其功能如下:
- set_real_ip_from:指定IP的来源信息,用于排除中间代理IP,可以填写IP或网段。
- real_ip_header:指定从Header的哪个属性里面取IP信息,常用的是
X-Forwarded-For
- real_ip_recursive:是否递归的排除中间代理IP信息,默认值为off,如果值设置为off,则只会排除直接上层代理的IP信息,如果值为on,则会从右至左依次排除用户配置的中间IP,直到遇到第一个非指定的IP,将此IP设置为真实IP。
虽然只有三个指令,但是其功能都是比较晦涩难懂的,下面我通过一个实验,来揭示此模块的功能及用法:
实现环境:
-
访问源IP:10.38.160.252
-
代理一:10.38.165.227(四层代理,非Nginx),其后端指向代理二的三台机器
-
代理二:
- 10.38.160.94
- 10.38.160.101
- 10.38.160.96
-
代理三:10.38.160.174
-
代理四:10.232.51.224
-
目的:10.232.51.224: 8090
连接过程示意图:
Nginx关键配置内容:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-For $remote_addr
-
remote_addr:远程地址,如果未经处理,此值为与Nginx直接连接的上层的IP地址
-
proxy_add_x_forwarded_for:其值为 从上一代理获取到的 X-Forwarded-For 值 追加上 remote_addr 的值,中间使用,分割
能获取到客户端真实IP的条件如下:
- 所有七层代理(代理2-3) 需要配置如下配置,以使其可以完整记录经过各个代理时的真实IP信息
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- 末端代理(代理4)需要配置如下配置,过滤掉中间配置
set_real_ip_from 10.38.160.94; // 过滤代理2
set_real_ip_from 10.38.160.101; // 过滤代理2
set_real_ip_from 10.38.160.96; // 过滤代理2
set_real_ip_from 10.38.160.174; // 过滤代理3
real_ip_header X-Forwarded-For; // 指定从哪个header里面取real ip
real_ip_recursive on; // 是否递归查找
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr; // 通过上面配置,最终Realip会被存储在$remote_addr中
通过如上的配置,我们就可以实现对真实IP的获取,真实IP就存储在 X-Real-IP 中
问题:
- 如果条件1不满足呢?
如果所有都没有配置X-Forwarded-For,那将无法获取到真实IP,只能获取到代理3的IP,如果部分节点设置了,只能获取到第一个设置的节点的上一个节点的IP信息,如从代理3开始设置了,那获取到的 realip 只能获取到代理2的IP信息
- 如果 real_ip_recursive为off或不设置呢?
如果 real_ip_recursive未设置或值不为on,那么即使set_real_ip_from设置了很多,也只会去除上个节点的IP信息,如我在代理4设置了如下信息:
set_real_ip_from 10.38.160.94;
set_real_ip_from 10.38.160.101;
set_real_ip_from 10.38.160.96;
set_real_ip_from 10.38.160.174;
real_ip_header X-Forwarded-For;
real_ip_recursive off;
proxy_set_header X-Real-IP $remote_addr;
那么,获取到的真实IP只会是代理2中的任意一个,即:10.38.160.94、10.38.160.101 或 10.38.160.96,即使set_real_ip_from设置了这三个IP,但是还是不会继续往上递归去除,只会对上个节点起作用
- 如果set_real_ip_from没有设置,或设置不完全呢?
set_real_ip_from的功能是从X-Forwarded-For中去除掉中间层IP信息,如果中间IP设置不全,则会导致某个未设置的中间IP被当成客户端真实IP