Правила фильтрации#
Ограничения по частоте и размеру сообщений#
Одним из свойств, которыми обладает SOWA, является ограничение по частоте и размеру сообщений.
Модуль limit_req#
В Platform V SOWA используется модуль limit_req, который предназначен для ограничения скорости обработки запросов за определенный период времени. Ограничение скорости может использоваться в целях безопасности, например, для замедления атак с использованием паролей. Он может помочь защитить от DDoS-атак, ограничив частоту входящих запросов значением, типичным для реальных пользователей, и (с ведением журнала) идентифицировать целевые URL-адреса. В более общем смысле он используется для защиты серверов приложений от чрезмерного количества одновременных запросов пользователей.
Стоит учесть важный момент — в сервисе SOWA нельзя указать несколько модулей limit_req.
Для того чтобы продемонстрировать работу модуля, в каталоге proxies создадим файл limit_req.yml и наполним его содержимым:
version: 2.0.3
profile: limit_req
profile_version: XXX
system:
wrk_count: 4
conn_count: 4096
optional:
log_level: debug
service:
service_http_proxy:
- id: service
listen:
- port: 8013
name: service
hostname:
- localhost
url: ^\/service$
allowed_queries:
- method: post
upstream_group_id: upstream_v1
upstream_groups:
- id: upstream_v1
servers:
- server: 127.0.0.1:18013
weight: 5
optional:
limit_req:
enabled: true
target: service
rate: 30
burst: 0
status_code: 429
rate_interval: m
size: 10m
В секции optional мы объявили группу параметров limit_req.
Обязательными параметрами являются:
enabled— вкл./откл. модуля limit_req;rate— в значении параметра задается предел количества запросов. В данном примере это 0,5 запроса в секунду (Requests Per Second (RPS) — количество запросов за одну секунду). NGINX фактически отслеживает запросы с точностью до миллисекунды, поэтому этот предел соответствует 1 запросу каждые 2000 миллисекунд. Это означает, что запрос отклоняется, если он поступает менее чем через 100 миллисекунд после предыдущего разрешенного;target— в значении параметра определяется ключ, по которому происходит ограничение:src_ip— ограничение происходит по количеству запросов для каждого ip адреса клиента отдельно;srv_port— ограничение происходит для каждого порта сервиса отдельно;service— ограничение происходит по количеству запросов для каждого сервиса отдельно и для всего сервиса целиком;http_.+— ограничение происходит по названию заголовка;host— ограничение происходит по объявленным хостам;request_method— ограничение происходит по request method.
Также объявлены следующие необязательные параметры:
burst— в значении параметра задается максимальный размер всплеска запросов. Если скорость поступления запросов превышает rate, то их обработка задерживается так, чтобы запросы обрабатывались с заданной скоростью. Избыточные запросы задерживаются до тех пор, пока их число не превысит максимальный размер всплеска. При превышении запрос завершается с ошибкой, которая задается в параметре status_code;status_code— код ответа при превышении допустимого объема запроса или частоты вызовов;rate_interval— интервал времени для измерения скорости запроса (по умолчанию - s). Если Вам необходимо 0.5r/s, то Вы можете указать 30r/m;size— размер разделяемой памяти для хранения информации о количестве запросов для каждого ip адреса. Хранение информации в общей памяти означает, что она может быть разделена между рабочими процессами NGINX. Информация о состоянии около 16 000 IP-адресов занимает 1 мегабайт, поэтому наша зона может хранить около 160 000 адресов. Если память исчерпана, когда NGINX необходимо добавить новую запись, она удаляет самую старую запись. Если свободного места все еще недостаточно для размещения новой записи, SOWA возвращает код состояния. Кроме того, для предотвращения исчерпания памяти каждый раз, когда NGINX создает новую запись, он удаляет до двух записей, которые не использовались в предыдущие 60 секунд.
Таким образом, каждый уникальный IP-адрес ограничен 0,5 запросами в секунду для /service, или, точнее, не может сделать запрос для этого URL в течение 2000 миллисекунд от его предыдущего.
Если мы получим 2 запроса в течение 2000 миллисекунд друг от друга, то для второго запроса SOWA вернет клиенту статус код 429.
Добавим профиль limit_req в реестр профилей:
sowa-config --add-profile limit_req
Сконфигурируем профиль limit_req:
sowa-config -c limit_req.yml
Запустим профиль limit_req:
sowa-config -r limit_req
В сервисы заглушек добавим сервис для профиля limit_req (относительно proxies: ../stubs/services/plain_service.yml).
- id: stub_limit_req
optional:
response_body: "Hello world"
response_headers:
- name: Content-Type
value: text/plain
- name: Number
value_from_header: Number
url: ^\/service$
listen:
- port: 18013
Добавим в response_headers заголовок запроса Number. В дальнейшем это будет необходимо для отслеживания запросов.
Переконфигурируем профиль sample_stub. Возможен случай, когда профиль не был остановлен ранее. Тогда конфигуратор выдаст сообщение Profile sample_stub is running. Are you sure that you want to reconfigure? [y/n]. Нажмите «y».
sowa-config --config ../stubs/sample_stub.yml
Перезапустим профиль sample_stub:
sowa-config --restart sample_stub
Отправим POST запросы к сервису service, объявленному в конфигурационном файле профиля limit_req:
curl -v -X POST -H "Number: 1" http://127.0.0.1:8013/service & curl -v -X POST -H "Number: 2" http://127.0.0.1:8013/service & wait
Результат выполнения запросов:

Первый код статус 200, второй - 429. Посмотрим в журнал для анализа
ошибки (/sowalogs/limit_req/services/service_http_proxy/service_error.log):
Здесь видна следующая ошибка: [error] 61434#61434: *7 **limiting requests, excess: 1.000 by zone "lr_service_http_proxy_service"**, client: 127.0.0.1, server: localhost, request: "POST /service HTTP/1.1", host: "127.0.0.1:8013"

Таким образом, было рассмотрено, как настраивается ограничение на RPS.
Параметры Upload/download size limit#
Далее рассмотрим пример, связанный с ограничением размера принимаемого/передаваемого контента.
Для этого создадим профиль updownsl.
sowa-config --add-profile updownsl
В каталоге proxies создадим файл updownsl.yml со следующим содержимым:
version: 2.0.3
profile: updownsl
profile_version: XXX
system:
wrk_count: 4
conn_count: 4096
optional:
log_level: debug
service:
service_http_proxy:
- id: service
listen:
- port: 8014
name: service
hostname:
- localhost
url: ^\/service$
allowed_queries:
- method: post
upstream_group_id: upstream_v1
upstream_groups:
- id: upstream_v1
servers:
- server: 127.0.0.1:18014
weight: 5
optional:
upload_size_limit:
size: 11
drop: "off"
download_size_limit:
size: 11
drop: "off"
В секции optional объявлены группы параметров upload_size_limit и download_size_limit, где:
size— максимальный размер тела запроса. Обязательный параметр в группах upload/download_size_limit. Текстовое значение с суффиксом: число без суффикса — байты, k / K — килобайты, m / M — мегабайты;drop— сброс соединения при превышении size («on», «off»).
Секции upload/download_size_limit доступны во всех типах сервисов и настраиваются для каждого сервиса.
Сконфигурируем профиль updownsl:
sowa-config -c updownsl.yml
Запустим профиль updownsl:
- id: service_4
optional:
response_body: "HelloWorld"
response_headers:
- name: Content-Type
value: text/plain
url: ^\/service$
listen:
- port: 18014
Переконфигурируем профиль sample_stub. Возможен случай, когда профиль не был остановлен ранее. Тогда конфигуратор выдаст сообщение Profile sample_stub is running. Are you sure that you want to reconfigure? [y/n]. Нажмите «y».
sowa-config --config ../stubs/sample_stub.yml
Перезапустим профиль sample_stub:
sowa-config --restart sample_stub
Отправим запрос с содержимым «HelloWorld1» на сервис service профиля updownsl. Длина контента HelloWorld1 равна 11 байтам, что является границей, установленного нами значения. Заголовок Content-Length указывает размер тела объекта в десятичном числе октетов (байтов), отправленных получателю.
curl -v -X POST -d "HelloWorld1" http://127.0.0.1:8014/service
Ответ выглядит следующим образом:
Код статус 200. Запрос прошел без ограничений со стороны SOWA.
Увеличим длину передаваемого сообщения и вновь отправим запрос:
curl -v -X POST -d "HelloWorld12" http://127.0.0.1:8014/service
Обратите внимание, что лимитирование действует только на передаваемое тело.
Стоит повторить, что заголовок Content-Length указывает размер тела объекта в десятичном числе октетов (байтов), отправленных получателю.

Аналогичным образом работают ограничения в download_size_limit. Можете попробовать реализовать это самостоятельно (для этого следует исправить сообщение в сервисе заглушки для нашего профиля).
Таким образом, в данном разделе были рассмотрены настройки ограничений по запросам и передаваемому контенту.
Ограничения по таймаутам#
В Platform V SOWA существуют различные таймауты, которые позволяют управлять поведением сервисов и их компонентов.
Client_header_timeout#
Вначале рассмотрим таймаут, связанный с передачей хедера клиентом - client_header_timeout - таймаут при чтении заголовка запроса клиента. Если по истечении этого времени клиент не передаст полностью заголовок, обработка запроса прекращается с ошибкой 408 (Request Time-out). Если заголовок окажется «слишком» большим, то увидим ошибку 413 (Request Entity Too Large Error). По умолчанию - 60s.
Создадим конфигурационный файл timeouts.yml и наполним его следующим содержимым:
profile: timeouts
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
optional:
log_level: debug
service:
service_http_proxy:
- id: service_1
name: service_1
url: ^\/hello$
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:11013
upstream_group_id: back_1
allowed_queries:
- method: get
listen:
- port: 11003
optional:
client_header_timeout: 10s
В конфигурационный файл для сервисов заглушек добавим сервис timeout_stub:
- id: timeout_stub
optional:
response_code: 200
response_body: "{\"msg\": \"my message\", \"result\": 0}"
response_headers:
- name: Content-Type
value: application/json
url: ^\/hello$
listen:
- port: 11013
Для того чтобы продемонстрировать работоспособность этого таймаута, воспользуемся утилитой nc (netcat).
Отправим запрос:
nc 127.0.0.1 11003
Программа предложит нам продолжить ввод. Добавим следующую строку:
GET /hello HTTP/1.1
Нажмем «ввод». Далее попробуем отправить какой-нибудь заголовок,
например «Host: 127.0.0.1».
Команда завершает свою работу через ~10 секунд. В консоле не увидели сообщение об ошибке. Посмотрим в логи профиля timeouts, а в частности сервиса service_1.
/sowalogs/timeouts/services/service_http_proxy/service_1_error.log:

/sowalogs/timeouts/services/service_http_proxy/service_1_access.log:

Видим код статус 408 и сообщение client timed out (110: Connection timed out) while reading client request headers. Таймаут client_header_timeout успешно отработал.
Client_body_timeout#
client_body_timeout — таймаут при чтении тела запроса клиента.
Внимание!
Таймаут устанавливается не на всю передачу тела запроса, а только между двумя последовательными операциями чтения.
Если по истечении этого времени клиент ничего не передаст, обработка запроса прекращается с ошибкой 408 (Request Time-out). По умолчанию - 60s.
Внесем изменения в конфигурационный файл timeouts.yml
profile: timeouts
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
optional:
log_level: debug
service:
service_http_proxy:
- id: service_1
name: service_1
url: ^\/hello$
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:11013
upstream_group_id: back_1
allowed_queries:
- method: post
listen:
- port: 11003
optional:
client_body_timeout: 3s
Был изменен разрешенный метод запроса и таймаут.
Переконфигурируем и перезапустим профиль.
Также воспользуемся командой nc для отправки запроса.
nc 127.0.0.1 11003
Теперь у нас есть 60 секунд, чтобы ввести запрос, адрес и заголовок запроса. После добавления каждой строки нажимайте «ввод». Обратите внимание, что мы используем заголовок запроса Transfer-encoding: chunked, который говорит нам о том, что данные тела сообщения будут передаваться частями. После завершения добавления хэдеров нажмите дважды «ввод».
POST /hello HTTP/1.1
User-Agent: curl/7.29.0
Host: localhost:11003
Accept: */*
Content-Length: 200
Content-Type: text/xml
Transfer-encoding:chunked
Можно успеть ввести тело, но для этого время ограничено в 3 секунды. После этого соединение закрывается.
Обратимся к access.log сервиса:

Как видно, код статус 408. Это то, что нужно было получить.
Proxy_read_timeout#
proxy_read_timeout - таймаут при чтении ответа проксированного сервера.
Внимание!
Таймаут устанавливается не на всю передачу тела запроса, а только между двумя последовательными операциями чтения.
Если по истечении этого времени проксируемый сервер ничего не передаст, соединение закрывается. По умолчанию — 60s.
Внесем изменения в конфигурационный файл timeouts.yml:
profile: timeouts
version: 2.0.3
system:
wrk_count: 1
conn_count: 4096
optional:
log_level: debug
service:
service_http_proxy:
- id: service_1
name: service_1
url: ^\/hello$
upstream_groups:
- id: back_1
servers:
- server: 127.0.0.1:11013
upstream_group_id: back_1
allowed_queries:
- method: post
listen:
- port: 11003
optional:
proxy_read_timeout: 3s
Был изменен только таймаут.
Переконфигурируйте и перезапустите профиль.
Отправьте запрос с помощью curl.
curl -v -X POST http://localhost:11003/hello
В результате следующее:

Обратитесь к access.log сервиса:

Как видно, код статус 504. Это то, что нужно было получить.
Перейдите к error.log сервиса:
Запрос завершился по прошествии трех секунд с сообщением [error] 62561#62561: *3 upstream timed out (110: Connection timed out) while reading response header from upstream, client:127.0.0.1, server: localhost, request: "POST /hello HTTP/1.1", upstream: "http://127.0.0.1:11013/hello", host:"localhost:11003"