Описана настройка следующей схемы защиты web-сервера:
ngx_http_limit_req
выявляет превышение лимита запросов;error.log
, куда Nginx пишет о превышениях;Имеется скрипт mitigator.py
(скачать)
для управления MITIGATOR, в частности, для временной блокировки IP-адреса
через MITIGATOR API. При необходимости скрипт можно доработать самостоятельно
для совершения любых других действий на MITIGATOR. Скрипт использует только
стандартные модули, работает с Python 2.7+ и Python 3.
Для запуска скрипта нужна учетная запись MITIGATOR и ID политики защиты
(например, 42
в URL .../policies/42
при заходе на MITIGATOR).
Также скрипт принимает IP и время блокировки в секундах.
Описание всех параметров печатается с ключом --help
(-h
).
Модуль ngx_http_limit_req
встроен в Nginx и позволяет ограничить количество
запросов в секунду (RPS) через конфигурацию Nginx.
Лимит на запросы относится к зоне (zone). Обычно зоной является IP клиента (то есть ограничиваются запросы от него) или URI на сайте (ограничиваются запросы к нему), но возможны и более сложные комбинации параметров запроса.
Зоны описываются в контексте http
. Опишем зону perip
(per IP), способную
отслеживать превышение лимита в 10 RPS для любого из 10 млн. IP-адресов.
Для этого в добавим в /etc/nginx.conf
через промежуточный файл строку:
mkdir -p /etc/nginx/nginx.conf.d
cat > /etc/nginx/conf.d/limit-req.conf <<'END'
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
END
Чтобы применить этот лимит к части сайта (контекст location
), то есть
не позволять обращаться с одного IP к странице более 10 раз в секунду,
используется следующая директива в /etc/nginx/sites-available/default
(если ограничиваются запросы для сайта по умолчанию):
server {
...
location / {
...
limit_req zone=perip burst=20 nodelay;
}
}
Здесь burst=20
позволяет всплески активности до 20 RPS (но в среднем
не более 10 RPS, как описано для зоны), а nodelay
означает, что запросы
сверх лимита сбрасываются, а не ожидают своей очереди (актуально при DDoS).
Обновим конфигурацию Nginx:
nginx -s reload
Когда лимит превышается, в error.log
появляются строки такого вида:
2019/01/11 09:27:12 [error] 155#155: *142 limiting requests, excess: 20.200 by zone "perip", client: 10.0.1.254, server: _, request: "GET / HTTP/1.1", host: "10.0.2.254"
Утилита fail2ban анализирует логи и при обнаружении в них заданных признаков выполняет действия по блокировке. Также fail2ban позволяет управлять списками заблокированных адресов.
Разместим скрипт блокировки:
install mitigator.py /usr/local/bin
Создадим новое действие fail2ban, которое будет вызывать скрипт:
cat >/etc/fail2ban/action.d/mitigator.conf <<'END'
[Definition]
actionban = \
/usr/local/bin/mitigator.py \
--server "<server>" \
--user "<user>" --password "<password>" \
--no-verify \
--policy <policy> \
tbl block --ip <ip> --time <bantime>
actionunban = \
/usr/local/bin/mitigator.py \
--server "<server>" \
--user "<user>" --password "<password>" \
--no-verify \
--policy <policy> \
tbl unblock --ip <ip>
END
Метки <server>
, <user>
, <password>
, <policy>
, <ip>
и <bantime>
нужно писать как есть — это параметры, которые при выполнении действия будут
автоматически заменены на правильные значения. Ключ --no-verify
нужен, если
MITIGATOR работает с самоподписанным сертификатом, и необходимо отключить его
проверку.
Создадим ограничение (jail), которое будет блокировать IP по записям
в error.log
:
cat >>/etc/fail2ban/jail.local <<END
[nginx-limit-req]
enabled = true
logpath = %(nginx_error_log)s
banaction = mitigator[server=mitigator.local, user=admin, password=admin, policy=42, bantime=600]
END
Модуль nginx-limit-req
поставляется с fail2ban и находит нужные строки.
В строке banaction=...
указываются актуальные параметры действия: сервер,
имя пользователя, пароль, ID политики. Для примера время блокировки 10 минут.
Перезагрузим правила fail2ban:
fail2ban-client reload
Трафик должен идти через MITIGATOR, должна быть включена общая защита и защита выбранной политики.
Имитировать атаку можно утилитой httperf
:
httperf --server 192.0.2.20 --num-conn 100
При успешной настройке через несколько секунд после запуска httperf
на сервере можно видеть, что fail2ban отработал:
$ fail2ban-client status nginx-limit-req
Status for the jail: nginx-limit-req
|- Filter
| |- Currently failed: 1
| |- Total failed: 65
| `- File list: /var/log/nginx/error.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 192.0.2.10
На MITIGATOR можно в карточке TBL политики проверить, что IP атакующего (192.0.2.10 в примере) находится в списке временно заблокированных.
Разблокировать IP можно как через MITIGATOR, так и через fail2ban:
fail2ban-client set nginx-limit-req unbanip 192.0.2.10
Если какой-то IP блокировать через fail2ban не нужно, а добавлять его в «белый список» (WL или TWL) на MITIGATOR нежелательно, можно игнорировать IP на уровне fail2ban:
fail2ban-client set nginx-limit-req addignoreip 192.0.2.10
Если fail2ban не отработал, ошибки можно наблюдать в его журнале:
tail -f /var/log/fail2ban/fail2ban.log