在nginx前有CDN时获取用户真实IP
通常在源机前有反代时我们使用nginx的realip模块来获取客户的IP; realip支持多个set_real_ip_from,但只支持一个real_ip_header; 但当在复杂网络环境,有多个不同的反代时,realip就不够用了; 比如在我的项目历史变迁中,源机对外有双线公网IP,内部有2个内网网段,有百度和阿里的CDN/WAF; 百度使用http_x_forwarded_for来传递真实ip,阿里使用自定的header头传递;
- 支持http_x_forwarded_for取IP
- 支持自定义header取IP
- 支持阿里云、百度云加速等cdn或waf获取IP
- 支持IPV6地址
- 支持IP黑白名单
- Gitee(国内): https://gitee.com/c1g/nginx_realip_plus
- Github: https://github.com/andychu46/nginx_realip_plus
- Blog: https://blog.c1gstudio.com/
nginx中的几个变量: remote_addr 代表客户端的IP,但它的值是你服务器网卡接入端发送方的值. X-Forwarded-For(简称XFF), 是一个 HTTP 扩展头部。它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。 XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。(注意:如果未经严格处理,可以被伪造) 如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,那么按照 XFF 标准,服务端最终会收到以下信息
X-Forwarded-For: clientip, proxy1ip, proxy2ip
这里没有proxy3ip,proxy3ip为当前接入端的remote_addr。
比如 PHP 可以直接使用
$_SERVER['REMOTE_ADDR']
获取到proxy3ip,也可以获取到X_FORWARDED_FOR
$_SERVER['HTTP_X_FORWARDED_FOR']
但HTTP_X_FORWARDED_FOR是可以伪造的,我们伪造一个‘112.1.1.1’发给ip所在地网站进行测试。
curl -s https://api.vore.top/api/IPdata?ip= -H 'X-Forwarded-For: 112.1.1.1,222.1.1.1'
{
"code": 200,
"msg": "SUCCESS",
"ipinfo": {
"type": "ipv4",
"text": "112.1.1.1",
"cnip": true
},
"ipdata": {
"info1": "江苏省",
"info2": "淮安市",
"info3": "",
"isp": "移动"
},
"adcode": {
"o": "江苏省淮安市 - 移动",
"p": "江苏",
"c": "淮安",
"n": "江苏-淮安",
"r": "江苏-淮安",
"a": "320800",
"i": true
},
"tips": "接口由VORE-API(https:\/\/api.vore.top\/)免费提供",
"time": 1725340014
}
它原样输出了我们伪造的XFF。 如果你想使用HTTP_X_FORWARDED_FOR取客户ip,那么你需要确定remote_addr的IP是认可的proxy地址。
百度cdn通过HTTP_X_FORWARDED_FOR传输真实ip
各文件放置在nginx.conf所在目录,通常是/opt/nginx/conf/
geo.cdn_ip.conf geo.cdn_proxy.conf
geo.allow.conf;
127.0.0.1/32 1; #host
192.168.0.0/24 1; #lan1
geo.abuse.conf
117.149.203.13 1;
ngxipcofig.conf
nginx.conf
http
{
# 其他配置 ...
#如果header头有下划线,需打开nderscores_in_headers
#underscores_in_headers on;
#ignore_invalid_headers off;
#请改成你认可的合法proxyip
set_real_ip_from 192.168.250.158/31;
# 使用"c1g-clientip"头获取真实IP,需要同步修改ngxinpconfig.conf中配制默认为“X-Real-IP”
real_ip_header c1g-clientip;
# 可选:如果有多个代理,可以使用此指令指定“X-Forwarded-For”中应该使用哪个IP地址
# real_ip_recursive on;
#载入realip
include ngxipcofig.conf;
#日志文件$clientRealIp 为真实客户ip
log_format access '$clientRealIp - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';
upstream www_server {
server 192.168.0.80:80;
}
# 测试主机 http://localhost/showip
server
{
listen 80;
server_name localhost;
location /showip {
default_type text/plain;
echo show realip infomation;
echo_duplicate 20 "---";
echo;
echo time_local\t\t\t\t: $time_local;
echo 原始ip(remote_addr)\t\t\t: $remote_addr;
echo realip(realip_remote_addr)\t\t: $realip_remote_addr;
echo 原始端口(REMOTE_PORT)\t\t\t: $remote_port;
echo realip端口(realip_REMOTE_PORT)\t\t: $realip_remote_port;
echo 下发真实ip及端口(proxyrealip)\t\t: $proxyrealip;
echo forwarded原始(http_x_forwarded_for)\t: $http_x_forwarded_for;
echo forwarded增加ip(proxypass_forwarded_for): $proxypass_forwarded_for;
echo forwarded中第一个(ForwardedFirstIp)\t: $forwardedfirstip;
echo forwarded中最后一个(ForwardedLastIp)\t: $forwardedlastip;
echo ip来源首次标记(clientipfrom)\t\t: $clientipfrom;
echo ip来源二次标记(clientipfrominner)\t: $clientipfrominner;
echo 客户真实ip(clientRealIpTmp)\t\t: $clientRealIpTmp;
echo 最终真实ip(clientRealIp)\t\t: $clientRealIp;
echo 是否通过负载均衡(is_F5_ip)\t\t: $is_f5_ip;
echo 是否来自cdn(clientipfromf5iscdn)\t: $clientipfromf5iscdn;
echo ip来源最终标记(clientipselector)\t: $clientipselector;
echo 白名单ip(is_white_ip)\t\t\t: $white_ip;
echo F5最后ip(F5LastIp)\t\t\t: $f5LastIp;
echo x_real_ip原始(http_x_real_ip)\t\t: $http_x_real_ip;
echo x_real_ip中取出realip(httpxrealip)\t: $httpxrealip;
echo 自定义header头(http_c1g_clientip)\t: $http_c1g_clientip;
}
#向上游传递真实ip
location /
{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxyrealip;
proxy_set_header X-Client-IP $proxycheckip;
proxy_set_header X-Client-Selector $clientipselector;
proxy_set_header X-Forwarded-For $proxypass_forwarded_for;
proxy_pass http://www_server;
}
}
}
nginx.conf
http
{
# 其他配置 ...
# 内网ip
set_real_ip_from 192.168.0.0/24;
real_ip_header X-Real-IP;
#载入ip配置
include ngxipcofig.conf;
}
/opt/nginx/sbin/nginx -t /opt/nginx/sbin/nginx -s reload
或者使用命令测试 curl -s http://192.168.244.8/showip -H 'Host:localhost' -H 'X-Forwarded-For: 223.5.5.3,223.5.5.2' -H 'c1g-clientip: 211.1.1.1'
show realip infomation
------------------------------------------------------------
time_local : 03/Sep/2024:17:56:12 +0800
原始ip(remote_addr) : 211.1.1.1
realip(realip_remote_addr) : 192.168.244.2
原始端口(REMOTE_PORT) :
realip端口(realip_REMOTE_PORT) : 52922
下发真实ip及端口(proxyrealip) : 211.1.1.1:
forwarded原始(http_x_forwarded_for) : 223.5.5.3,223.5.5.2
forwarded增加ip(proxypass_forwarded_for): 223.5.5.3,223.5.5.2, 211.1.1.1
forwarded中第一个(ForwardedFirstIp) : 223.5.5.3
forwarded中最后一个(ForwardedLastIp) : 223.5.5.2
ip来源首次标记(clientipfrom) : 0
ip来源二次标记(clientipfrominner) :
客户真实ip(clientRealIpTmp) : 211.1.1.1
最终真实ip(clientRealIp) : 211.1.1.1
是否通过负载均衡(is_F5_ip) : 0
是否来自cdn(clientipfromf5iscdn) : 0
ip来源最终标记(clientipselector) : 0
白名单ip(is_white_ip) : 0
F5最后ip(F5LastIp) : 211.1.1.1
x_real_ip原始(http_x_real_ip) :
x_real_ip中取出realip(httpxrealip) : 211.1.1.1
自定义header头(http_c1g_clientip) : 211.1.1.1