-
Notifications
You must be signed in to change notification settings - Fork 0
Контроль пропускной способности (троттлинг) API c помощью шлюза авторизации OpenIG
Эта статья является продолжением серии статей о защите веб сервисов при помощи шлюза с открытым исходным кодом OpenIG. В этой статье мы рассмотрим, как защитить сервисы, ограничив количество запросов за определенное время времени.
Для чего нужно ограничивать количество запросов? Опытные коллеги могут пропустить этот раздел и сразу перейти к разделу настройки.
- Борьба с DDoS атаками. DDoS атаки - одни из самых распространенных. Цель атаки - перегрузить атакуемый сервис большим количеством запросов. Сервис не успевает обработать все запросы, время отклика растет, и сервис просто перестает отвечать. Аналогичная ситуация возможна при неожиданном наплыве клиентов на сервис, что очень похоже на DDoS атаку.
- Контроль соглашения об уровне обслуживания. Допустим, организация предоставляет API с разными уровнями обслуживания. Например, обычный клиент может осуществлять 10 вызовов API в минуту, а премиальный клиент до 1000.
- Предотвращение “слива” базы данных. Например, при обычной работе сотрудник может запрашивать данные по 5 клиентам в минуту, и если он превышает этот лимит, то такое поведение похоже на попытку скачивания всей базы. Ограничение количества вызовов помогает предотвратить такую атаку.
Для демонстрационных целей клонируйте проект командой
git clone -b features/throttling https://github.com/maximthomas/openig-protect-ws.git
И запустить его командой
docker compose up
В файле docker-compose.yaml
описано 2 сервиса - OpenIG и демонстрационной сервис sample-service, который защищен OpenIG.
В демонстрационном сервисе две конечные точки - корневая /
, доступ к которой имеют все пользователи и /secure
доступ к которой имеют только аутентифицированные пользователи.
К этим конечным точкам в OpenIG прописаны маршруты openig-config/config/routes/10-api.json
и openig-config/config/routes/20-secure.json
соотвественно.
Для авторизации в /secure
используется JWT, подписанный приватным ключом openig-config/keys/private_key.pem
.
Фильтр ScriptableFilter
при помощи скрипта openig-config/scripts/groovy/jwt.groovy
разбирает JWT, проверяет подпись публичным ключом openig-config/config/config.json
и пишет в контекст claims role
и sub
.
В файле openig-config/config/config.json
описаны фильтры, которые срабатывают для всех маршрутов, а так же определены фильтры, которые могут быть использованы в произвольных маршрутах.
Подробнее настройка была описана в статье
Теперь, когда у вас запущен OpenIG и защищаемый сервис, добавим в маршрут фильтр, который ограничивает все не аутентифицированные запросы к сервису с ограничением - не более 5 запросов в 5 секунд.
Откройте файл маршрута 10-api.json
в директории openig-config/config/routes
и добавьте в цепочку фильтров фильтр с типом ThrottlingFilter
.
Атрибуты numberOfRequests
и duration
объекта rate
определяют лимит запросов за период времени соотвественно.
10-api.json
{
"name": "${matches(request.uri.path, '^/$')}",
"condition": "${matches(request.uri.path, '^/$')}",
"monitor": true,
"timer": true,
"handler": {
"type": "Chain",
"config": {
"filters": [
{
"type": "ThrottlingFilter",
"name": "simple-throttling",
"config": {
"requestGroupingPolicy": "",
"rate": {
"numberOfRequests": 5,
"duration": "5 s"
}
}
},
...
DDoS атаки ведутся только с фиксированного набора IP адресов. И для обеспечения нормальной работы пользователей можно ограничить количество запросов для каждого IP адреса.
Добавим в атрибут requestGroupingPolicy
фильтра ThrottlingFilter
IP адрес запроса:
{
"type": "ThrottlingFilter",
"name": "simple-throttling",
"config": {
"requestGroupingPolicy": "${(not empty request.headers['X-Real-Ip'][0])?request.headers['X-Real-Ip'][0]:contexts.client.remoteAddress}",
"rate": {
"numberOfRequests": 5,
"duration": "5 s"
}
}
},
В выражении из листинга выше, OpenIG проверяет сначала значение заголовка, X-Real-Ip
(этот заголовок может выставлять балансировщик нагрузки), и если оно не пустое, использует его, если же пустое, использует IP адрес запроса.
Проверим настройку, выполнив команду curl несколько раз
for i in `seq 7`; \
do curl --trace-time -v -H "Content-Type: application/json" -H "Accept: application/json" --data '{"test": "test"}' http://localhost:8080; \
done 2>&1 | grep '< HTTP'
15:55:44.207986 < HTTP/1.1 200
15:55:45.237957 < HTTP/1.1 200
15:55:45.278702 < HTTP/1.1 200
15:55:45.319421 < HTTP/1.1 200
15:55:45.352789 < HTTP/1.1 200
15:55:45.376685 < HTTP/1.1 429
15:55:45.395739 < HTTP/1.1 429
По выводу команды curl видно, что на запросы, начиная с шестого OpenIG возвращает HTTP статус 429.
Пример полного ответа:
15:56:09.535261 < HTTP/1.1 429
15:56:09.535302 < Retry-After: 1
15:56:09.535330 < Retry-After-Partition: 10.1.1.5
15:56:09.535357 < Retry-After-Rate: 5/5 SECONDS
15:56:09.535384 < Retry-After-Rule: simple-throttling
...
Обратите внимание на заголовки, которые возвращает ThrottlingFilter
при превышении лимита:
Заголовок | Описание |
---|---|
Retry-After | Количество секунд, которое необходимо ждать до следующего запроса |
Retry-After-Partition | Значение группировки, по которой ведется подсчет частоты запросов |
Retry-After-Rate | Допустимая частота запросов |
Retry-After-Rule | Имя фильтра OpenIG |
Теперь настроим ограничение более гибко. Установим ограничения для аутентифицированных пользователей на конечную точку /secure
. Ограничение будет работать индивидуально для каждого пользователя, аналогично IP адресу из примера выше. Ограничения будут сгруппированы по значению JWT claimsub
. Обычные пользователи могут отправлять максимум 5 запросов за 10 секунд, а пользователи с ролью supervisor
, которым нужен существенно большей объем данных - могут отправлять до 10 запросов за 10 секунд. Значение свойства будет браться из claim JWT role
в скрипте jwt.groovy
.
Добавим в маршрут 20-secure.json
фильтр ThrottlingFilter
после фильтра ScriptableFilter
, который разбирает JWT.
{
"type": "ThrottlingFilter",
"name": "auth-users-throttling",
"config": {
"requestGroupingPolicy": "${attributes['sub']}",
"throttlingRatePolicy": {
"type": "MappedThrottlingPolicy",
"config": {
"throttlingRateMapper": "${attributes['role']}",
"throttlingRatesMapping": {
"supervisor": {
"numberOfRequests": 10,
"duration": "5 s"
}
},
"defaultRate": {
"numberOfRequests": 5,
"duration": "5 s"
}
}
}
}
}
Вместо объекта rate
добавим throttlingRatePolicy
с типом MappedThrottlingPolicy
Таким образом для JWT с ролью supervisor
допустимо 10 запросов за 10 секунд, для всех остальных - 5 запросов.
Проверим ограничения для обычного пользователя:
export OPENAM_JWT=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzYW1wbGUtc2VydmljZSIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJyb2xlIjoibWFuYWdlciIsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNzI2MjM5MDIyfQ.bhzhwj2cY1iYbpx7Mzbukfi1jOCvWP-Pdd9dm3hf7lZDDuokNVDUXU3jvHial4QN-bOTSNCUKVy907hokcVeQaFwbiYoZs485Kr230Z0y9MU6zbDe8yQp68-71TDgJGIZ78YYOKvJTrzCWgWgE_Py1DskG_ViSxfGFlETpFQa056Rk2bty-9iuc_Cx5_Wr6RCcJTG6WYRrBtdWGIFxljEjxSAcJYmGPPA8dHHORDOnmka2OAjWURnsqbz50aI_SrWpnqp4i2eXVA1b5QD5rlsgc_QAqJptghrijBlRPhasTk1N-kXE8Ozboa0FwGfIRr7gNiG-3if7INZe2R5NUCmjlAlywcSfOunWuSzY8tLGTHV2swnQPP8lBXwVJcS5nJMqBNIbcLcFWHk3ryvvtf-LYty_jAY8v1zDe9-DwFPWI0rry8fmiZj7yhAnvX5EHZHvSQtp_zyPpVWvOXFPwasI0jdKoxhWvyJpbmw-D95J5CgJAMfkrWPDQKVt3ipebwnMJStA3xAPPyl28mTBYhJrT6gzIOS3DseoVKK4adn34ZrQi2Hm-wyUtbdulopK739MKM73NYgoFXSJeVUqcy4iw3-In5XmOhdRnUL50TSiaNBbkys8iK7r00HD3kI3CH0GfaPdrcgRgaFXKmVDhX-tEaPJYcuEUTHfQAxWwMdiw
for i in `seq 7`; \
do curl --trace-time -v GET -H "Content-Type: application/json" -H "Accept: application/json" -H "Authorization: Bearer ${OPENAM_JWT}" http://localhost:8080/secure; \
done 2>&1 | grep '< HTTP'
15:57:33.514169 < HTTP/1.1 200
15:57:33.545997 < HTTP/1.1 200
15:57:33.580583 < HTTP/1.1 200
15:57:33.615292 < HTTP/1.1 200
15:57:33.647661 < HTTP/1.1 200
15:57:33.672396 < HTTP/1.1 429
15:57:33.696753 < HTTP/1.1 429
Ограничения для супервизора:
export OPENAM_JWT_SUPERVISOR=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzYW1wbGUtc2VydmljZSIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJyb2xlIjoic3VwZXJ2aXNvciIsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNzI2MjM5MDIyfQ.ccigmz0n1gP1fIe0HP5jMAjHWKkD1cwAViGfSapfVZ86GxKY9wkOWegYABmDyEXWwHWAcwFFFu1ZF7JYRiBmci87cRj5MSbw6Mrb1M_8rZj6aP9y9qTyWY80PtMJw6Udcn_wqvfCeMlKLlaItnUYc6-bth1rb_tJNd9FxDcpMZt-5q1uMGfeEPWsyF4a81kSFmNr2aD4rp8ftpLv6VoObkEdYmwkn9aRKLAxNjD9Ze8rdKQBgCk_rR3hTzURyPO_2QZsLDfpPMQx0O3Qbx9x_4om5D_hlrBOdNp6k435J1ZT2sllaJaP_HEQSGgWAwS1I9me9jwfIuA-Fhcxa6si7P0MlSX7Bj6Zki492RBvw2dsspnDZ_BOiVFteMYorS2KZoahQyYtxPubZSdCNqJ3fG8qX3zDj1EESS2srFQrF6baZfpJMHUNMCO_2QSioBBi8ffatG2snwHLQKiTr2TD-YqBx_rU3BGV3wGa9bXSAaTJCvn9x8Id_ie8x5xfaZXJL0r0gunj1LZuYKsNjo4VMMTn-pu5UZtttg9s30OozCEzvC5fM3LXDR2R_klanvFWWQlDabiF1kUnzQuSD9uj37pnbHgv0NOG3RePO0hujqelmj5HVzEE-h6ULKeUKJAxNZ9otMJb25RpQr_cZvIX3UPzFbLqbI7hyfzjZP6258Q
for i in `seq 12`; \
do curl --trace-time -v GET -H "Content-Type: application/json" -H "Accept: application/json" -H "Authorization: Bearer ${OPENAM_JWT_SUPERVISOR}" http://localhost:8080/secure; \
done 2>&1 | grep '< HTTP'
15:58:05.830975 < HTTP/1.1 200
15:58:05.860576 < HTTP/1.1 200
15:58:05.890634 < HTTP/1.1 200
15:58:05.922926 < HTTP/1.1 200
15:58:05.957944 < HTTP/1.1 200
15:58:05.986881 < HTTP/1.1 200
15:58:05.019237 < HTTP/1.1 200
15:58:05.051065 < HTTP/1.1 200
15:58:05.084710 < HTTP/1.1 200
15:58:05.114731 < HTTP/1.1 200
15:58:05.140818 < HTTP/1.1 429
15:58:05.165421 < HTTP/1.1 429
Интернет шлюз (реверсивный прокси) контроля доступа к UI и API.
Позволяет организовывать защищенный доступ к веб приложениям и API путем решения задач маршрутизации, аутентификации, авторизации, федерации и расширения профиля.
Поддерживает открытые стандарты аутентификации и федерации: OAuth, OpenID Connect, SAML. Позволяет безопасно настроить функцию Replay password для унаследованных систем и производить изменение контента “на лету”.
Эволюция: Forgerock/Open Identity Platform OpenIG