如何设置能限制某个IP某一时间段的访问次数,特别面对恶意的DDOS攻击的时候。其中CC攻击(Challenge Collapsar)是DDOS(分布式拒绝服务)的一种,也是一种常见的网站攻击方法,攻击者通过代理服务器或者肉鸡向受害主机不停地发大量数据包,造成对方服务器资源耗尽,一直到宕机崩溃。
Nginx可以通过限制IP访问次数、添加IP黑名单、禁止代理访问等配置防御DDOS,CC等流量攻击。
CC攻击一般就是使用有限的IP数对服务器频繁发送数据来达到攻击的目的,Nginx可以通过 ngx_http_limit_conn_module 和 ngx_http_limit_req_module 配置来限制IP在同一时间段的访问次数来防CC攻击。
ngx_http_limit_req_module:该模块用来限制单个的IP地址的请求的处理速率,使用“泄漏桶”方法进行限制,指令 limit_req_zone 和 limit_req 配合使用来达到限制。一旦并发连接超过指定数量,就会返回503错误。
ngx_http_limit_conn_module:该模块用来限制单个IP地址的并发连接数,使用 limit_conn_zone 和 limit_conn 指令。
这两个模块的区别是前者对一段时间内的连接数限制,后者是对同一时刻的连接数限制。
注意:Nginx是用yum安装的,配置文件目录和其他方式安装的不同
一、使用ngx_http_limit_req_module模块
ngx_http_limit_req_module 模块使用官方文档:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html
ngx_http_limit_req_module模块用于限制单一IP地址的请求的处理速率,使用“漏斗”方法完成限制
打开配置文件
vim /etc/nginx/nginx.conf
在http段下写入以下配置
limit_req_zone $binary_remote_addr zone=req_zone:10m rate=1r/s;
单个配置说明:
limit_req_zone:限制请求
$binary_remote_addr:二进制IP地址
zone=req_zone:限制策略的名称
10m:占用10M内存空间
rate=1r/s:允许每秒1次请求
整个配置说明:
设置共享内存区域名称和大小以及请求速率。
定义一个名为req_zone的limit_req_zone用来存储session,大小是10M内存,以变量 $binary_remote_addr 作为会话的判断基准(即一个地址一个会话),限制每秒的请求为1个(包括各种请求,例如ajax,例如img的src,如果给的过少会导致上述请求返回503错误,另外这里的单位也可以改成每分钟,例如每两秒一个请求,30r/m)。
$binary_remote_addr变量的大小对于IPv4地址总是4字节,对于IPv6地址总是16字节。存储状态在32位平台上总是占用64字节,在64位平台上占用128字节。一个兆字节区域可以保持大约1.6万个64字节的状态或大约8千个128字节的状态。
如果区域存储已用尽,则将删除最近最少使用的状态。即使在此之后无法创建新状态,请求也会因503错误终止。
在server段或者location段引用限制策略的名称
打开配置文件,注意:不是之前的/etc/nginx/nginx.conf配置文件
vim /etc/nginx/conf.d/default.conf
在server段或者location段写入以下配置:
limit_req zone=req_zone;
#limit_req zone=req_zone burst=5;
#limit_req zone=req_zone burst=5 nodelay;
配置说明:
limit_req zone=req_zone:引用限制策略名称,和http段定义的zone=req_zone名称要一致
下面对注释掉的12行和13行中的 burst=5 和 nodelay 配置做个说明
burst=5:平均每秒最多允许不超过1个请求,并且突发不超过5个请求。
nodelay:平均每秒允许不超过1个请求,突发请求不超过5个,如果不希望在请求有限的情况下延迟过多的请求,则应该使用参数nodelay
举个例子:
设置rate=20r/s每秒请求数为20个,burst为5个,burst是爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有5个,超过的请求会直接报503错误然后返回。
可能有几个limit_req指令,例如,以下配置限制来自单个IP地址的请求的处理速率,同时限制虚拟服务器的请求处理速率,如下:
在http段写入一下配置
limit_req_zone $binary_remote_addr zone=per_ip:10m rate=1r/s;
limit_req_zone $server_name zone=per_server:10m rate=10r/s;
在server段引用限制策略的名称
limit_req zone=per_ip burst=5 nodelay;
limit_req zone=per_server burst=10;
配置说明:
burst=5:平均每秒最多允许不超过1个请求,并且突发不超过5个请求。
nodelay:如果设置,会在瞬时提供处理(burst + rate)个请求的能力,请求超过(burst + rate)的时候就会直接返回503,永远不存在请求需要等待的情况。(这里的rate的单位是:r/s)如果没有设置,则所有请求会依次等待排队。
结束以后必须重启Nginx或者重新加载配置文件才会生效
# 重启Nginx
systemctl restart nginx
# 重新加载
nginx -s reload
这时配置就完成了,如果同一个IP在反复请求次数过于频繁时,会怎么样?下面测试
Nginx默认根目录为:/usr/share/nginx/html/,在下面新建一个测试网页:
vim /usr/share/nginx/html/nginx_limit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
</head>
<body>
<h2>Nginx访问限制!</h2>
<img src="img/1.jpg">
</body>
</html>
在浏览器访问:
Ctrl+F5连续多次刷新,在控制台出现503错误
也可以用压力测试工具进行测试
# 安装
yum install -y httpd-tools
# 测试
ab -n 100 -c 10 http://192.168.5.66/nginx_limit.html
其中,-n表示请求数,-c表示并发数
[root@CentOS7 ~]# ab -n 100 -c 10 http://192.168.5.66/nginx_limit.html
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.5.66 (be patient).....done
Server Software: nginx/1.16.1
Server Hostname: 192.168.5.66
Server Port: 80
Document Path: /nginx_limit.html
Document Length: 250 bytes # 文档长度
Concurrency Level: 10 # 当前并发数
Time taken for tests: 5.003 seconds # 消耗总时间
Complete requests: 100 # 完成请求数
Failed requests: 94 # 失败请求数
(Connect: 0, Receive: 0, Length: 94, Exceptions: 0)
Write errors: 0
Non-2xx responses: 94 # 有问题的响应数
Total transferred: 67564 bytes # 总的传输大小
HTML transferred: 47936 bytes # http传输大小
Requests per second: 19.99 [#/sec] (mean) # 每秒钟处理多少个请求
Time per request: 500.281 [ms] (mean)
Time per request: 50.028 [ms] (mean, across all concurrent requests)
Transfer rate: 13.19 [Kbytes/sec] received
查看错误日志
tail -f /var/log/nginx/error.log
日志中会记录下限制的请求
二、使用ngx_http_limit_conn_module模块
ngx_http_limit_conn_module 模块使用官方文档:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html
ngx_http_limit_conn_module模块用于限制单个IP地址的连接数
设置共享内存区域和给定键值的最大允许连接数。当超过此限制时,服务器将返回503(Service Temporarily Unavailable)错误 。
打开配置文件
vim /etc/nginx/nginx.conf
在http段写入以下配置
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
配置说明:
设置共享内存区域名称和大小。
定义一个名为conn_zone的limit_conn_zone用来存储session,大小是10M内存,以变量 $binary_remote_addr 作为会话的判断基准(即一个地址一个会话)。
$binary_remote_addr变量的大小对于IPv4地址总是4字节,对于IPv6地址总是16字节。存储状态在32位平台上总是占用32或64字节,在64位平台上占用64字节。当大小为1M的时候,可以保存大约3.2万个32字节的状态或大约1.6万个64字节的状态。
如果区域存储耗尽,服务器将向所有后续请求返回503错误。
在location段引用
limit_conn conn_zone 1;
配置说明:
设置最大允许连接数。
conn_zone:要和http段中配置的zone=conn_zone策略名称一致。
1:一个会话只能进行一个连接。 也就是,限制 / 目录下,一次一个IP地址只允许一个连接,多过一个,一律503。
可能有几个limit_conn指令,以下配置将限制每个客户端IP与服务器的连接数,并同时限制与虚拟服务器的连接总数:
在http段写入以下配置
limit_conn_zone $binary_remote_addr zone=per_ip:10m;
limit_conn_zone $server_name zone=per_server:10m;
在server段或者location段写入以下配置
limit_conn per_ip 10; # 单个客户端IP与服务器的连接数
limit_conn per_server 100; # 限制与服务器的连接总数
三、禁止IP或IP网段
查找服务器所有访问者IP方法
awk '{print $1'} nginx_access.log |sort |uniq -c |sort -rn
nginx_access.log 为Nginx访问日志文件所在路径
会到如下结果,前面是IP的访问次数,后面是IP,可以把访问次数异常的IP屏蔽掉,如下面结果,
屏蔽IP的方法
ngx_http_access_module 模块使用官方文档:http://nginx.org/en/docs/http/ngx_http_access_module.html
在Nginx的配置文件目录下面,新建屏蔽IP文件,命名为nginx_limit,以后新增加屏蔽IP只需编辑这个文件即可。
vim /etc/nginx/conf.d/nginx_limit
加入如下内容并保存:
deny 192.168.5.207;
在Nginx的配置文件default.conf中加入如下配置,可以放到http,server,location,limit_except语句块,需要注意路径,本例当中default.conf,nginx_limit在同一个目录中。
include /etc/nginx/conf.d/nginx_limit;
保存default.conf文件,然后测试现在的nginx配置文件是否是合法的:
nginx -t
如果配置没有问题,就会输出:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
如果配置有问题就需要检查下哪儿有语法问题,如果没有问题,重启 Nginx:
systemctl restart nginx
注意:
屏蔽IP的配置文件既可以屏蔽单个IP,也可以屏蔽IP段,或者只允许某个IP或者某个IP段访问。
# 屏蔽单个IP访问
deny IP;
# 允许单个IP访问
allow IP;
# 屏蔽所有IP访问
deny all;
# 允许所有IP访问
allow all;
# 屏蔽整个段即从123.0.0.1到123.255.255.254访问的命令
deny 123.0.0.0/8;
# 屏蔽IP段即从123.45.0.1到123.45.255.254访问的命令
deny 124.45.0.0/16;
# 屏蔽IP段即从123.45.6.1到123.45.6.254访问的命令
deny 123.45.6.0/24;
# 如果想实现这样的应用,除了几个IP外,其他全部拒绝,
# 那需要在 nginx_limit中这样写
allow 1.1.1.1;
allow 1.1.1.2;
deny all;
单独网站屏蔽IP的方法,把 include /etc/nginx/conf.d/nginx_limit; 放到网址对应的在server{}语句块,
所有网站屏蔽IP的方法,把 include /etc/nginx/conf.d/nginx_limit; 放到http {}语句块。
四、阻止用户使用代理
项目在上线之前,会对Nginx进行压测。在真正上线以后,如果别人对服务器使用ab测试之类的,会对服务器有很大的风险。甚至就 curl 和 wget 都不想让其用,只有正常的请求才可以使用。
Nginx提供了这样的一个功能,拒绝用户代理,这就需要使用到$http_user_agent变量。
下面三句if是禁止使用代理IP来访问,或禁止使用压力测试软件进行DOS攻击(放在default.conf的server里面)
if ($http_user_agent ~* ApacheBench|WebBench|java/){
return 403;
}
if ($http_user_agent ~* (Wget|ab) ) {
return 403;
}
if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
return 403;
}
这些 if 声明使用正则表达式匹配了任意不良用户字符串,并向匹配的对象返回403 HTTP状态码。 $http_user_agent是HTTP请求中的一个包含有用户代理字符串的变量。‘~’操作符针对用户代理字符串进行大小写敏感匹配,而‘~*’操作符则进行大小写无关匹配。‘|’操作符是逻辑或,因此,你可以在 if 声明中放入众多的用户代理关键字,然后将它们全部阻挡掉。
# 重启nginx
systemctl restart nginx