Nginx1.16访问限制

如何设置能限制某个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

posted @ 2020-01-07 17:35  OpsDrip  阅读(1483)  评论(0编辑  收藏  举报