iptables限制ip访问_(转)nginx代理后,获取真实IP,做并发访问限制的方法(限流)

站点在运行时,为了防止DDoS 攻击、或内部接口调用造成的数据迸发,nginx提供了limit限流模块:

HttpLimitZoneModule 限制同时并发访问的数量HttpLimitReqModule 限制访问数据,每秒内最多几个请求

一、普通配置:

什么叫普通配置?

普通配置就是针对【用户浏览器】→【网站服务器】这种常规模式的 nginx 配置。那么,如果我要对单 IP 做访问限制,绝大多数教程都是这样写的:

## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址最多有 50 个并发连接

## 你想开 几千个连接 刷死我? 超过 50 个连接,直接返回 503 错误给你,根本不处理你的请求了

limit_conn_zone $binary_remote_addr zone=TotalConnLimitZone:10m ;limit_conn  TotalConnLimitZone  50;limit_conn_log_level notice;## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址每秒处理 10 个请求## 你想用程序每秒几百次的刷我,没戏,再快了就不处理了,直接返回 503 错误给你limit_req_zone $binary_remote_addr zone=ConnLimitZone:10m  rate=10r/s;limit_req_log_level notice;

## 具体服务器配置

server {    listen   80;    location ~ .php$ {                ## 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了        limit_req zone=ConnLimitZone burst=5 nodelay;        fastcgi_pass   127.0.0.1:9000;        fastcgi_index  index.php;        include fastcgi_params;    }   }

这样一个最简单的服务器安全限制访问就完成了,这个基本上你 Google 一搜索能搜索到 90% 的网站都是这个例子,而且还强调用“$binary_remote_addr”可以节省内存之类的云云。

二、CDN 或 SLB 代理之后

为了增加安全、性能,许多站点都用到了CDN加速,或者其他的二级代理,例如阿里的SLB负载均衡等等。

于是,网站的访问模式就变为:用户浏览器 → CDN 节点 / SLB 节点→ 网站源服务器

甚至是更复杂的模式:用户浏览器 → CDN/SLB 节点(CDN 入口、CCDDoS 攻击流量清洗等) → 阿里云盾 → 源服务器

可以看到,我们的网站中间经历了好几层的透明加速和安全过滤, 这种情况下,我们就不能用上面的“普通配置”。因为普通配置中基于【源 IP 的限制】的结果就是,我们把【CDN /SLB节点】或者【阿里云盾】给限制了,因为这里“源 IP”地址不再是真实用户的 IP,而是中间 CDN /SLB节点的 IP 地址。

我们需要限制的是最前面的真实用户,而不是中间为我们做加速的加速服务器。

其实,当一个 CDN /SLB或者透明代理服务器把用户的请求转到后面服务器的时候,这个 CDN /SLB服务器会在 Http 的头中加入一个记录

X-Forwarded-For : 用户 IP, 代理服务器 IP

如果中间经历了不止一个代理服务器,这个记录会是这样

X-Forwarded-For : 用户 IP, 代理服务器 1-IP, 代理服务器 2-IP, 代理服务器 3-IP, ….

可以看到经过好多层代理之后, 用户的真实 IP 在第一个位置, 后面会跟一串中间代理服务器的 IP 地址,从这里取到用户真实的 IP 地址,针对这个 IP 地址做限制就可以了。

那么针对 CDN /SLB模式下的访问限制配置就应该这样写:

http{    ## 这里取得原始用户的IP地址,没走CDN/SLB的,给到$remote_addr    map $http_x_forwarded_for  $clientRealIp {        default $remote_addr;        ~^(?P[0-9.]+),?.*$$firstAddr;    }    #设置IP白名单,对内部的IP不设限    map $clientRealIp $limit{        default $clientRealIp;        xx.xx.xx.xx "";    }    #以真实IP为单位,限制请求数,并返回429状态;    limit_req_status 429;    limit_req_zone $limit zone=ConnLimitZone:20m rate=5r/s;    limit_req_log_level notice;    #以真实IP为单位,限制该IP的并发连接数,并返回429状态;    limit_conn_status 429;    limit_conn_zone $limit zone=TotalConnLimitZone:20m ;    limit_conn  TotalConnLimitZone 100;    limit_conn_log_level notice;    #以访问域名为单位,限制总并发链接数;    limit_conn_zone $server_name zone=SumConnLimitZone:20m;}
## 具体Server:如下在监听php/go/java部分新增限制规则即可,或直接放在域名下面,限制全部访问server {    listen   80;    location ~ .php$ {        #限制总并发连接数        limit_conn SumConnLimitZone 10000;        #最多5个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 429 错误给你了        limit_req  zone=ConnLimitZone  burst=5  nodelay;        fastcgi_pass   127.0.0.1:9000;        fastcgi_index  index.php;        include fastcgi_params;    }   }

三、如何验证

根据以上配置,我们知道nginx,每秒最多允许通过10+5个请求,在压测时,就会有两种情况:

在白名单内(到白名单的服务器测试),压测该站点,应该全部通过

不在白名单内,最多只允许通过10 +5 个请求,余下部分应该返回429

前提:压测总次数超过 10 +5,否则看不出效果。

centos一般都自带有 siege压测工具,还比较好用:yum -y install siege

使用方法:siege -c 3 -r 10 -b https://xxxx.xx.com/api/xxxx-c 3 表示3个用户-r 10 表示访问10次以上表示:3个用户,每个用户访问10次请求,共计30次

经测试,每增加一台nginx,相同的配置应该是 x 2,例如:nginx1 的配置是 10+5,nginx2的配置也是10+5,域名部署在这两台nginx上,请求数最大允许为:20+10

以上测试,如果不能通过,应该是配置问题,那么我们用echo模块来调试下:

四、echo 模块作者原文提到了 nginx 的一个 echo 模块,特意玩了下感觉挺有意思的,下面贴一下简单集成步骤。

①、给 nginx 集成 echo 模块

cd /usr/local/src#下载echo模块并解压:wget https://github.com/openresty/echo-nginx-module/archive/v0.61.tar.gztar zxvf v0.61.tar.gz#下载nginx并解压wget http://nginx.org/download/nginx-1.12.2.tar.gztar -xzvf nginx-1.12.2.tar.gzcd nginx-1.12.2/#查看在用nginx的编译参数(如果是全新安装则省略)/usr/local/nginx/sbin/nginx -Vnginx version: nginx/1.12.2built by gcc 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) #以下这行即为旧的编译参数:configure arguments: --user=www --group=www --prefix=/usr/local/nginx --with-http_gzip_static_module#在旧的编译参数基础上新增【--add-module=/echo模块的解压路径】参数,开始编译./configure --prefix=/usr/local/nginx/nginx  --add-module=/usr/local/src/echo-nginx-module-0.61#make编译make -j2#平滑升级nginx (如果是全新安装请执行:make install)mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.oldcp -f objs/nginx /usr/local/nginx/sbin/make upgrade

以上升级、编译和添加第三方模块不熟悉的朋友,可以参考我另外一篇博客:https://mp.csdn.net/mdeditor/81136273

②、echo 用法举例:其实就和 shell 的 echo 差不多,能否输出自定义信息。

比如,在 nginx 里面配置如下:

location /hello {  echo "hello, world!";}

访问 http://xxx.com/hello 就会在浏览器里面输出 hello, world! 了(如果域名开了 CDN 可能会报 404)。

又比如,测试本文提到的真实用户的 IP,只要在本文第二步配置基础上,加上如下规则并 reload 即可:

server {    listen   80;        server_name  yourdomain.com;        ## 以下是新增规则:        ## 当用户访问 /ip 的时候,我们输出 $clientRealIp 和 $limit变量,看看这个变量        ## 值是不是真的 用户源IP 地址        location /ip{                echo $clientRealIp;                echo $limit;        }}

认真看的朋友,会问 clientRealIp 和 limit 有什么区别:clientRealIp 如果走 SLB/CDN,获取的就是真实IP,反之,获取的就是remote_addr

limit 是在clientRealIp的基础上,排除了“IP白名单”,也就是说,当你的源IP,是白名单时,你的limit,应该为“空”,这样就不受限流了

所以,我们可以以此来判断,IP白名单是否有效:curl http://xxx.xxx.cn/ip

d75d5bdf5e99136df95eea6b6bafdc78.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值