Как настроить rate limit в Nginx

Здрасти мои хорошие!

Рассмотрим ситуацию — злодеи устроили нагрузочное тестирование и натравили на твой ламповый стартап какой-нибудь «яндекс-танк».

 читать первым в телеграм    читать первым в макс

Подключить DDosGuard или Кратор!

Эт я пеню, ценник на такие услуги — моё почтение, да и по функционалу там очень даже всё урезано. А cloudflare по регламенту компании запрещен. Замкнутый круг.

Ну так что делать-то?

Роман Шубин
Роман Шубин
CEO & CTO, Главред в «Цифровой улей»
Задать вопрос
Да блядь nginx затюнить и отбить всю эту хуйню-муйню. Понятно дело оно особо при дидосах не спасет, но со сканерами и «танками» заебись справится.

В nginx есть параметр limit_req_zone, он-то нам и пригодится. Этот параметр ограничивает количество одновременных запросов с одного айпишника.

Поехали настраивать.

Создаем файл /etc/nginx/conf.d/assholes.conf:

geo $limited {
    default 1;
    192.168.1.1 0;  # Этот IP не лимитируется
    10.0.0.0/24 0;  # 10.0.0.0/24 тоже без ограничений
}

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

limit_req_zone $limit zone=bashdays:20m rate=200r/s;

limit_req_zone — ключ для отслеживания запросов.

$binary_remote_addr — системная переменная nginx, внутри хранит IP-адрес клиента.

zone=bashdays:20m — произвольное имя зоны, 20m это объём памяти в мегабайтах для хранения данных. В этой зоне хранятся данные о количестве запросов от каждого уникального клиента.

rate=200r/s — ограничение равное 200 запросов в секунду с одного IP-адреса клиента.

Для рейта есть такая табличка:

Сценарийrateburst
API с высокой нагрузкой100r/s200
Средний веб-сайт50r/s100
Анти-DDoS защита10r/s20
CDN или кеширующий прокси500r/s1000

Тестируем так:

ab -n 1000 -c 50 http://bashdays.ru/
wrk -t4 -c100 -d10s http://bashdays.ru/

И смотрим нет ли ошибок 503 Слишком дохуя запросов и корректно ли отрабатывает лимит.

Сука! А как рассчитать эту 20m памяти? Щаа…

Пардон, хуйню сморозил, давай на котиках:

Пример расчёта для 20m (20 мегабайт = 20 × 1024 × 1024 = 20 971 520 байт):

20 971 520 / 128 = 163 840

То есть, зона 20m может хранить лимиты примерно для 163 тысяч уникальных клиентов одновременно.

А откуда взялось 128?

Как я написал выше nginx хранит каждого уникального клиента (ключ $binary_remote_addr или другое значение) в зоне, используя примерно 128 байт на запись.

Есть такая табличка:

КлиентовРазмер зоны
< 20 0004m
~80 00010m
~160 00020m
500 000+64m

Да, еще можно настроить burst для резких пиков. burst позволяет временно превышать rate, прежде чем включится жёсткий лимит.

Например:

limit_req zone=bashdays burst=400 nodelay;

Клиенту разрешается до 400 мгновенных запросов, а затем он попадает в ограничение 200r/s.

nodelay означает, что первые 400 запросов проходят сразу, а потом начинается строгий лимит.

Теперь по маппингам и гео хуйне.

В первом блоке про гео:

  1. Все пользователи получат $limited = 1 (по умолчанию).
  2. Те, кто с 192.168.1.1 или из 10.0.0.0/24, получат $limited = 0 и не будут ограничены.

Во втором блоке про маппинг:

  1. Если $limited = 1, то $limit = $binary_remote_addr (IP-адрес клиента).
  2. Если $limited = 0, то $limit = "", и клиент не попадает в limit_req_zone.

Короче делаем что-то вроде белого списка и кто в него не входит — идёт нахуй! Надеюсь понятно объяснил.

Теперь чтобы вся эта поебота заработала, нужно прописать в нужный локейшен:

location / {
    limit_req zone=bashdays burst=10 nodelay;
    try_files $uri $uri/ /index.php?$args;
}

Про burst и nodelay выше уже рассказывал.

Ну а чтобы отдавать нужный статус при достижении лимита, делаем:

location / {
    limit_req_status 429;
    limit_req zone=bashdays burst=10 nodelay;
    try_files $uri $uri/ /index.php?$args;
}

Возвращаем 429 Слишком дохуя запросов, вместо стандартного 503.

Роман Шубин
Роман Шубин
CEO & CTO, Главред в «Цифровой улей»
Задать вопрос
Бездумно это настраивать не советую, могут пострадать обычные пользователи, а вот если всё вдумчиво сделать — спасешь свой стартап от злых писек.

Ну и сыпь в комменты свои варианты!

Комментарии