В nginx c помощью директивы limit_req_zone мы можем ограничивать кол-во запросов к нашим частям сайта, тем самым предотвращая атаки на отказ в обслуживании.
Самы базовый вариант настройки сводится к добавлению вот такого параметра в конфиг
limit_req_zone $binary_remote_addr zone=defender:10m rate=200r/s;
И включении в нужном location:
limit_req zone=defender burst=1 nodelay;
Что тут происходит?
$binary_remote_addr — это зарезервированная переменная nginx, в которой будет хранится IP адрес клиента.
zone=defender:10m — defender это произвольное имя зоны, 10м — это 10 мегабайт памяти на хранение данных в ней.
rate=200r/s — ограничиваем кол-во запрос в секунду для одного клиента. В данном случае клиент может отправить 200 запросов в секунду.
limit_req zone=defender burst=1 nodelay — указываем какую зону используем, параметр burst позволяет установить кол-во запросов как-бы сверху нашего лимита. Т.е. если бы у нас burst был 10, то первые 10 запросов клиента прошли бы успешно и только начиная со 11 включилось ограничение заданное в зоне. Таким образ клиент мог бы отправить 210 запросов.
Таким образом, параметр burst
позволяет управлять начальным «всплеском» запросов, что может быть полезно для обработки временных пиков активности без сразу же блокировки запросов.
nodelay — говорит отдавать статус (503 по умолчанию) немедленно, когда исчерпан лимит.
Вроде неплохо, но есть одни нюнас, например для посещения сайта 200r/s должно хватить за глаза. А вот если мы заходим в admin панель WP, этого уже не хватает.
Сразу же приходит на ум задание «белого списка» с и IP адресами, которых бы данное ограничение не касалось бы.
Давайте создадим вот такой файл:
geo $limited {
default 1;
<ip_address> 0;
}
map $limited $limit {
1 $binary_remote_addr;
0 "";
}
limit_req_zone $limit zone=defender:10m rate=200r/s;
Я положил его в /etc/nginx/conf.d/, он автоматически подключится в нашу конфигурацию.
Тут мы используем модуль geo, который нам позволяет задать не просто IP адрес, но и под сеть из адресов. Присваиваем это все переменной $limited и создаем маппинг, где дефолтное значение равно 1, добавленные нами все IP адреса или под сети будет иметь значение 0.
Значение 0 в данном случае разрешающее, т.е. то, что имеет значение 0 не будет попадать под ограничение лимитов зоны.
Далее мы создаем маппинг на основе $limited переменной (она родилась в модуле geo) и записываем это в новую переменную $limit. Если на предыдущем шаге, в модуле geo мы получили 0, то в новом маппинге 0 будет соответствовать пустая строка, а 1 будет соответствовать адрес клиента.
Получается, что если мы имеем пустую строку, то у нас нет IP адреса к которому можно было бы применить ограничения зоны, следовательно он пропускается.
На стороне nginx мы создали список белых адресов, теперь давайте включим блокировку на основе этих данных в fail2ban.
В /etc/fail2ban/jail.conf добавляем:
[nginx-limit-req]
enabled = true
port = http,https
logpath = %(nginx_error_log)s
findetime = 600
bantime = 600
maxretry = 5
Перезапускаем и для начала исключим IP адрес нашей машины из белого списка и запустим тест wrk.
Мы успешно забанили сами себя, теперь при попытке открыть сайт, мы увидим что-то вроде этого:
Разбанить можно так:
fail2ban-client set nginx-limit-req unbanip <ip_address>
Возвращаем себя в whitelist и перезапускаем wrk. Никаких проблем с доступность быть не должно.