一、背景–当前问题
1、正常情况,nginx 限制ip访问方式:
# nginx http\server 块中配置
allow 192.168.6.0/16;
# allow all;
deny 1.2.3.4/32;
# deny all;
2、当nginx经过前端 elb(aws负载均衡)、cdn等代理后,来源IP总是elb、cdn等代理 IP地址
当 nginx处于前端负载均衡、cdn等代理后面,来源IP总是代理IP,这样就无法如上对来源IP进行限制访问。
如这里: AWS ELB 后端获取真实客户端IP地址配置,可以获取到来源IP地址,不过只是显示看的,来源IP仍是elb、cdn等IP。
二、 解决方案
既然已经拿到用户真实 IP 地址了,稍加配置,就可以了。
1、在 Nginx 的 http 模块内加入如下配置:
# 获取用户真实IP,并赋值给变量$clientRealIP
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
那么,$clientRealIP
就是用户真实 IP 了,其实就是匹配了 $http_x_forwarded_for
的第一个值,具体原理:
其实,当一个 CDN 或者透明代理服务器把用户的请求转到后面服务器的时候,这个 CDN 服务器会在 Http 的头中加入一个记录
X-Forwarded-For : 用户 IP, 代理服务器 IP
如果中间经历了不止一个代理服务器,这个记录会是这样
X-Forwarded-For : 用户 IP, 代理服务器 1-IP, 代理服务器 2-IP, 代理服务器 3-IP, ….
可以看到经过好多层代理之后, 用户的真实 IP 在第一个位置, 后面会跟一串中间代理服务器的 IP 地址,从这里取到用户真实的 IP 地址,针对这个 IP 地址做限制就可以了。
而且代码中还配合使用了$remote_addr
,因此$clientRealIP
还能兼容上文中第①种直接访问模式,不像 $http_x_forwarded_for
在直接访问模式中将会是空值!
所以,$clientRealIP
还能配置到 Nginx 日志格式中,替代传统的 $remote_addr
使用,推荐!
2、通过对 $clientRealIP
这个变量的判断,实现限制访问
nginx server 块中
#如果真实IP为 121.42.0.18、121.42.0.19,那么返回403
if ($clientRealIp ~* "121.42.0.18|121.42.0.19") {
#如果你的nginx安装了echo模块,还能如下输出语言,狠狠的发泄你的不满(但不兼容返回403,试试200吧)!
#add_header Content-Type text/plain;
#echo "son of a bitch,you mother fucker,go fuck yourself!";
return 403;
break;
}
把这个保存为 deny_ip.conf ,上传到 Nginx 的 conf 文件夹,然后在要生效的网站 server 模块中引入这个配置文件,并 Reload 重载 Nginx 即可生效:
#禁止某些用户访问
include deny_ip.conf;