Случилась на днях, как всегда, не в самый подходящий момент, DDoS-атака на один из сайтов, размещенных на моем сервере. DDoS-атаки бывают разные, в этот раз злоумышленники запустили HTTP флуд.
Флуд был не столько тяжелый по трафику, сколько интенсивный по количеству запросов. Причем, как назло, запросов не однотипных, а постоянно меняющихся. Все, что у меня на тот момент было из софтовых средств защиты, не могло эффективно справиться с таким флудом, поэтому пришлось использовать железные решения.
Железные решения я считаю правильным выбором, но доступны они не всем и не всегда, а многие атаки, как показала моя практика, успешно отбиваются правильным использованием доступных программных средств. К тому же, захотелось немного поэкспериментировать.
Модуль ngx_http_limit_req_module
После детального изучения возможностей давно и вполне успешно используемой мною связки nginx + Apache и документации к nginx родилось решение на базе модуля ngx_http_limit_req_module.
Модуль ngx_http_limit_req_module (0.7.21) позволяет ограничить скорость обработки запросов по заданному ключу или, как частный случай, скорость обработки запросов, поступающих с одного IP-адреса. Ограничение обеспечивается с помощью метода “leaky bucket”. Пример конфигурации
Задаёт зону разделяемой памяти (zone) и максимальный размер всплеска запросов (burst). Если скорость поступления запросов превышает описанную в зоне, то их обработка задерживается так, чтобы запросы обрабатывались с заданной скоростью. Избыточные запросы задерживаются до тех пор, пока их число не превысит максимальный размер всплеска. При превышении запрос завершается с ошибкой 503 (Service Temporarily Unavailable). По умолчанию максимальный размер всплеска равен нулю. Например, директивы limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server { location /search/ { limit_req zone=one burst=5; }
позволяют в среднем не более 1 запроса в секунду со всплесками не более 5 запросов.
Если же избыточные запросы в пределах лимита всплесков задерживать не требуется, то следует использовать параметр nodelay: limit_req zone=one burst=5 nodelay;
Задаёт желаемый уровень записи в лог случаев отказа в обработке запросов при превышении скорости и случаев задержек при обработке запроса. Задержки записываются в лог с уровнем на единицу меньшим, чем отказы, например, если указано “limit_req_log_level notice”, то задержки будут записываться в лог на уровне info. синтаксис: limit_req_status код; умолчание: limit_req_status 503; контекст: http, server, location
Эта директива появилась в версии 1.3.15.
Позволяет переопределить код ответа, используемый при отклонении запросов. синтаксис: limit_req_zone $переменная zone=название:размер rate=скорость; умолчание: — контекст: http
Задаёт параметры зоны разделяемой памяти, которая хранит состояние для разных значений ключа. Состояние в частности хранит текущее число избыточных запросов. Ключом является любое непустое значение заданной переменной (пустые значения не учитываются). Пример использования: limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
В данном случае состояния хранятся в зоне “one” размером 10 мегабайт, и средняя скорость обработки запросов для этой зоны не может превышать 1 запроса в секунду.
В качестве ключа используется IP-адрес клиента. Обратите внимание, что вместо переменной $remote_addr используется переменная $binary_remote_addr, позволяющая уменьшить размер состояния до 64 байт. В зоне размером 1 мегабайт может разместиться около 16 тысяч состояний размером 64 байта. При переполнении зоны в ответ на последующие запросы сервер будет возвращать ошибку 503 (Service Temporarily Unavailable).
Скорость задаётся в запросах в секунду (r/s). Если же нужна скорость меньше одного запроса в секунду, то она задаётся в запросах в минуту (r/m), например, ползапроса в секунду — это 30r/m.
Этот модуль позволяет ограничить число запросов для заданной сессии или с одного адреса. Я не буду детально описывать его возможности, документация доступна всем желающим.
Что я сделал
Я проверил, собран ли nginx с модулем ngx_http_limit_req_module и внес в конфигурационный файл сервера следующие строки:
http { # Директива описывает зону, в которой хранятся состояния сессий. # Значения сессий определяется заданной переменной.
# В данном случае состояния сессий хранятся в зоне "one" размером # 10 мегабайт и средняя скорость запросов для этой зоны не может # более 2 запросов в секунду.
# Данный случай частный лично для моей # ситуации и подбирается индивидуально.
...
# Атакуемый домен. server {
...
location / { # Директива задаёт зону (zone) и максимально возможные всплески # запросов (burst). Если скорость запросов превышает описанную # в зоне, то их обработка запроса задерживается так, чтобы запросы # обрабатывались с заданной скоростью. Избыточные запросы задерживаются # до тех пор, пока их число не превысит # заданное число всплесков. В этом случае запрос завершается кодом # "Service unavailable" (503).
limit_req zone=one burst=4; }
* This source code was highlighted with Source Code Highlighter.
* пример конфигурации и пояснения со страницы документации по модулю
Что я получил
Все боты, которые с неистовой частотой «долбили» сервер, начали получать в ответ http- ошибку 503. А выбрать с логов IP ботов например так: