Одной из наиболее полезных, но часто неправильно понимаемых и неправильно настраиваемых функций NGINX является ограничение скорости . Он позволяет ограничить количество HTTP-запросов, которые пользователь может сделать за определенный период времени. Запрос может быть таким же простым, как GET запрос на домашнюю страницу веб-сайта или POSTзапрос в форме входа.
Ограничение скорости можно использовать в целях безопасности, например, для замедления атак с подбором пароля методом перебора. Он может помочь защититься от DDoS-атак , ограничивая частоту входящих запросов до значения, типичного для реальных пользователей, и (с ведением журнала) идентифицировать целевые URL-адреса. В более общем плане он используется для защиты вышестоящих серверов приложений от одновременной перегрузки слишком большим количеством пользовательских запросов.
В этом блоге мы рассмотрим основы ограничения скорости с помощью NGINX, а также более сложные настройки. Ограничение скорости работает таким же образом в NGINX Plus.
Чтобы узнать больше об ограничении скорости с помощью NGINX, посмотрите наш вебинар по запросу .
NGINX Plus R16 и более поздние версии поддерживают «глобальное ограничение скорости»: экземпляры NGINX Plus в кластере применяют согласованное ограничение скорости для входящих запросов независимо от того, на какой экземпляр в кластере поступает запрос. (Обмен состоянием в кластере доступен и для других функций NGINX Plus.) Подробности см. в нашем блоге и в Руководстве администратора NGINX Plus .
Как работает ограничение скорости NGINX
Ограничение скорости NGINX использует алгоритм дырявого ведра , который широко используется в телекоммуникациях и компьютерных сетях с коммутацией пакетов для борьбы с пульсацией при ограничении полосы пропускания. Аналогия с ведром, в который вода наливается сверху, а вытекает снизу; если скорость наливания воды превышает скорость ее утечки, ведро переливается. С точки зрения обработки запросов вода представляет собой запросы от клиентов, а корзина представляет собой очередь, в которой запросы ожидают обработки в соответствии с алгоритмом планирования «первым пришел — первым обслужен» (FIFO). Утечка воды представляет собой запросы, выходящие из буфера для обработки сервером, а переполнение представляет собой запросы, которые отбрасываются и никогда не обслуживаются.
Ограничение скорости настраивается с помощью двух основных директив limit_req_zoneи limit_req, как в этом примере:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {location /login/ {
limit_req zone=mylimit;
proxy_pass http://my_upstream;
}
}
Директива limit_req_zoneопределяет параметры ограничения скорости и limit_reqвключает ограничение скорости в контексте, в котором она появляется (в примере для всех запросов к /login/ ).
Директива limit_req_zoneобычно определяется в httpблоке, что делает ее доступной для использования в нескольких контекстах. Он принимает следующие три параметра:
Ключ — определяет характеристику запроса, к которой применяется ограничение. В примере это переменная NGINX $binary_remote_addr, которая содержит двоичное представление IP-адреса клиента. Это означает, что мы ограничиваем каждый уникальный IP-адрес частотой запросов, определенной третьим параметром. (Мы используем эту переменную, поскольку она занимает меньше места, чем строковое представление IP-адреса клиента $remote_addr).
Зона — определяет зону общей памяти, используемую для хранения состояния каждого IP-адреса и частоты доступа к URL-адресу с ограниченным запросом. Хранение информации в общей памяти означает, что она может совместно использоваться рабочими процессами NGINX. Определение состоит из двух частей: имени зоны, определяемого ключевым словом zone=, и размера, следующего за двоеточием. Информация о состоянии около 16 000 IP-адресов занимает 1 мегабайт, поэтому наша зона может хранить около 160 000 адресов.
Если хранилище исчерпано, когда NGINX необходимо добавить новую запись, он удаляет самую старую запись. Если освободившегося места по-прежнему недостаточно для размещения новой записи, NGINX возвращает код состояния . Кроме того, чтобы предотвратить исчерпание памяти, каждый раз, когда NGINX создает новую запись, он удаляет до двух записей, которые не использовались в течение предыдущих 60 секунд.503 (Service Temporarily Unavailable)
Скорость – устанавливает максимальную скорость запроса. В примере скорость не может превышать 10 запросов в секунду. NGINX фактически отслеживает запросы с точностью до миллисекунды, поэтому этот предел соответствует 1 запросу каждые 100 миллисекунд (мс). Поскольку мы не допускаем всплески (см. следующий раздел ), это означает, что запрос отклоняется, если он поступает менее чем через 100 мс после предыдущего разрешенного.
Директива limit_req_zone устанавливает параметры ограничения скорости и зоны общей памяти, но фактически не ограничивает скорость запросов. Для этого вам необходимо применить ограничение к конкретному блоку locationили serverблоку, включив limit_reqтуда директиву. В этом примере мы ограничиваем скорость запросов к /login/ .
Таким образом, теперь каждый уникальный IP-адрес ограничен 10 запросами в секунду для /login/ — или, точнее, не может сделать запрос на этот URL-адрес в течение 100 мс после предыдущего.
Что если мы получим 2 запроса с разницей в 100 мс? По второму запросу NGINX возвращает 503клиенту код состояния. Вероятно, это не то, что нам нужно, потому что приложения имеют тенденцию быть пульсирующими по своей природе. Вместо этого мы хотим буферизовать любые лишние запросы и своевременно обслуживать их. Здесь мы используем burstпараметр to limit_req, как в этой обновленной конфигурации:
location /login/ {limit_req zone=mylimit burst=20;
proxy_pass http://my_upstream;
}
Параметр burst определяет, сколько запросов клиент может сделать сверх скорости, указанной в зоне (в нашем примере зоны mylimit ограничение скорости составляет 10 запросов в секунду или 1 каждые 100 мс). Запрос, пришедший раньше, чем через 100 мс после предыдущего, помещается в очередь, и здесь мы устанавливаем размер очереди равным 20.
Это означает, что если с данного IP-адреса одновременно поступает 21 запрос, NGINX немедленно пересылает первый запрос группе вышестоящих серверов и помещает оставшиеся 20 в очередь. Затем он пересылает запрос в очередь каждые 100 мс и возвращает 503клиенту только в том случае, если входящий запрос приводит к тому, что количество запросов в очереди превышает 20.
Конфигурация, burst обеспечивающая плавный поток трафика, но не очень практична, поскольку может сделать ваш сайт медленным. В нашем примере 20-й пакет в очереди ожидает пересылки 2 секунды, после чего ответ на него может оказаться бесполезен для клиента. Чтобы решить эту ситуацию, добавьте nodelayпараметр вместе с burstпараметром:
location /login/ { limit_req zone=mylimit burst=20 nodelay; proxy_pass http://my_upstream; }
С помощью этого nodelayпараметра NGINX по-прежнему распределяет слоты в очереди в соответствии с burstпараметром и налагает настроенное ограничение скорости, но не путем разделения пересылки запросов в очереди. Вместо этого, когда запрос поступает «слишком рано», NGINX немедленно пересылает его, пока в очереди для него есть свободный слот. Он помечает этот слот как «занятый» и не освобождает его для использования другим запросом до тех пор, пока не пройдет соответствующее время (в нашем примере — через 100 мс).
Предположим, как и раньше, что очередь из 20 слотов пуста и с данного IP-адреса одновременно поступает 21 запрос. NGINX немедленно пересылает все 21 запрос и помечает 20 слотов в очереди как занятые, а затем освобождает 1 слот каждые 100 мс. (Если бы вместо этого было 25 запросов, NGINX немедленно переправил бы 21 из них, пометил бы 20 слотов как занятые и отклонил 4 запроса со статусом 503.)
Теперь предположим, что через 101 мс после пересылки первого набора запросов одновременно поступают еще 20 запросов. Освободился только 1 слот в очереди, поэтому NGINX пересылает 1 запрос и отклоняет остальные 19 со статусом 503. Если вместо этого до поступления 20 новых запросов пройдет 501 мс, 5 слотов будут свободны, поэтому NGINX немедленно перенаправит 5 запросов и отклонит 15.
Эффект эквивалентен ограничению скорости в 10 запросов в секунду. Эта nodelayопция полезна, если вы хотите установить ограничение скорости, не ограничивая разрешенный интервал между запросами.
Примечание. В большинстве случаев мы рекомендуем включать в директиву параметры burstи .nodelaylimit_req
С помощью NGINX Plus R17 или NGINX Open Source 1.15.7 вы можете настроить NGINX так, чтобы он разрешал пакет запросов для соответствия типичному шаблону запросов веб-браузера, а затем ограничивал дополнительные чрезмерные запросы до точки, после которой дополнительные чрезмерные запросы отклоняются. Двухступенчатое ограничение скорости включается параметром delayдирективы limit_req.
Чтобы проиллюстрировать двухэтапное ограничение скорости, здесь мы настраиваем NGINX для защиты веб-сайта, устанавливая ограничение скорости в 5 запросов в секунду (r/s). Веб-сайт обычно имеет 4–6 ресурсов на страницу и не более 12 ресурсов. Конфигурация допускает пакетную обработку до 12 запросов, первые 8 из которых обрабатываются без задержки. Задержка добавляется после 8 чрезмерных запросов, чтобы обеспечить соблюдение ограничения в 5 об/с. После 12 чрезмерных запросов любые дальнейшие запросы отклоняются.
limit_req_zone $binary_remote_addr zone=ip:10m rate=5r/s;server {
listen 80;
location / {
limit_req zone=ip burst=12 delay=8;
proxy_pass http://website;
}
}
Параметр delayопределяет точку, в которой в пределах размера пакета избыточные запросы регулируются (задерживаются) для соответствия определенному пределу скорости. При такой конфигурации клиент, который создает непрерывный поток запросов со скоростью 8 об/с, ведет себя следующим образом.
Иллюстрация поведения, ограничивающего скорость, сrate=5r/s burst=12 delay=8
Первые 8 запросов (значение delay) проксируются NGINX Plus без задержки. Следующие 4 запроса ( burst - delay) задерживаются, чтобы не была превышена заданная скорость 5 об/с. Следующие 3 запроса отклоняются, поскольку общий размер пакета превышен. Последующие запросы задерживаются.
Примеры расширенной конфигурации
Комбинируя базовое ограничение скорости с другими функциями NGINX, вы можете реализовать более тонкое ограничение трафика.
В этом примере показано, как установить ограничение скорости на запросы от всех, кто не находится в «белом списке».
geo $limit {default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
server {
location / {
limit_req zone=req_zone burst=10 nodelay;
# ...
}
}
В этом примере используются обе директивы geoи map. Блок geoприсваивает значение 0IP $limit-адресам в белом списке и 1всем остальным. Затем мы используем карту для перевода этих значений в ключ, например:
Если $limitесть 0, $limit_keyустанавливается пустая строка
Если $limitустановлено значение IP-адрес клиента в двоичном формате 1.$limit_key
Если объединить эти два значения, $limit_keyто для IP-адресов из разрешенного списка будет задана пустая строка, а в противном случае — IP-адрес клиента. Если первый параметр каталога limit_req_zone(ключ) представляет собой пустую строку, ограничение не применяется, поэтому IP-адреса из белого списка (в подсетях 10.0.0.0/8 и 192.168.0.0/24) не ограничиваются. Все остальные IP-адреса ограничены 5 запросами в секунду.
Директива limit_req применяет ограничение к местоположению / и разрешает пакеты до 10 пакетов сверх настроенного лимита без задержки при пересылке.
Включение нескольких limit_reqдиректив в место
Вы можете включить несколько limit_reqдиректив в одном месте. Применяются все ограничения, соответствующие данному запросу, то есть используется наиболее строгий из них. Например, если задержку налагают более чем одна директива, используется самая длинная задержка. Аналогично, запросы отклоняются, если это является следствием какой-либо директивы, даже если другие директивы разрешают их прохождение.
Расширяя предыдущий пример, мы можем применить ограничение скорости к IP-адресам в белом списке:
http {# ...
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;
server {
# ...
location / {
limit_req zone=req_zone burst=10 nodelay;
limit_req zone=req_zone_wl burst=20 nodelay;
# ...
}
}
}
IP-адреса в белом списке не соответствуют первому пределу скорости ( req_zone ), но соответствуют второму ( req_zone_wl ) и поэтому ограничены 15 запросами в секунду. IP-адреса, не включенные в белый список, соответствуют обоим ограничениям скорости, поэтому применяется более строгий: 5 запросов в секунду.
Настройка связанных функций
По умолчанию NGINX регистрирует запросы, которые задерживаются или отбрасываются из-за ограничения скорости, как в этом примере:
2015/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: nginx.com, request: "GET / HTTP/1.0", host: "nginx.com"
Поля в записи журнала включают в себя:
2015/06/13 04:20:00 – Дата и время создания записи в журнале.
[error] - Уровень опасности
120315#0 – Идентификатор процесса и идентификатор потока рабочего NGINX, разделенные #знаком
*32086 – Идентификатор прокси-соединения с ограничением скорости.
limiting requests – Индикатор того, что запись журнала фиксирует ограничение скорости.
excess – Количество запросов в миллисекунду сверх настроенной скорости, которую представляет этот запрос.
zone – Зона, определяющая установленный лимит скорости
client – IP-адрес клиента, делающего запрос
server – IP-адрес или имя хоста сервера
request – Фактический HTTP-запрос, сделанный клиентом
host – Значение HostHTTP-заголовка
По умолчанию логи NGINX отклоняют запросы на errorуровне, как показано [error]в примере выше. (Он регистрирует отложенные запросы на один уровень ниже, поэтому warnпо умолчанию.) Чтобы изменить уровень журналирования, используйте директиву limit_req_log_level. Здесь мы устанавливаем отклоненные запросы на вход на warnуровне:
location /login/ {limit_req zone=mylimit burst=20 nodelay;
limit_req_log_level warn;
proxy_pass http://my_upstream;
}
Код ошибки отправлен клиенту
По умолчанию NGINX отвечает кодом состояния 503 ( Service Temporarily Unavailable) , когда клиент превышает предел скорости. Используйте limit_req_statusдирективу, чтобы установить другой код состояния ( 444в этом примере):
location /login/ {limit_req zone=mylimit burst=20 nodelay;
limit_req_status 444;
}
Отказ во всех запросах к определенному местоположению
Если вы хотите запретить все запросы для определенного URL-адреса, а не просто ограничить их, настройте locationдля него блокировку и включите директиву:deny all
location /foo.php {deny all;
}
Мы рассмотрели многие функции ограничения скорости, которые предлагают NGINX и NGINX Plus, включая настройку скорости запросов для разных мест в HTTP-запросах и настройку дополнительных функций для ограничения скорости, таких как параметры burstи nodelay. Мы также рассмотрели расширенную настройку для применения различных ограничений для IP-адресов клиентов, включенных в разрешенные и запрещенные списки, и объяснили, как регистрировать отклоненные и отложенные запросы.
[ 1 Из 2 Нашли полезным ]