Ограничение скорости на NGINX с группировкой по IP-адресам

Мы пытаемся реализовать ограничение скорости запросов на нашем сервере Django NGINX. Я просмотрел несколько статей о том, как это сделать, например https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-http/.

Я вижу, что есть возможность установить ограничение скорости на IP-адрес, а также на конкретное "местоположение", то есть конечную точку API. Но возможно ли также сгруппировать несколько IP-адресов и применить совокупное ограничение скорости.

Например, у нас есть партнер с 5 IP-адресами, и мы хотим применить ограничение скорости, скажем, 10 rps на определенный API (/users/), но эти 10 rps являются совокупным ограничением для всех 5 IP-адресов, а НЕ 10 rps для КАЖДОГО IP-адреса. Я не смог найти в официальной документации NGINX или где-либо в Google, возможно ли это или нет.

Может ли кто-нибудь помочь мне, пожалуйста?

Ваши условия довольно сложны. Если, например, требования заключаются в применении фиксированной скорости 10 rps для группы из 5 IP адресов, и такой же скорости 10 rps для всех остальных IP, то это можно сделать с помощью одной limit_req зоны:

limit_req_zone $users_key zone=users:10m rate=10r/s;
geo $users_key {
    <ip1>    unique_customer_id;
    <ip2>    unique_customer_id;
    <ip3>    unique_customer_id;
    <ip4>    unique_customer_id;
    <ip5>    unique_customer_id;
    default  $remote_binary_addr;
}
server {
    ...
    location /users/ {
        limit_req zone=users;
        ... proxy request to the backend
    }
}

Только unique_customer_id может быть любой строкой, которая не будет равна любому возможному значению $binary_remote_addr (которое является двоично-упакованной строкой длиной 4 или 16 байт, поэтому достаточно использовать что-то простое, как 1; не используйте длинную строку id, чтобы минимизировать объем памяти разделяемой зоны).

Однако, поскольку ваши требования сложнее, включая разные тарифы для разных пользователей, нам нужно объявить много разных зон, чтобы достичь желаемого результата. Решение основано на том, что пустой ключ зоны не будет учитываться для ограничения запросов с помощью одного правила limit_req.

Вот возможное решение.

# zones definitions
limit_req_zone $unknown_customer zone=users_5:10m rate=5r/s;
limit_req_zone $customer_limited_group_1 zone=users_10:1m rate=10r/s;
limit_req_zone $customer_limited_group_2 zone=users_20:1m rate=20r/s;
limit_req_zone $unknown_customer zone=products_5:10m rate=5r/s;
limit_req_zone $customer_limited_group_1 zone=products_15:1m rate=15r/s;
limit_req_zone $customer_limited_group_2 zone=products_30:1m rate=30r/s;

# calculating keys
map $remote_addr $unknown_customer {
    123.123.123.123    '';
    45.45.45.45        '';
    65.65.65.65        '';
    # exclude any other IP for customers with different rates here, e.g.
    # 124.124.124.124  '';
    # 85.85.85.85      '';
    default            $binary_remote_addr;
}
map $remote_addr $customer_limited_group_1 {
    123.123.123.123    1;
    # here you can add another customers limited to 10/15 rps with another id, e.g.
    # 124.124.124.124  2;
    # the default value will be an empty string
}
map $remote_addr $customer_limited_group_2 {
    # this will be a key for a different zone;
    # value '1' does not interfere with the '1' from the previous map block in any way
    45.45.45.45        1;
    65.65.65.65        1;
    # another customers limited to 20/30 rps can be added here with different ids, e.g.
    # 85.85.85.85      2;
}

server {
    ...
    location /users/ {
        limit_req zone=users_5;
        limit_req zone=users_10;
        limit_req zone=users_20;
        ... proxy request to the backend
    }
    location /products/ {
        limit_req zone=products_5;
        limit_req zone=products_15;
        limit_req zone=products_30;
        ... proxy request to the backend
    }
}
Вернуться на верх