由于后端服务器和客户端并没有直接的通信,所以客户端的真实 IP 只能通过代理服务器传递给后端服务器。
我们可以在代理服务器上将客户端的 IP 封装到请求报文中发送给后端服务器。
示例:
在代理服务器上配置:
...
location / {
proxy_pass http://webs;
proxy_set_header X-Real-IP $remote_addr; # 将 $remote_addr 的值封装到请求报文头部的 X-Real-IP 字段中
}
...
做了以上配置后,再次用客户端访问代理服务器,在后端服务器上抓包,可以看到请求头部有了 X-Real-IP 字段,它的值正是客户端真实 IP
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 字段中获取。
做完上述调度算法的试验后,我们查看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的变量:
$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
代理服务器一配置
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及所经过的所有代理服务器(最后一个除外)的值组成
在实验二的基础上,修改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即可