对HTTP资源访问限制
nginx限制范围,有可能是:
- 对指定键值(比如每个IP)的连接数;
- 对指定键值(比如每秒/分钟之间允许处理的请求数)的访问频率;
- 连接的下载速率。
一、连接数的限制
1、用 limit_conn_zone 指令定义某个键,并且共享内存区域参数(工作进程会使用该区域作为共享该键值数)。第1参数是一表达式作为键,第二参数zone,用于指定zone名称和它的大小。
limit_conn_zone $binary_remote_addr zone=addr:10m;
2、用 limit_conn 指令 在 location{},server{},或 http{} 上下文中使用连接限制,该指令的第一参数为上方定义的共享内存区域的名称,第二参数定义该键的允许连接数。
location /download/ { limit_conn addr 1; }
上例限制了每个IP地址的连接数,因为 $binary_remote_addr 变量作为键。
另一个常用的限制连接数是指定某个服务器 $server_name变量:
http { limit_conn_zone $server_name zone=servers:10m; server { limit_conn servers 1000; } }
二、限制访问频率
访问频率限制常用于DDOS攻击防范,或防止上游服务器群组同一时间处理过多的访问。该方法基于 leaky bucket 算法:请求以各种速率到达存储桶,并以固定速率离开存储桶。
使用频率限制前,需配置 limit_req_zone指令,有以下参数:
key键:该参数用于区分各客户端,通常用变量表示;
共享内存区:其名称和大小用于存储这些key键;
频率:请求访问频率限制设定,单位为每秒请求(r/s) 或者每分钟请求(r/m)。
limit_req_zone指令定义在http{}层级:
http { #... limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; }
通过以上配置,共享内存区域 one 拥有 10MB 大小。该区保存 $binary_remote_addr变量值 即客户端IP。
注意: $remote_addr 也是记录客户端IP地址,而$binary_remote_addr 保存客户端IP地址值的二进制位数,使用空间更小。
共享区域的大小可通过以下计算得出:$binary_remote_addr 的 IPV4地址值为4字节,在64位系统占用128字节。因此,大约16,000个IP地址的状态信息占用该区域的1MB大小。
如果NGINX需要添加新条目时存储空间已用尽,它将删除最旧的条目。如果释放的空间仍然不足以容纳新记录,则NGINX返回状态代码" 503 servcie Unavaliable"。状态码可以用 limit_req_status 指令重新定义。
区域设置好后,就可以用 limit_req 指令在 server{}、location{}或 http{} 上下文中设定好。
http { #... limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { #... location /search/ { limit_req zone=one; } } }
以上配置,nginx将不会处理多于每秒1个请求的 /search/位置访问。这些请求的处理以不超过指定的频率的方式延迟,如果请求数量超过指定的速率,NGINX将延迟处理此类请求,直到“存储桶”(共享存储区 one)已满。如果已满时仍有请求过来,则nginx回复 503 Service Unavailable 错误。
三、测试请求频率限制
在配置真实环境下的频率限制前,可以用 dry run 模式不限制请求处理,但会将过多请求的问题记录下来。dry run模式开启使用 limit_req_dry_run 指令:
location /search/ { limit_req zone=one; limit_req_dry_run on; }
日志中记录 dry_run标记:
2019/09/03 10:28:45 [error] 142#142: *13246 limiting requests, dry run, excess: 1.000 by zone "one", client: 172.19.0.1, server: www.example.com, request: "GET / HTTP/1.0", host: "www.example.com:80"
四、处理过多请求的选项
由于流量趋向于突发性,因此在流量突发期间响应客户端请求返回错误不是最好的情况。
NGINX对于过多请求可以被缓冲和处理。limit_req指令的burst参数设置等待以指定频率处理的过多请求的最大数量:
http { #... limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { #... location /search/ { limit_req zone=one burst=5; } } }
以上配置,如果请求超出每秒1个,则超出的请求放入one区。one区已满则过多的请求排入队列,队列大小5个。队列中的请求处理将请不超出设定的总速率。超出burst限制,将会返回503。
如果在流量突发期间不希望延迟请求,请添加nodelay参数:
location /search/ { limit_req zone=one burst=5 nodelay; }
另外,delay参数可以定义了延迟过多请求以符合定义的速率限制的时间点:
location /search/ { limit_req zone=one burst=5 delay=3; }
使用此配置时,前3个请求(延迟)将无延迟地传递,接下来的2个请求(突发-延迟)将以总速率不大于指定速率的方式延迟,进一步的请求将被拒绝,因为总突发大小已超过,往后的请求将被延迟。
五、限制带宽
限制每条连接的带宽使用,使用 limit_rate 指令:
location /download/ {
limit_rate 50k;
}
以上设置了访问 /download/的每个客户端在每个连接至多使用50K每秒的速率。不过,这里没有限制同一客户端同时开设多个连接。所以要做到下载速度不能超出指定值,那么同时也要限制连接数量:
location /download/ { limit_conn addr 1; limit_rate 50k; }
还可以限制客户端在下载一定数量的数据后才进行限制速度,请使用 limit_rate_after 指令。比如允许客户快速下载一定数量的数据(例如,文件标头-电影索引)后,开始限制下载其余数据的速率。
以下示例合并使用了连接数和带宽限制。每个客户端地址允许的最大连接数设置为5个连接,这适合大多数常见情况,因为现代浏览器通常一次最多打开3个连接。同时,提供下载的位置仅允许一个连接:
http { limit_conn_zone $binary_remote_address zone=addr:10m server { root /www/data; limit_conn addr 5; location / { } location /download/ { limit_conn addr 1; limit_rate_after 1m; limit_rate 50k; } } }
limit_rate 还可以设置 变量,进行动态控制带宽:
map $ssl_protocol $response_rate { "TLSv1.1" 10k; "TLSv1.2" 100k; "TLSv1.3" 1000k; } server { listen 443 ssl; ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; ssl_certificate www.example.com.crt; ssl_certificate_key www.example.com.key; location / { limit_rate $response_rate; # Limit bandwidth based on TLS version limit_rate_after 512; # Apply limit after headers have been sent proxy_pass http://my_backend; } }
限制对代理TCP资源的访问
一、限制IP地址
allow和deny指令能分开对指定的IP地址或地址段允许或禁止访问。这两指令设置在stream或server上下文中。
如果设置了一系列规则,则么匹配按顺序从头到尾,直到头一条匹配到为止。
stream { #... server { listen 12345; deny 192.168.1.2; allow 192.168.1.1/24; allow 2001:0db8::/32; deny all; } }
上例中按顺序,192.168.1.1/24为允许访问,并排除192.168.1.2。而2001:0db8::/32的IPV6地址则被允许,并排除其他所有IP地址。
二、限制TCP连接数
nginx能限制单一IP同时建立的TCP连接数,这对防止DoS攻击很有效。
首先,在 stream 上下文使用 limit_conn_zone 指令定义存储连接到一服务器的最大TCP连接数区域,和与其相关的键。
stream { #... limit_conn_zone $binary_remote_addr zone=ip_addr:10m; #... }
上例中,该键使用 $binary_remote_addr 变量定义。而共享存储区名为ip_addr,具有10MB大小。
区域定义好后,再使用 limit_conn 指令限制连接。该指令第一参数为 limit_conn_zone 定义的区域名称,第二参数指明允许的各IP地址最大连接数。该指令可在stream 或 server上下文中定义。
stream { #... limit_conn_zone $binary_remote_addr zone=ip_addr:10m; server { #... limit_conn ip_addr 1; } }
当限制每个IP地址的连接数时,请注意网络地址转换(NAT)设备后面的多个主机共享相同的IP地址。
三、限制带宽
要设置下载、上传的TCP连接速度,可以用 proxy_download_rate 或 proxy_upload_rate 指令:
server { #... proxy_download_rate 100k; proxy_upload_rate 50k; }
上例设置了单个TCP连接拥有100K下载速率,和50K上传速率。
如果需要限制每个客户端的速率,可以限制连接数为1:
stream { #... limit_conn_zone $binary_remote_addr zone=ip_addr:10m; server { #... limit_conn ip_addr 1; proxy_download_rate 100k; proxy_upload_rate 50k; } }