Правила фильтрации#

Ограничения по частоте и размеру сообщений#

Одним из свойств, которыми обладает 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 — в значении параметра определяется ключ, по которому происходит ограничение:

    1. src_ip — ограничение происходит по количеству запросов для каждого ip адреса клиента отдельно;

    2. srv_port — ограничение происходит для каждого порта сервиса отдельно;

    3. service — ограничение происходит по количеству запросов для каждого сервиса отдельно и для всего сервиса целиком;

    4. http_.+ — ограничение происходит по названию заголовка;

    5. host — ограничение происходит по объявленным хостам;

    6. 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

Результат выполнения запросов: d19

Первый код статус 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" d20

Таким образом, было рассмотрено, как настраивается ограничение на 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

Ответ выглядит следующим образом: d21 Код статус 200. Запрос прошел без ограничений со стороны SOWA.

Увеличим длину передаваемого сообщения и вновь отправим запрос:

curl -v -X POST -d "HelloWorld12" http://127.0.0.1:8014/service

d22 Обратите внимание, что лимитирование действует только на передаваемое тело.

Стоит повторить, что заголовок Content-Length указывает размер тела объекта в десятичном числе октетов (байтов), отправленных получателю. d23

Аналогичным образом работают ограничения в 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». d24 Команда завершает свою работу через ~10 секунд. В консоле не увидели сообщение об ошибке. Посмотрим в логи профиля timeouts, а в частности сервиса service_1.

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

d25

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

d26

Видим код статус 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 сервиса: d27

Как видно, код статус 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

В результате следующее: d28

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

Как видно, код статус 504. Это то, что нужно было получить.

Перейдите к error.log сервиса: d30 d31 Запрос завершился по прошествии трех секунд с сообщением [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"