简介

Nginx作为一款轻量高性能开源的Web服务器,深受大众欢迎并在服务器中得到了广泛应用。但是Nginx默认是不设防的,即不会自动防御DDoS攻击和CC攻击,因此攻击者可以轻易地发送大量的请求从而耗尽你的服务器资源、恶意盗刷你的服务器流量或者让你的后端服务器崩溃,因此配置Nginx基础防御是很有必要的,本篇文章就记录如何为你的Nginx配置一些基础的防御规则从而保护你的Web服务器。

相关文章链接:

Nginx限制单个IP的TCP连接数与TCP连接速率

示例配置代码:

#将这行代码放到Nginx配置文件的HTTP块中
limit_conn_zone $binary_remote_addr zone=ip_addr:10m;

上面的一行代码的意思是以二进制格式标记客户端的 IP 地址,共享内存区域的名称是ip_addr,区域大小是 10 兆字节。

然后在要应用该限制的Server块部分写入下列代码:

limit_conn ip_addr [单IP最大可接受TCP连接数];
#例如,规定单个IP最多与服务器建立一个TCP连接,超出的连接将返回503
limit_conn ip_addr 1;

#实测如果你的网站是个普通的博客之类的静态为主的网站,不是什么下载站、视频站、音乐站,没有搞聊天通信什么需要使用TCP长连接的话,限制设置为1是不会影响正常用户访问的。

#单条TCP连接的上传与下载速率限制
#下载最高1000kb/s
proxy_download_rate 1000k;
#上传最高50kb/s
proxy_upload_rate   500k;

建议根据实际情况来设置限制,写后记得好好测试一下再将配置上线,否则可能导致正常用户访问出现异常。

官方文档:https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-tcp/

Nginx限制单个IP的请求速率

示例配置代码:

#将这行代码放到Nginx配置文件的HTTP块中
limit_req_zone $binary_remote_addr zone=peripreq:10m rate=3r/s;

上面的一行代码的意思是:

  • limit_req_zone:定义请求限制区域
  • $binary_remote_addr:以二进制的方式标记客户端IP
  • zone=peripreq:10m:本共享内存区域的名称是peripreq,区域大小是 10 兆字节,它的大小与队列的最大长度有关。
  • rate=3r/s:定义单个IP最大可接受的请求速率为3次每秒(可自行更改

这时我们就有一个限制区域limit_req_zone)了,只需在需要应用该限制区域Server块中引用这个区域名称就行。

引用方法:在要应用该限制的Server块部分写入如下代码

limit_req zone=peripreq burst=30 nodelay;

意思是:

  • limit_req:请求数限制
  • zone=peripreq:区域规则名称为:peripreq
  • burst=30:最大可接受突发请求数为30
  • nodelay:要求立即返回结果

重点说明一下burstnodelay

关于burst的突发请求数,如果一个IP超过了限制区域原本的限制(比如上面设置的是3r/s),那么就会开始使用突发请求数的配额,如果请求数超过了基础限制速率+突发请求数,则会将该请求放入队列或则直接返回503。

突发请求数会以基础限制速率的速度恢复。

关于nodelay参数,这个参数决定了是否将超出的请求放到队列中处理,队列满了才会返回503。加了这个参数的话就是要求立即返回结果,不会将超出范围的请求放入队列中等待,而是立即返回503。

温馨提示请求数!=用户看的网页数,正常打开一个网页可能会发送几十个请求,请使用网页调试工具查看打开一次自己的网站页面会发送多少个请求,请根据自己网站的实际情况调整并测试。

示例参数大小:

菜鸟教程和知乎为例,打开一次它们的首页共发送了大约一百多次请求,那么rate=3r/sburst=200是比较河里的。

这里可能有小伙伴会问:3r/s远远小于200,这难道不是很容易就超出限制了?

那是因为很多资源第一次请求后就缓存下来了,不会再向服务器索取了,因此只有第一次打开会发送这么多的请求数,用适当的突发设置请求应付一下就OK了。

image-20220711143830788

image-20220711143651076

Nginx限制后端服务器的最大请求次数

上面的那些手段对于DoSCC攻击可能有点用,但是如果遇到DDoS就用处不是很大,对于这种分布式的攻击,我们应该首先考虑一下对后端的保护,防止服务器后端因同时收到太多请求而直接崩溃导致数据损坏等情况的发生。

示例配置代码:

#将这行代码放到Nginx配置文件的HTTP块中
#下面的这个是针对站点本身的总请求次数限制,用于对后端的保护,防止DDoS攻击直接把后端给爆破了
limit_req_zone $server_name zone=perserverreq:10m rate=20r/s;

上面的一行代码的意思是:

  • limit_req_zone:定义请求限制区域
  • $server_name:以网站名为单位构建限制区域
  • zone=peripreq:10m:本共享内存区域的名称是perserverreq,区域大小是 10 兆字节,它的大小与队列的最大长度有关。
  • rate=20r/s:定义单个站点最大可接受的请求速率为20次每秒(可自行更改

然后同样在Server部分引用该限制区域,再设置个突发请求限制以灵活应对突发请求

#允许最大突发50次服务器请求
limit_req zone=perserverreq burst=100;

这里的20和100只是参考,这两个参数需要根据自己的服务器后端的最大并发处理能力来决定,

配置自动拉黑触发过载保护的IP

以上的那些手段只是配置了一下Nginx的并发限制,达到阈值后Nginx只是会给相应的IP返回错误,并不会拿那些恶意IP怎么样,光做到这些还是不太够的,至少要能把这些恶意IP拉黑掉。

为了实现拉黑功能可以借助一些WAF模块如:ModSecurityhttp_guardngx_lua_waf

关于它们的使用方法大家可以上网上搜索一下,这里笔者来介绍一下如何使用Fail2Ban来实现IP自动拉黑。

首先你需要在相应Server块定义网站错误日志,例如在server块中加上

    error_log  /www/wwwlogs/error.log;

Fail2Ban需要读取这个日志文件以判断封禁IP

关于Fail2Ban的配置和相关的用法(包括Nginx自动拉黑)笔者已经在这个文章中写过了,可以去看一下我的这篇文章:https://www.hash070.top/archives/fail2ban.html

这里就再写一下如何清空日志吧,我想到的方法很简单:使用echo命令用一个空格覆盖你的日志文件就OK了。例如:

echo " " > /root/nginx/logs/error.log

想要定时清理的话,借助crontab可以轻松实现,它的用法可以看我的这篇文章:https://www.hash070.top/archives/linux-crontab.html

Q.E.D.