【005】Nginx学习笔记-Nginx真实IP
真实IP
- 配置代理服务器将客户端 IP 传递给后端服务器
查看后端服务器的访问日志,我们发现日志中记录的客户端 IP 并非真实客户端的 IP,而是代理服务器的 IP 。
之所以会这样,原因是客户端向代理服务器发起请求后,再由代理服务器向后端服务器发起请求。
站在后端服务器的角度,代理服务器才是直接通信的“客户端”,所以记录的是代理服务器的 IP,显然,记录这个 IP 是没有意义的。
由于后端服务器和客户端并没有直接的通信,所以客户端的真实 IP 只能通过代理服务器传递给后端服务器。
我们可以在代理服务器上将客户端的 IP 封装到请求报文中发送给后端服务器。
示例:
在代理服务器上配置:
...
location / {
proxy_pass http://webs;
proxy_set_header X-Real-IP $remote_addr; # 将 $remote_addr 的值封装到请求报文头部的 X-Real-IP 字段中
}
...
做了以上配置后,再次用客户端访问代理服务器,在后端服务器上抓包,可以看到请求头部有了 X-Real-IP 字段,它的值正是客户端真实 IP
- 配置后端 web 服务器记录下客户端真实 IP
虽然经过以上第1步配置,代理能够将客户端真实 IP 传递过来了,但默认情况下后端服务器并不会在日志中记录请求头部 X-Real-IP 字段的值。
2-1、后端服务器为 apache 产品
对于 apache 产品,只需修改配置文件中的 LogFormat 即可:
LogFormat “%{X-Real-ip}i %l %u %t “%r” %>s %b “%{Referer}i” “%{User-Agent}i”” combined
2-2、后端为 nginx
如果后端为nginx,有两种方法可实现:
A、直接修改 log_format ,将 $remote_addr 改为 $http_x_real_ip 即可
B、安装 real_ip 模块(ngx_http_realip_module),然后修改配置文件:
...
location / {
root html;
index index.html index.htm;
set_real_ip_from 代理服务器IP;
real_ip_header X-Real-IP;
}
...
这样配置后,$remote_addr 的值将会从请求报文中的 X-Real-IP 字段中获取。
客户端真实IP
做完上述调度算法的试验后,我们查看apache的连接日志,发现访问到服务器的ip都是代理服务器的ip,这样我们就无法统计客户端的ip了
我们在nginx代理服务器配置文件做如下操作
重启nginx ,然后再代理服务器用tcpdump抓包,并且访问代理服务器
我们抓到了真机的ipreal-ip 192.168.10.1
但是nginx的连接日志仍然没有记录客户端ip,
怎么解决呢,我们在代理服务器端nginx的配置文件内的log_format
下添加$http_x_real_ip
我们用不同的虚拟机访问代理服务器,然后再服务器端我们就能看见真实的ip了
但是apache的日志还没有显示真实ip,所以我们这样更改配置文件
重启并查看服务器apache的连接日志
深入理解真实IP
几个关于IP的变量:
$remote_addr - 直接与服务器通信的客户端的IP地址
$http_x_real_ip - 从请求报文首部的X-Real-IP字段获取值
$http_x_forwarded_for - 从请求报文首部的X-Forwarded-For字段获取的值
$realip_remote_addr - 最后一个反向代理服务器的IP
$proxy_add_x_forwarded_for - 如果请求报文首部没有 X-Forwarded-For 字段,则此变量值为“$remote_addr”;
如果请求报文首部有 X-Forwarded-For 字段,则此变量值为 “X-Forwarded-For的值, $remote_addr”。
实验一
实验一: 一个代理服务器的情况
代理服务器: 192.168.10.41
web服务器: 192.168.10.42
客户端: 192.168.10.31
web服务器配置(nginx):
location = /test {
echo "remote_addr: $remote_addr";
echo "http_x_forwarded_for: $http_x_forwarded_for";
echo "http_x_real_ip: $http_x_real_ip";
echo "real-ip-remote-addr: $realip_remote_addr";
}
1.代理服务器配置一:
location = /test {
proxy_pass http://192.168.10.42;
}
访问结果:
# curl 192.168.10.41/test
remote_addr: 192.168.10.41
http_x_forwarded_for:
http_x_real_ip:
real-ip-remote-addr: 192.168.10.41
2.代理服务器配置二:
location = /test {
proxy_pass http://192.168.10.42;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
访问结果:
# curl 192.168.10.41/test
remote_addr: 192.168.10.41
http_x_forwarded_for: 192.168.10.31
http_x_real_ip: 192.168.10.31
real-ip-remote-addr: 192.168.10.41
分析: 只有一个代理的情况下$http_x_forwarded_for也记录了客户端的真实IP
实验二: 多个代理服务器的情况
web服务器配置同实验一
代理服务器一配置
location = /test {
proxy_pass http://192.168.10.41;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
代理服务器二配置
location = /test {
$http_x_forwarded_for";
proxy_pass http://192.168.10.42;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
访问结果
# curl 192.168.10.32/test
remote_addr: 192.168.10.41
http_x_forwarded_for: 192.168.10.31, 192.168.10.32
http_x_real_ip: 192.168.10.32 #从这里可以看出X-Real-IP被$remote_addr覆盖了,而$remote_addr是proxy2的上一个proxy的IP
real-ip-remote-addr: 192.168.10.41
分析: 如果有多个代理,
第一个代理上的$http_x_forwarded_for记录了客户端的真实IP(X-Forwarded-For字段)
后面代理上记录的是X-Forwarded-For的值及上一个代理的IP,使用","隔开.
$http_x_forwarded_for的值由客户端IP及所经过的所有代理服务器(最后一个除外)的值组成
实验三:利用realip模块获取客户端真实IP
在实验二的基础上,修改web服务器的配置:
location = /test {
set_real_ip_from 192.168.10.0/24; #定义可发送真实IP的地址,可以是一个具体地址,也可以是CIDR地址
real_ip_header X-Forwarded-For; #指定真实IP从哪个请求头中获取
real_ip_recursive on; #是否递归解析,当其值为off时,将把real_ip_header指定请求头中的最后一个IP作为真实IP
echo "remote_addr: $remote_addr";
echo "http_x_forwarded_for: $http_x_forwarded_for";
echo "http_x_real_ip: $http_x_real_ip";
echo "real-ip-remote-addr: $realip_remote_addr";
}
访问结果
# curl 192.168.10.32/test
remote_addr: 192.168.10.31
http_x_forwarded_for: 192.168.10.31, 192.168.10.32
http_x_real_ip: 192.168.10.32
real-ip-remote-addr: 192.168.10.41
分析: 使用realip模块后,nginx可以通过$remote_addr获取到客户端真实IP
实验四:伪装请求头
代理服务器一配置
location = /test {
proxy_pass http://192.168.10.41;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
代理服务器二配置
location = /test {
proxy_pass http://192.168.10.42;
proxy_set_header X-Real-IP $http_x_real_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
web服务器配置:
location = /test {
set_real_ip_from 192.168.10.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
echo “remote_addr: $remote_addr”;
echo “http_x_forwarded_for: $http_x_forwarded_for”;
echo “http_x_real_ip: $http_x_real_ip”;
echo “real-ip-remote-addr: $realip_remote_addr”;
}
访问结果
# curl -H "X-Real-IP: 10.10.10.11" 192.168.10.32/test
remote_addr: 192.168.10.31
http_x_forwarded_for: 192.168.10.31, 192.168.10.32
http_x_real_ip: 192.168.10.31
real-ip-remote-addr: 192.168.10.41
# curl -H "X-Forwarded-For: 10.10.10.10" -H "X-Real-IP: 10.10.10.11" 192.168.10.32/test
remote_addr: 10.10.10.10
http_x_forwarded_for: 10.10.10.10, 192.168.10.31, 192.168.10.32
http_x_real_ip: 192.168.10.31
real-ip-remote-addr: 192.168.10.41
分析:
当我们改变了x-real-ip的配置后,发现即使一开始将X-Real-IP进行伪装,客户端的真实IP仍然能够正确的传递进来;
而如果一开始对X-Forwarded-For进行伪装,我们获取到的真实IP就不正确了. 为了避免这种情况,我们可以在第一个代理上修改设置,将proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;修改为proxy_set_header X-Forwarded-For $remote_addr;即可
多个代理服务器(nginx)的情况下,想要获取真实IP:
1).通过X-Forwarded-For字段获取:
第一个代理服务器: proxy_set_header X-Forwarded-For $remote_addr;
后面的代理服务器: proxy_set_header X-Forwarded-For KaTeX parse error: Double subscript at position 12: proxy_add_x_̲forwarded_for; …remote_addr即可
2).通过X-Real-IP字段获取:
第一个代理服务器: proxy_set_header X-Real-IP $remote_addr;
后面的代理服务器: proxy_set_header X-Real-IP $http_x_real_ip;
最终服务器修改日志格式中的变量为 $http_x_real_ip即可
Reference
- 课堂笔记