Nginx 用户请求限制

我们经常会遇到服务器流量异常、负载过大等这些恶意的攻击访问。资源浪费还是其次,造成正常用户无法访问那就不好了。

Nginx有ngx_http_limit_conn_modulengx_http_limit_req_module两个模块可以在一定程度上缓解这个问题。接下来就分别介绍这两个模块配置用法以及如何添加白名单。

ngx_http_limit_conn_module

该模块可以根据定义的键来限制每个键值的连接数,并不是同一个IP来源的所有连接数都会被该模块计数,只有那些正在被处理的请求(这些请求的头信息已被完全读入)所在的连接才会被计数。

1
2
3
4
5
6
7
8
9
10
11
12
http {
limit_conn_zone $binary_remote_addr zone=limit:10m;
limit_conn_log_level info;
server {
location ^~ /download/ {
limit_conn limit 4;
limit_rate 200k;
alias /data/blog.dingmingk.com/download/;
}
}
}

语法: limit_conn_zone $variable zone=name:size;

默认值: none

配置段: http

该指令描述会话状态存储区域。键的状态中保存了当前连接数,键的值可以是特定变量的任何非空值(空值将不会被考虑)。$variable定义键,zone=NAME定义区域名称,后面的limit_conn会用到的。size定义各个键共享内存空间大小。如:

1
limit_conn_zone $binary_remote_addr zone=addr:10m;

客户端的IP地址作为键。

注意,这里使用的是$binary_remote_addr变量,而不是$remote_addr变量。

  • $remote_addr 变量长度为7字节到15字节,而存储状态在32位平台中占用32字节或64字节,在64位平台中占用64字节。
  • $binary_remote_addr 变量的长度是固定的4字节,存储状态在32位平台中占用32字节或64字节,在64位平台中占用64字节。

1M共享空间可以保存3.2万个32位的状态,1.6万个64位的状态。如果共享内存空间被耗尽,服务器将会对后续所有的请求返回 503(Service Temporarily Unavailable)错误。

limit_zone 指令和 limit_conn_zone 指令同等意思,已经被弃用,就不再说明了。

语法: limit_conn_log_level info|notice|warn|erro

默认值: error

配置段: http,server,location

当达到最大限制连接数后,记录日志的等级。

语法: limit_conn zone_name number

默认值: none

配置段: http,server,location

指定每个给定键值的最大同时连接数,当超过这个数字时被返回 503(Service Temporarily Unavailable)错误。

当多个 limit_conn 被配置时,所有的连接数限制都会生效。比如下面的配置不仅会限制单一IP来源的连接数,同时也会限制单一虚拟服务器的总连接数:

1
2
3
4
5
6
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
limit_conn perip 10;
limit_conn perserver 100;
}

limit_conn指令可以继承

语法: limit_conn_status code;

默认值: 503

配置段: http,server,location

该指令在1.3.15版本引入,指定当超过限制时,返回的状态码。

语法: limit_rate rate

默认值: 0

配置段: http,server,location,if in location

对每个连接的速率限制。参数rate的单位是字节/秒,设置为0将关闭限速。按连接限速而不是按IP限制,因此如果某个客户端同时开启了两个连接,那么该客户端的整体速率是这条指令设置值的2倍。

ngx_http_limit_req_module

ngx_http_limit_req_module模块和ngx_http_limit_conn_module模块基本类似,该模块可以通过定义的键值来限制请求处理的频率。特别的,可以限制来自单个IP地址的请求处理频率。限制的方法如同漏斗,每秒固定处理请求数,推迟过多请求。

1
2
3
4
5
6
7
8
9
10
http {
limit_req_zone $binary_remote_addr zone=dingmingk_com:10m rate=1r/s;
server {
location ^~ /download/ {
limit_req zone=ttlsa_com burst=5;
alias /data/blog.dingmingk.com/download/;
}
}
}

语法: limit_req_zone $variable zone=name:size rate=rate;

默认值: none

配置段: http

设置一块共享内存限制域用来保存键值的状态参数,特别是保存当前超出请求的数量。键的值就是指定的变量(空值不会被计算)。

客户端的IP地址作为键。

语法: limit_req zone=name [burst=number] [nodelay];

默认值: -

配置段: http,server,location

设置对应的共享内存限制域和允许被处理的最大请求数阈值。如果请求的频率超过了限制域配置的值,请求处理会被延迟,所以所有的请求都是以定义的频率被处理的。超过频率限制的请求会被延迟,直到被延迟的请求数超过了定义的阈值,这时这个请求会被终止,并返回503(Service Temporarily Unavailable)错误。

白名单

有些时候,我们不希望对某些IP进行限制,比如公司IP、反代服务器等,这就需要配置白名单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
http {
geo $whiteiplist {
default 1;
127.0.0.1 0;
10.0.0.0/8 0;
120.26.73.0 0;
}
map $whiteiplist $limit {
1 $binary_remote_addr;
0 "";
}
limit_conn_zone $limit zone=limit:10m;
server {
listen 8080;
server_name blog.dingmingk.com;
location ^~ /dingmingk.com/ {
limit_conn limit 4;
limit_rate 200k;
alias /data/blog.dingmingk.com/download/;
}
}
}

技术要点

  • geo指令定义一个白名单$whiteiplist,默认值为1,所有都受限制。如果客户端IP与白名单列出的IP相匹配,则$whiteiplist的值为0也就不受限制。
  • map指令是将$whiteiplist值为1的,也就是受限制的IP,映射为客户端IP。将$whiteiplist值为0的,也就是白名单IP,映射为空的字符串。
  • limit_conn_zonelimit_req_zone指令对于键为空值的将会被忽略,从而实现对于列出来的IP不做限制。