Nginx中具有geo和map的多个limit_req_zone

时间:2018-11-28 14:00:38

标签: nginx rate-limiting

在使用地图和地理模块的nginx中定义速率限制时,我需要帮助。

我已经定义了4种情况。这些情况中的每一种都应设置不同的速率限制:

geo $limited_ip {
            default                 0;
            1.1.1.1/24              1;
            2.2.2.2/24              2;
            3.3.3.3/24              3;
    }

我使用map模块将客户端的ip值传递给变量:

map $limited_ip $limited_ip_key {
            0 '';
            1 $binary_remote_addr;
            2 $binary_remote_addr;
            3 $binary_remote_addr;
    }

现在,我设置了4个限制区域。最后一个区域用于测试:

limit_req_zone $limited_ip_key zone=zone0:10m rate=100r/m;
limit_req_zone $limited_ip_key zone=zone1:10m rate=200r/m;
limit_req_zone $limited_ip_key zone=zone2:10m rate=500r/m;
limit_req_zone $limited_ip_key zone=zone3:10m rate=1r/m;

最后,我将限制应用于server {}块:

limit_req zone=zone0 burst=10 nodelay;
limit_req zone=zone1 burst=10 nodelay;
limit_req zone=zone2 burst=10 nodelay;
limit_req zone=zone3 burst=1 nodelay;

配置测试是否正常,我也重新加载了nginx。使用apache Bench工具(ab)锤击Nginx服务器,看来zone3对于任何源IP总是匹配的。为什么来自其他遮罩的IP由地理模块匹配区域3定义?

日志:

*2757 limiting requests, excess: 1.697 by zone "zone3", client: 3.3.3.3, server: my.domain.com, request: "HEAD / HTTP/1.0", host: "my.domain.com"
*29449 limiting requests, excess: 1.958 by zone "zone3", client: 2.2.2.2, server: my.domain.com, request: "HEAD / HTTP/2.0", host: "my.domain.com"

我发现的所有结果都大约定义了2个区域,我找不到包含更多区域的示例。也许不可能这样做吗?

谢谢

1 个答案:

答案 0 :(得分:0)

I was searching for the same configuration today and could not find an example with more than 2 zones too. So here is what in theory should work (I have not yet tried it out) :

geo $limited_ip {
            default                 0;
            1.1.1.1/24              1;
            2.2.2.2/24              2;
            3.3.3.3/24              3;
    }


map $limited_ip $limited_ip_key0 {
        0 $binary_remote_addr;
        1 '';
        2 '';
        3 '';
}

map $limited_ip $limited_ip_key1 {
        0 '';
        1 $binary_remote_addr;
        2 '';
        3 '';
}

map $limited_ip $limited_ip_key2 {
        0 '';
        1 '';
        2 $binary_remote_addr;
        3 '';
}

map $limited_ip $limited_ip_key3 {
        0 '';
        1 '';
        2 '';
        3 $binary_remote_addr;
}

limit_req_zone $limited_ip_key0 zone=zone0:10m rate=100r/m;
limit_req_zone $limited_ip_key1 zone=zone1:10m rate=200r/m;
limit_req_zone $limited_ip_key2 zone=zone2:10m rate=500r/m;
limit_req_zone $limited_ip_key3 zone=zone3:10m rate=1r/m;


limit_req zone=zone0 burst=10 nodelay;
limit_req zone=zone1 burst=10 nodelay;
limit_req zone=zone2 burst=10 nodelay;
limit_req zone=zone3 burst=1 nodelay;

Explanation:

a) if IP is none of the 3 defined then

$limited_ip_key0 = $binary_remote_addr
$limited_ip_key1 = ''
$limited_ip_key2 = ''
$limited_ip_key3 = ''

thus the zone0 will be matched only, and the rate limit of 100r/m will be applied

b) if IP is 1.1.1.1/24

$limited_ip_key0 = ''
$limited_ip_key1 = $binary_remote_addr
$limited_ip_key2 = ''
$limited_ip_key3 = ''

thus the zone1 will be matched only, and the rate limit of 200r/m will be applied

c) if IP is 2.2.2.2/24

$limited_ip_key0 = ''
$limited_ip_key1 = ''
$limited_ip_key2 = $binary_remote_addr
$limited_ip_key3 = ''

thus the zone2 will be matched only, and the rate limit of 500r/m will be applied

d) if IP is 3.3.3.3/24

$limited_ip_key0 = ''
$limited_ip_key1 = ''
$limited_ip_key2 = ''
$limited_ip_key3 = $binary_remote_addr

thus the zone3 will be matched only, and the rate limit of 1r/m will be applied

Here is a php script to automatically create the required zones so you can be able to administer more zones and ips easily

<?php

list($mapString, $zonesString, $zonesArray) = createZones(
    "myendpoint",
    array(
        "default"=>array(
            "zoneSize"=>"10m",
            "rate"=>"10r/s",
            "burst"=>"100",
            "options"=>""
        ),
        "zone1"=>array(
            "ips"=>array(
                "11.11.11.11/32",
                "1.1.1.1/24"
            ),
            "zoneSize"=>"10m",
            "rate"=>"10r/s",
            "burst"=>"100",
            "options"=>""
        ),
         "zone2"=>array(
            "ips"=>array(
                "22.22.22.22/32",
                "2.2.2.2/24"
            ),
            "zoneSize"=>"10m",
            "rate"=>"20r/s",
            "burst"=>"100",
            "options"=>"nodelay"
        ),
        "zone3"=>array(
            "ips"=>array(
                "33.33.33.33/32",
                "3.3.3.3/24"
            ),
            "zoneSize"=>"10m",
            "rate"=>"30r/s",
            "burst"=>"100",
            "options"=>""
        ),
    )
);

echo "# Define the ips and maps\n$mapString\n#Define the zones\n$zonesString\n\n";

echo "\t# limit directives to be placed inside the location section\n";
foreach ($zonesArray as $zoneName=>$zoneString) {
    echo   "\t$zoneString\n";
}



function createZones($endpointPrefix,$zones) {

    $mapString0='geo $'.$endpointPrefix.' {'."\n\t\tdefault\t0;";

    $mapString1='';
    $zonesString='';
    $zonesArray=array();

    $zoneNum=0;
    foreach ($zones as $zoneName=>$params) {

        $zoneName=strtolower($zoneName);
        if ($zoneName!='default') {
            $zoneNum++;
            foreach($params['ips'] as $ip) {
                $mapString0.="\n\t\t$ip\t$zoneNum;";
            }
        }

    }
    $mapString0.="\n}\n";

    $zoneNumTotal=$zoneNum;
    // now that we now the total zones we can create the maps
    $zoneNum=0;
    foreach ($zones as $zoneName=>$params) {

        $zoneName=strtolower($zoneName);
        $mapString1.='map $'.$endpointPrefix.' $'.$endpointPrefix.'_key_'.$zoneName.' {';

        for($zoneNumTemp=0;$zoneNumTemp<=$zoneNumTotal;$zoneNumTemp++) {
            if ($zoneNum==$zoneNumTemp) {
                $mapString1.="\n\t\t$zoneNumTemp\t\$binary_remote_addr;";
            } else {
                $mapString1.="\n\t\t$zoneNumTemp\t'';";
            }
        }
        $mapString1.="\n}\n\n";

        $zoneNum++;
    }

    // now create the actual zones string
    foreach ($zones as $zoneName=>$params) {
        $zoneName=strtolower($zoneName);
        $zonesString.="\n";
        if ( isset( $params['ips']) ) {
            foreach ($params['ips'] as $ip) {
                $zonesString .= "# $ip\n";
            }
        }
        $zonesString.='limit_req_zone $'.$endpointPrefix.'_key_'.$zoneName.' zone='.$endpointPrefix.'_'.$zoneName.':'.$params['zoneSize'].' rate='.$params['rate'].';';
        $zonesString.="\n";
    }

    // now create the limits that should be applied inside the location sections

    foreach ($zones as $zoneName=>$params) {
        $zoneName=strtolower($zoneName);
        $zonesArray[$zoneName]='limit_req zone='.$endpointPrefix.'_'.$zoneName;

        if ($params['burst']) {
            $zonesArray[$zoneName].=' burst='.$params['burst'];
        }
        if ($params['options']) {
            $zonesArray[$zoneName].=' '.$params['options'];
        }

    }

    return array($mapString0."\n".$mapString1, $zonesString,$zonesArray);
}

When executed the above script produces:

# Define the ips and maps
geo $myendpoint {
                default 0;
                11.11.11.11/32  1;
                1.1.1.1/24      1;
                22.22.22.22/32  2;
                2.2.2.2/24      2;
                33.33.33.33/32  3;
                3.3.3.3/24      3;
}

map $myendpoint $myendpoint_key_default {
                0       $binary_remote_addr;
                1       '';
                2       '';
                3       '';
}

map $myendpoint $myendpoint_key_zone1 {
                0       '';
                1       $binary_remote_addr;
                2       '';
                3       '';
}

map $myendpoint $myendpoint_key_zone2 {
                0       '';
                1       '';
                2       $binary_remote_addr;
                3       '';
}

map $myendpoint $myendpoint_key_zone3 {
                0       '';
                1       '';
                2       '';
                3       $binary_remote_addr;
}


#Define the zones

limit_req_zone $myendpoint_key_default zone=myendpoint_default:10m rate=10r/s;

# 11.11.11.11/32
# 1.1.1.1/24
limit_req_zone $myendpoint_key_zone1 zone=myendpoint_zone1:10m rate=10r/s;

# 22.22.22.22/32
# 2.2.2.2/24
limit_req_zone $myendpoint_key_zone2 zone=myendpoint_zone2:10m rate=20r/s;

# 33.33.33.33/32
# 3.3.3.3/24
limit_req_zone $myendpoint_key_zone3 zone=myendpoint_zone3:10m rate=30r/s;


        # limit directives to be placed inside the location section
        limit_req zone=myendpoint_default burst=100
        limit_req zone=myendpoint_zone1 burst=100
        limit_req zone=myendpoint_zone2 burst=100 nodelay
        limit_req zone=myendpoint_zone3 burst=100