如何在nginx中进行速率限制,但包括/排除某些IP地址?


27

我可以用来limit_req对服务器的所有请求进行速率限制。

但是,我想删除某些IP地址(即白名单)的速率限制,而对某些其他IP地址(即某些我希望低至1r / s的IP)使用不同的速率限制。

我尝试使用条件语句(例如if ( $remote_addr = "1.2.3.4" ) {}),但这似乎仅适用于重写规则,而不适用于速率限制规则。

Answers:


33

最好避免使用“ if”指令。当limit_req_zone(和limit_conn_zone)中的键为空时,不应用限制。您可以将其与地图和地理模块结合使用,以在不应用节流阀限制的情况下创建IP白名单。

本示例说明如何为单个IP的并发请求和请求速率配置限制。

http {
    geo $whitelist {
       default 0;
       # CIDR in the list below are not limited
       1.2.3.0/24 1;
       9.10.11.12/32 1;
       127.0.0.1/32 1;
    }

    map $whitelist $limit {
        0     $binary_remote_addr;
        1     "";
    }

    # The directives below limit concurrent connections from a 
    # non-whitelisted IP address to five

    limit_conn_zone      $limit    zone=connlimit:10m;

    limit_conn           connlimit 5;
    limit_conn_log_level warn;   # logging level when threshold exceeded
    limit_conn_status    503;    # the error code to return

    # The code below limits the number requests from a non-whitelisted IP
    # to one every two seconds with up to 3 requests per IP delayed 
    # until the average time between responses reaches the threshold. 
    # Further requests over and above this limit will result 
    # in an immediate 503 error.

    limit_req_zone       $limit   zone=one:10m  rate=30r/m;

    limit_req            zone=one burst=3;
    limit_req_log_level  warn;
    limit_req_status     503;

区域指令必须放置在http级别,但是其他指令可以放置在更下方,例如,在服务器或位置级别以限制其范围或进一步调整限制。

有关更多信息,请参考Nginx文档ngx_http_limit_req_modulengx_http_limit_conn_module


这两个模块之间有什么区别?
mente

1
根据评论,第一个限制并发连接,第二个限制连接速率
shonky linux user

您能否解释为什么要分两个阶段进行映射,geo后跟map,而不是直接geo用于设置$limit
Marcus Downing

2
似乎geo无法映射到变量,因此,如果指定$binary_remote_addr为映射值,它将转换为文字字符串"$binary_remote_addr",而不是变量的值。
ColinM '16

1
我要补充一点,如果所讨论的IP已经在区域中,则必须重新启动nginx;否则,请重新启动。重新加载是不够的。
Halfgaar

5

您可以安全地使用命名位置,例如if()块中的“ @location”。

请参阅:http//wiki.nginx.org/IfIsEvil

这样的事情应该起作用:

http {

   limit_req_zone $binary_remote_addr zone=delay:10m rate=1r/m;

   server {
      ...

      error_page 410 = @slowdown;

      if( $remote_addr != "1.2.3.4" ) {
         return 410;
      }

      location @slowdown {
         limit_req zone=delay burst 5;
         ...
      }

      location / {
         ...
      }
   }

使用与“ location / {}”相同的信息填写“ location @slowdown {}”,例如,如果您将nginx用作反向代理,则使用proxy_pass。


我不确定我是否了解410部分?客户端是否实际看到http 410状态代码?
svrist 2012年

1
哇,这确实有效!非常漂亮的error_page把戏,+ 1!@svrist,请参阅serverfault.com/a/870170/110020,以获取有关此类工作原理以及原因的完整说明。
cnst
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.