首页 > 系统相关 >Nginx在CDN加速之后,获取用户真实IP做并发访问限制的方法

Nginx在CDN加速之后,获取用户真实IP做并发访问限制的方法

时间:2023-01-09 12:22:31浏览次数:55  
标签:Nginx ## IP CDN 用户 nginx limit

最近一直在帮一个购买了张戈博客付费服务的朋友做网站防护,为了简单抵挡一下竞争对手的 DDoS 攻击,他给网站开启了Incapsula的免费 CDN 服务。

开启 CDN 之后,我之前给他写的 Shell 防护脚本也就宣告无效了,因为不管是正常访问还是攻击访问,脚本拿到的 IP 都是 CDN 节点的,而我不可能把 CDN 的节点 IP 也给禁用了,那就都不能访问了。

为了解决这个窘迫,我想到了以前看过的 Nginx 访问限制。通过查资料,让我拜读了一枚大神的神作,感觉收获颇丰!于是转过来整理一下,分享给更多需要的人!


Nginx 有 2 个模块用于控制访问“数量”和“速度”,简单的说,控制你最多同时有 多少个访问,并且控制你每秒钟最多访问多少次, 你的同时并发访问不能太多,也不能太快,不然就“杀无赦”。

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 之后

目前国内已经争相出现了百度云加速、加速乐、360 网站卫士以及安全宝等免费 CDN。让我们这些小网站也能免费享受以前高大上的 CDN 加速服务。

于是,网站的访问模式就变为:

用户浏览器 → CDN 节点 → 网站源服务器

甚至是更复杂的模式:

用户浏览器 → CDN 节点(CDN 入口、CC\DDoS 攻击流量清洗等) → 阿里云盾 → 源服务器

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

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

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

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

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

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

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

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

  ## 这里取得原始用户的 IP 地址
  map $http_x_forwarded_for $clientRealIp {
  "" $remote_addr;
  ~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
  }
   
  ## 针对原始用户 IP 地址做限制
  limit_conn_zone $clientRealIp zone=TotalConnLimitZone:20m ;
  limit_conn TotalConnLimitZone 50;
  limit_conn_log_level notice;
   
  ## 针对原始用户 IP 地址做限制
  limit_req_zone $clientRealIp zone=ConnLimitZone:20m rate=10r/s;
  #limit_req zone=ConnLimitZone burst=10 nodelay; #如果开启此条规则,burst=10 的限制将会在 nginx 全局生效
  limit_req_log_level notice;
   
  ## 具体 Server:如下在监听 php 部分新增限制规则即可
  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;
  }
   
  }

三、echo 模块

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

①、给 nginx 集成 echo 模块:

  cd /usr/local/src
  #下载 echo 模块并解压:
  wget https://github.com/openresty/echo-nginx-module/archive/v0.57.tar.gz
  tar zxvf v0.57.tar.gz
   
  #下载 nginx 并解压
  wget http://nginx.org/download/nginx-1.6.0.tar.gz
  tar -xzvf nginx-1.6.0.tar.gz
  cd nginx-1.6.0/
   
  #查看在用 nginx 的编译参数(如果是全新安装则省略)
  /usr/local/nginx/sbin/nginx -V
  nginx version: nginx/1.6.0
  built 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.57
   
  #make 编译
  make -j2
   
  #平滑升级 nginx (如果是全新安装请执行:make install)
  mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
  cp -f objs/nginx /usr/local/nginx/sbin/
  make upgrade

②、echo 用法举例:

其实就和 shell 的 echo 差不多,能否输出自定义信息。

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

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

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

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

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

生效后,访问 http://yourdomain.com/realip 就能显示你所用电脑(宽带)的真实 IP 了(可在 ip138 查验准确性):Nginx在CDN加速之后,获取用户真实IP做并发访问限制的方法

Ps:感兴趣又喜欢学习的童鞋可以看下相关 wiki 文档:http://wiki.nginx.org/HttpEchoModule

本文介绍到这就差不多结束了,也是在神作的基础上精简整理并测试的,如果看完还有些许疑问,请前往查看神作原文,也许还是大神写的比较好理解(是否是原创我就不深究了,感觉也是转来转去,都没留链接,悲哀的互联网)!

本文整理自【棒主妇开源】,原文地址:《网站安全配置(Nginx)防止网站被攻击》。

标签:Nginx,##,IP,CDN,用户,nginx,limit
From: https://www.cnblogs.com/gaoyanbing/p/17036657.html

相关文章

  • Nginx配置中的log_format用法梳理(设置详细的日志格式)
    nginx服务器日志相关指令主要有两条:一条是log_format,用来设置日志格式;另外一条是access_log,用来指定日志文件的存放路径、格式和缓存大小,可以参加ngx_http_log_module。一......
  • 阿里云SLB后Nginx、Tomcat获取真实IP
    一、SLB后Nginx如何获取真实IP前提:nginx作为slb获取真实ip是使用http_realip_module,默认一键安装包安装的nginx没有安装这个模块需要重新重新编译nginx并加装。查看......
  • 【linux】nginx: [emerg] getpwnam("nginx") failed in /usr/local/nginx/conf/nginx.
    启动nginx报错:        nginx:[emerg]getpwnam("nginx")failedin/usr/local/nginx/conf/nginx.conf:2是因为配制文件中使用的启动账户在系统中并没有找到解......
  • 03 JavaScript
    JavaScriptECMAScript和JavaScript的关系1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组织ECMA,希望这门语言能够成为国际标准。次年,ECM......
  • Linux查看公网IP和私网(内网)IP的方法
    查看Linux公网IP地址查看Linux公网IP结合第三方网站辅助一下即可,直接curl一下http://ifconfig.me或者http://cip.cc即可命令:curlifconfig.me命令:curlcip.cc ......
  • Nginx常见的错误及解决方法
    1、Nginx 常见启动错误 有的时候初次安装nginx的时候会报这样的错误 sbin/nginx -c conf/nginx.conf 报错内容:sbin/nginx: error while loading shared libr......
  • JavaScript 性能优化
    1.内存管理内存管理介绍内存:由可读写单元组成,表示一片可操作空间管理:人为的去操作一片空间的申请、使用和释放内存管理:开发者主动申请空间、使用空间、释放空......
  • Nginx的alias指令引发的漏洞
    前言一开始看到alias都不知道是什么,我们先来学习一下Nginx以其高性能著称,常用作前端反向代理服务器。同时nginx也是一个高性能的静态文件服务器。通常都会把应用的静态文......
  • 客服系统前端开发:JavaScript删除对象数组中指定key value的对象【唯一客服】网页在线
    经常我们有这样的需要,比如有一个对象数组,我们要把这个数组里某个对象删除掉,根据他的某一个key的value来删除可以使用JavaScript的filter()方法来删除对象数组中指定k......
  • TypeScript支持的数据类型(一)
    说明TypeScript做为JavaScript的超集,对于JavaScript中的类型完全支持,除了JavaScript中的类型之外,TS自身也提供了一些数据类型。在TS中,数据类型的整体结构如下:最顶级......