Модель безопасности¶
Provisa применяет многоуровневую модель безопасности во всех языках запросов (GraphQL, SQL, Cypher) и во всех транспортах (REST, gRPC, Arrow Flight, JDBC, WebSocket). (REQ-001, REQ-266) Управление применяется единообразно — не существует пути запроса, который его обходит. (REQ-002, REQ-266)
Уровни применяются по порядку. Запрос должен пройти каждый уровень, прежде чем будет вычислен следующий.
Многоуровневая модель¶
Уровень 0 — фильтрация интроспекции¶
Схема и каталог, предъявляемые роли, содержат только таблицы из её списка domain_access и колонки, проходящие правила visible_to для каждой колонки. (REQ-039) Объекты вне доступа роли невидимы на этапе обнаружения — их нельзя запросить, автодополнить или заключить об их существовании. (REQ-039) Это относится к схеме GraphQL, каталогу SQL и обозревателю схемы в редакторе запросов. (REQ-039, REQ-363)
См. Видимость схемы.
Уровень 1 — публичный доступ¶
Таблицы в доменах без ограничения domain_access видны всем аутентифицированным идентичностям без дополнительной настройки. Нулевое трение для действительно публичных данных.
Уровень 2 — доступ к домену¶
Каждая роль несёт список domain_access из идентификаторов доменов. Запрос, затрагивающий таблицу вне этих доменов, отклоняется до выполнения. (REQ-038, REQ-039) Это грубая граница владения — роль отдела кадров не может добраться до финансовых таблиц, как бы ни был написан SQL. (REQ-002)
См. Модель прав.
Уровень 3 — безопасность на уровне строк¶
После подтверждения доступа к домену предикаты WHERE для каждой таблицы и каждой роли внедряются в каждый SELECT во время выполнения. (REQ-041, REQ-263) Предикаты вычисляются по необработанным данным. Региональный руководитель, запрашивающий общую таблицу заказов, видит только строки своего региона даже при SELECT *. (REQ-264)
См. Безопасность на уровне строк (RLS).
Уровень 4 — видимость и маскирование колонок¶
Колонки со списком visible_to, в котором нет запрашивающей роли, вырезаются из вывода запроса. (REQ-040, REQ-263) У колонок с правилом маскирования значения подменяются — вырезание по регулярному выражению, замена на константу или усечение — прежде чем результаты покинут сервер. (REQ-263) Маскирование применяется во всех языках запросов и форматах вывода. (REQ-263)
См. Модель прав на колонки и Маскирование на уровне колонок.
Уровень 5 — защита предикатов¶
Маскированные колонки отклоняются в предложениях WHERE и HAVING. (REQ-263) Без этого вызывающая сторона могла бы вычислить немаскированное значение двоичным поиском по фильтру, даже если вывод маскирован. Отклонение выполняется на этапе разбора запроса, до выполнения. (REQ-531)
Управление связями (V002)¶
Условия JOIN в SQL должны соответствовать зарегистрированной, утверждённой связи между таблицами. (REQ-001) Неутверждённые соединения отклоняются. Каждая связь несёт понятные человеку обоснование и описание — подсказку и пользователям, и автономным агентам о том, почему путь обхода существует. Это политика управления, а не жёсткая граница безопасности: уровни 2–5 действуют независимо от структуры соединения, поэтому намеренный обход не раскрывает данных, до которых роль не смогла бы добраться двумя отдельными запросами. Попытки обхода записываются в журнал и доступны для аудита.
Механизмы обхода — V002 можно обойти двумя способами. Первый — возможность: роль, обладающая ignore_relationships, соединяет отношения, не покрытые каталогом. Среди предустановленных системных ролей ею обладает только modeler — исследовательская роль, чья задача — определять модель, а не применять её. (REQ-1297) У analyst её нет. [tool-verified: provisa/core/db.py:84]
Второй — отказ по двум условиям, оба из которых должны выполняться:
- Флаг роли —
relationship_guard: falseв определении роли (по умолчанию:true). [tool-verified:provisa/core/models.py:349] - Отказ на уровне запроса — SQL содержит комментарий
--relationship-guard=false. [tool-verified:provisa/compiler/params.py:80]
Один только флаг роли не обходит V002; один только комментарий не обходит V002.
Режим высокой безопасности закрепляет защиту. При security.mode: high ни один из обходов не действует: ignore_relationships игнорируется, relationship_guard: false игнорируется, и каждое соединение должно существовать в каталоге утверждённых связей. (REQ-693) Это намеренное дублирование — производственная роль, которой возможность выдали по ошибке, всё равно не сможет вырваться из модели. [tool-verified: provisa/pgwire/_pipeline.py:377]
Путь GraphQL — для запросов GraphQL V002 пропускается безусловно. Связи, определённые в SDL, утверждены по построению; проверка избыточна и не применяется. [tool-verified: provisa/api/data/endpoint.py:468]
Пути SQL и Cypher — V002 активна по умолчанию. И endpoint_dev.py, и cypher_router.py выполняют проверку по двум условиям перед вызовом validate_sql. [tool-verified: provisa/api/data/endpoint_dev.py:127, provisa/api/rest/cypher_router.py:260]
Путь pgwire — та же проверка по двум условиям, что и для SQL. Комментарий --relationship-guard=false вырезается из запроса перед выполнением; до базы данных он не доходит. [tool-verified: provisa/pgwire/_pipeline.py:60]
Эти уровни складываются. У роли с доступом к домену, RLS и маскированными колонками все пять ограничений действуют одновременно. Добавление нового источника данных, колонки или связи не требует обновления каждого правила — каждый уровень настраивается независимо и применяется автоматически к любому запросу, который затрагивает управляемые объекты.
Модель прав¶
Независимо назначаемые возможности с необязательной иерархией ролей через parent_role_id. admin даёт все. (REQ-042)
| Возможность | Описание |
|---|---|
source_registration |
Регистрация источников данных |
table_registration |
Регистрация таблиц и колонок |
create_relationship |
Определение связей по внешним ключам |
access_config |
Настройка RLS и маскирования |
query_development |
Выполнение запросов |
write |
Вызов зарегистрированных мутаций (грубый шлюз; см. «Авторизация мутаций») |
full_results |
Обход ограничений выборки |
ignore_relationships |
Обход управления связями (V002). Среди системных ролей есть только у modeler и полностью игнорируется в режиме высокой безопасности |
admin |
Суперпользователь — даёт все |
Наследование ролей¶
Роли могут наследовать возможности и доступ к доменам от родительской роли через parent_role_id. (REQ-215) Иерархия разворачивается при запуске — дочерние роли объединяют возможности и доступ к доменам родителя со своими. (REQ-215)
roles:
- id: basic_user
capabilities: [query_development]
domain_access: [public]
- id: analyst
capabilities: [full_results]
domain_access: [sales, analytics]
parent_role_id: basic_user # inherits query_development + public domain
Модель прав на колонки¶
У каждой колонки есть модель прав из четырёх полей, управляющая чтением, записью и маскированием для каждой роли. (REQ-042, REQ-249)
Трёхуровневая видимость¶
| Уровень | Условие | Результат |
|---|---|---|
| Скрыта | Роли нет в visible_to |
Колонка отсутствует в SDL GraphQL |
| Маскирована | Роль есть в visible_to, есть правило маскирования, роли нет в unmasked_to |
Колонка видна, но данные маскируются в SQL |
| Немаскирована | Роль есть в visible_to И роль есть в unmasked_to (или правила маскирования нет) |
Полный доступ на чтение |
Права на запись¶
| Поле | Пустое значение означает | Назначение |
|---|---|---|
visible_to |
Читать могут все роли | Определяет, кто видит колонку (маскированной или нет) |
unmasked_to |
Ни одна роль не видит немаскированные данные | Определяет, кто обходит маскирование |
writable_by |
Ни одна роль не может писать | Определяет, кто может изменять данные (INSERT/UPDATE) |
Право на запись применяется в конвейере мутаций. Роль, отсутствующая в writable_by, получает ошибку 403 при попытке записи в ограниченную колонку. (REQ-033, REQ-034)
Пример¶
columns:
- name: email
visible_to: [admin, analyst, viewer]
writable_by: [admin]
unmasked_to: [admin]
mask_type: regex
mask_pattern: "(.).*@"
mask_replace: "$1***@"
- name: salary
visible_to: [admin, hr]
writable_by: [hr]
unmasked_to: [admin, hr]
mask_type: constant
mask_value: "0"
- name: created_at
visible_to: [] # all can read
writable_by: [] # nobody can write (auto-set)
В этом примере:
email: admin видитalice@example.comи может редактировать; analyst и viewer видятa***@example.comsalary: admin и hr видят реальное значение; hr может редактировать; все остальные роли вообще не видят колонкуcreated_at: читать может каждый, писать — никто
Авторизация мутаций¶
Зарегистрированные мутации (удалённый GraphQL, OpenAPI, gRPC, Hasura) закрыты двумя независимыми проверками. (REQ-867, REQ-868) Роль может вызвать мутацию, только если обладает глобальной возможностью write И присутствует в списке writable_by этой мутации. (REQ-868) Пустой writable_by означает запрет по умолчанию — вызвать мутацию не может ни одна роль. (REQ-867)
Мутации классифицируются как запись по контракту, а не по заявлению вызывающей стороны. (REQ-869) SELECT, который ссылается на функцию вида «мутация», повышается до записи и подпадает под ту же двойную проверку, поэтому вызывающая сторона не может вызвать мутацию, замаскировав её под чтение. (REQ-869) Переклассификация мутации в безопасную для чтения требует возможности access_config и записывается как решение управления; отказа на уровне отдельного запроса не предусмотрено. (REQ-870)
Видимость схемы¶
Схемы GraphQL для каждой роли скрывают неавторизованное содержимое: (REQ-039)
- Доступ к домену: роль видит таблицы только в доменах её
domain_access("*"= все) (REQ-039) - Видимость колонок: колонки, отсутствующие в
visible_toдля роли, опускаются в SDL (REQ-039) - Неавторизованные таблицы и колонки в схеме не появляются (REQ-039)
Безопасность на уровне строк (RLS)¶
Внедрение предложения SQL WHERE для каждой таблицы и каждой роли. Применяется после компиляции, до выполнения. (REQ-041, REQ-263)
rls_rules:
- table_id: orders
role_id: analyst
filter: "region = current_setting('provisa.user_region')"
Фильтр присоединяется к предложению WHERE запроса через AND. Работает и для запросов, и для мутаций (UPDATE/DELETE). (REQ-035, REQ-041)
Маскирование на уровне колонок¶
Маскирование задаётся один раз для колонки — это свойство колонки, а не роли. Поле unmasked_to определяет, какие роли его обходят. (REQ-249)
| Тип маски | Поддерживаемые типы | Выражение SQL |
|---|---|---|
regex |
Строковые (varchar, char, text) | REGEXP_REPLACE(col, pattern, replace) |
constant |
Любой | Литеральное значение (NULL, 0, произвольное) |
truncate |
Дата и метка времени | DATE_TRUNC(precision, col) |
Маскирование проталкивается в проекцию SQL SELECT — база данных возвращает уже маскированные данные. (REQ-263) Для маскированных ролей немаскированные данные никогда не идут по сети. (REQ-263) Маскированные колонки также блокируются в предложениях WHERE и HAVING (защита предикатов уровня 5), чтобы предотвратить вычисление немаскированного значения через фильтрацию. (REQ-263, REQ-531)
Выборка¶
Все роли видят выборочные результаты (по умолчанию: 100 строк), если у них нет возможности full_results. (REQ-554) Управляется переменной окружения PROVISA_SAMPLE_SIZE. (REQ-554)
Журналирование аудита¶
Каждый запрос, затрагивающий актив домена, записывается в журнал аудита query_audit_log, доступный только для добавления. (REQ-596, REQ-613) Каждая строка фиксирует tenant_id, user_id, role_id, хеш SHA-256 текста запроса, table_ids, source, status_code, duration_ms и logged_at. (REQ-596) Текст запроса никогда не хранится дословно — только его хеш. (REQ-596)
Журнал доступен только для добавления на уровне базы данных: правила PostgreSQL блокируют DELETE и UPDATE. (REQ-596, REQ-613) Два индекса — (tenant_id, logged_at) и (user_id, logged_at) — поддерживают запросы соответствия по диапазону времени в пределах арендатора и по отдельному пользователю. (REQ-596, REQ-613)
Когда шифрование включено, колонка с хешем текста запроса хранится в зашифрованном виде и расшифровывается только при авторизованном административном чтении. (REQ-689)
Ограничение частоты запросов¶
Ограничения частоты для каждой роли настраиваются в provisa.yaml: максимум запросов в секунду, максимум одновременных подписок SSE и максимум одновременных потоков Arrow Flight. (REQ-369) Ограничения применяются на уровне API до компиляции или выполнения; запросы сверх лимита отклоняются с HTTP 429 и заголовком Retry-After. (REQ-369)
У службы запросов на естественном языке (POST /query/nl) есть независимый лимит через nl.rate_limit (запросов в минуту на роль). Запросы сверх лимита отклоняются до любого обращения к LLM. (REQ-370)
Состояние ограничения частоты хранится в Redis (cache.redis_url) как счётчик скользящего окна — без состояния на уровне экземпляра, — поэтому лимиты действуют на всех горизонтально масштабируемых экземплярах Provisa. (REQ-371)
Аутентификация¶
Подключаемые провайдеры аутентификации: (REQ-120)
| Провайдер | Тип токена | Сценарий использования |
|---|---|---|
none |
Заголовок X-Provisa-Role | Разработка |
basic |
Локальные учётные записи с bcrypt + JWT | Автономные развёртывания |
firebase |
Идентификационный токен Firebase | Продуктив |
keycloak |
JWT Keycloak | Корпоративные среды |
oauth |
JWT OIDC | PingFed, Okta, Azure AD, Auth0 |
simple |
bcrypt + JWT | Тестирование |
Сопоставление ролей: утверждения идентичности → роль Provisa по настраиваемым правилам. (REQ-120) Поле assignments_source определяет, откуда берутся назначения ролей: claims читает их из утверждений JWT-токена (по умолчанию), provisa читает их из внутреннего хранилища назначений Provisa. (REQ-551)
Суперпользователь, заданный в provisa.yaml (имя пользователя плюс пароль из секрета окружения), всегда получает роль администратора и все возможности независимо от настроенного провайдера — путь начальной загрузки для первичной настройки. (REQ-125)
Поверхности и учётные данные¶
Каждая поверхность аутентифицируется через один и тот же контракт провайдера, поэтому учётные данные, работающие на одной, работают на всех, где протокол способен их передать. (REQ-124, REQ-1263) Эта таблица — единственный справочник; документация по отдельным поверхностям её не повторяет.
| Поверхность | Пароль | Токен провайдера | Персональный токен доступа | Клиентский сертификат (mTLS) |
|---|---|---|---|---|
| HTTP (REST, JSON:API, GraphQL) | Authorization: Basic |
Authorization: Bearer |
Authorization: Bearer |
через терминирующий прокси |
| pgwire | поле пароля (открытый текст или SCRAM) | поле пароля, развёртывания с OIDC | поле пароля | да |
| Bolt | схема basic |
схема bearer |
схема bearer |
да |
| Arrow Flight | — | token в рукопожатии или в теле тикета |
то же | да |
| gRPC | — | метаданные authorization |
метаданные authorization |
да |
| MCP | — | Authorization: Bearer |
Authorization: Bearer |
через терминирующий прокси |
Там, где в ячейке стоит —, протокол не несёт поля имени пользователя, с которым можно было бы связать пароль; эти случаи покрывают формы с токенами. pgwire — зеркальный случай: в стартовом пакете есть одно поле секрета и нет схемы, поэтому метод выбирает то, чем секрет является, — PAT распознаётся по префиксу, секрет читается как bearer-токен, когда настроенный провайдер является провайдером токенов, а всё остальное считается паролем. Выбор делается один раз — учётные данные, отвергнутые выбранным валидатором, не проверяются повторно другим.
Матрица подкреплена tests/unit/test_auth_surface_conformance.py, который прогоняет реальную точку входа валидации каждой поверхности и падает, когда добавлена новая поверхность без строки в таблице.
Персональные токены доступа¶
PAT — это долгоживущий bearer-секрет, который пользователь выпускает для клиента, не способного пройти интерактивный вход, — скрипта, BI-инструмента, драйвера. (REQ-1263) Он несёт собственные организацию и роль, и каждая поверхность разрешает его через один и тот же валидатор, поэтому ни одной поверхности не нужно знать, что такое PAT.
Проводной формат — provisa_pat_, за которым следуют 43 символа url-safe base64. Именно префикс направляет предъявленный секрет в хранилище токенов, а не к провайдеру идентичности, и он же делает утёкший токен находимым через grep в журналах и репозиториях.
- Хранение — хранится только SHA-256 секрета. Сам секрет показывается ровно один раз, при создании, и восстановить его нельзя. В списке присутствуют отображаемый префикс и отметки времени жизненного цикла, но никогда не рабочие учётные данные.
- Выпуск и отзыв —
POST /auth/tokens,GET /auth/tokens,DELETE /auth/tokens/{token_hash}, плюс раздел самообслуживания в собственном профиле пользователя в административном интерфейсе. Выпуск и отзыв учётных данных — действие владельца токена. - Атрибуция — прошедший проверку PAT разрешается в учётную запись его владельца: идентификатор пользователя, электронная почта и отображаемое имя. Поэтому строка аудита или отчёт об использовании, записанные под PAT, называют человека, а не учётные данные. То, какой именно из токенов этого человека действовал, передаётся отдельно, в
raw_claims["token_name"]. - Срок действия — токен может нести срок действия; истёкший токен отклоняется при проверке. Удаление членства пользователя отзывает вместе с ним и его токены.
SCRAM-SHA-256 в pgwire¶
При провайдере basic установка auth.scram: true заставляет pgwire объявлять SASL (код аутентификации 10) с механизмом SCRAM-SHA-256, так что пароль доказывается, а не пересылается. (REQ-1394) Привязка канала (SCRAM-SHA-256-PLUS) не предлагается.
SCRAM требует верификатора по RFC 5802, который нельзя вывести из хеша bcrypt. Верификатор записывается каждый раз, когда пароль проходит в открытом виде, — регистрация, вход, смена пароля, административный сброс, — поэтому развёртывание, включившее SCRAM, накапливает верификаторы по мере того, как пользователи в следующий раз аутентифицируются, и первое SCRAM-подключение каждого пользователя следует за его следующим вводом пароля. Пользователю, у которого верификатора ещё нет, отвечают имитацией обмена, неотличимой от настоящей, поэтому по сети не видно, кто уже перешёл.
Взаимный TLS¶
Проверка клиентского сертификата переносит первую проверку в рукопожатие TLS: вызывающая сторона без сертификата, подписанного удостоверяющим центром развёртывания, вообще не доходит до уровня учётных данных. (REQ-1228) Она доступна в pgwire, Bolt, gRPC и Arrow Flight — четырёх транспортах, которые терминируют собственный TLS.
| Переменная | Значение |
|---|---|
PROVISA_MTLS_CLIENT_CA |
PEM-набор удостоверяющих центров, которым разрешено подписывать клиентские сертификаты |
PROVISA_MTLS_MODE |
required (по умолчанию, когда центр задан) или optional |
PROVISA_MTLS_BIND_PRINCIPAL |
Если включено, общее имя сертификата должно совпадать с именем пользователя, под которым затем аутентифицируется соединение |
Переопределения для отдельных протоколов следуют тому же именованию, что и настройки TLS. Ничто не выводится по умолчанию: режим, заданный без удостоверяющего центра, не даёт запуститься, и нераспознанный режим тоже не даёт запуститься, вместо того чтобы толковаться как ближайший безопасный вариант, — развёртыванию, которое считает, что требует клиентские сертификаты, но не требует их, хуже, чем тому, которое не запустилось.
Ограничение попыток входа¶
Подбор пароля не зависит от протокола: одну и ту же учётную запись можно долбить через HTTP, pgwire и Bolt. Поэтому счётчик живёт на уровне проверки учётных данных, а не на какой-то одной поверхности, и блокировка, заработанная где угодно, действует везде. (REQ-1393)
Ограничение включено по умолчанию — пять неудач за пять минут блокируют субъект на пятнадцать минут — и настраивается в auth.login_throttle. Заблокированному субъекту отказывают до того, как учётные данные вообще будут рассмотрены, а успешная аутентификация очищает историю этого субъекта.
Ключ — это принципал, который несёт протокол. Поверхность, работающая только с bearer-токенами, принципала не несёт, поэтому ключом становится дайджест самих учётных данных; это останавливает неограниченное воспроизведение одного скомпрометированного токена. Хранилище — на процесс, поэтому развёртывание с несколькими рабочими процессами API допускает до max_attempts на каждый процесс: ограничение — тормоз для подбора, а не распределённая квота.
Адресация организации в проводных протоколах¶
При мультиарендности организация адресуется по имени узла: acme.provisa.dev — это организация acme. По HTTP это имя приходит в заголовке Host. Клиент pgwire или Bolt такого заголовка не отправляет, но он отправляет имя узла, к которому подключался, в TLS ClientHello, и Provisa читает организацию оттуда. (REQ-1234) В клиенте ничего не меняется — достаточно подключиться к acme.provisa.dev.
Имя узла — это запрос, а не выдача прав. Оно попадает в тот же резолвер, что и заголовок Host, а тот отказывает в любой организации, в которой аутентифицированный принципал не состоит и на которую не имеет межорганизационного права. Подключение к имени узла, в организации которого у вас нет членства, не даёт доступа к данным. Клиент, подключившийся по IP-адресу, имени узла не отправляет и разрешает организацию только по принципалу — так работает каждое соединение в развёртывании с одной организацией.
gRPC, Arrow Flight и MCP передают свои сертификаты библиотекам, не предоставляющим обратного вызова по имени узла; эти транспорты называют организацию заголовком метаданных x-provisa-org.
Режим высокой безопасности¶
security.mode: high в provisa.yaml утверждает одну гарантию: бэкенд Provisa никогда не работает с данными в открытом виде. (REQ-693) Каждая значимая колонка зашифрована в источнике, и прочитать её может только клиент, владеющий ключом расшифровки. У этой гарантии есть последствия, которые развёртывание должно учесть.
Что делает этот режим:
- Конечные точки данных требуют доказательства расшифровки на клиенте. Всё под
/data/возвращает 403, если вызывающая сторона не предъявляет заголовокX-Provisa-KMS-Key— признак клиента JDBC или Python, настроенного на локальную расшифровку. Браузер или REST-потребитель, работающий с открытым текстом, такого ключа не несёт и получает отказ. Шлюз — запрет по умолчанию для всего дерева: маршрут, добавленный завтра, закрывается в день выпуска, а исключение приходится обосновывать. - Конечные точки метаданных схемы остаются открытыми.
/data/sdl,/data/introspection,/data/schema-version,/data/domains,/data/protoи/data/compileне возвращают строк данных, а клиент должен прочитать схему — включая то, какие поля помечены@encrypted, — прежде чем вообще сможет подключиться. - gRPC и Arrow Flight продолжают работать при том же доказательстве. Это транспорты, которыми в самом деле пользуются шифрующие клиенты; закрыв их, развёртывание с высокой безопасностью осталось бы без проводного протокола. Вызов данных на любом из них должен нести тот же ключ KMS в метаданных вызова.
- pgwire, Bolt и MCP не запускаются. Ни у одного из трёх нет рукопожатия на соединение, способного нести контекст расшифровки: набор строк pgwire и результат Cypher идут по сети открытым текстом, а вызов инструмента MCP передаёт результаты модели в виде текста. Настроенный порт для любого из них при запуске отклоняется, а не обслуживается.
- Защиту связей обойти нельзя. И
ignore_relationships, иrelationship_guard: falseигнорируются; см. Управление связями.
Как убедиться, что развёртывание в этом режиме: его называет журнал запуска, запрос /data/sql без ключа KMS отвечает 403 с сообщением, называющим REQ-693, а порты pgwire, Bolt и MCP не слушают.
Хук утверждения ABAC¶
Необязательный внешний хук политики, срабатывающий перед выполнением запроса. (REQ-203) Когда он настроен, Provisa обращается к вашему движку политик, передавая идентичность пользователя, роли, таблицы, колонки и операцию. Ответ определяет, будет ли запрос выполнен. (REQ-203)
Область действия¶
Хук срабатывает, только когда запрос затрагивает таблицу или источник, попавшие в область действия, — для всего остального накладных расходов нет. (REQ-204)
| Конфигурация | Действие |
|---|---|
auth.approval_hook.scope: all |
Хук вызывает каждый запрос |
sources[].approval_hook: true |
Хук вызывают все таблицы этого источника |
tables[].approval_hook: true |
Хук вызывает эта таблица |
Протоколы¶
Поддерживаются три транспорта: (REQ-246)
| Тип | Сценарий использования | Поле конфигурации |
|---|---|---|
webhook |
Любая служба политик, работающая по HTTP (OPA, собственная) | url |
unix_socket |
OPA или sidecar политик на той же машине | socket_path + url |
grpc |
Высокопроизводительная служба политик, размещённая рядом | url (host:port) |
Транспорт gRPC использует контракт provisa.auth.ApprovalService, определённый в provisa/auth/approval.proto. Реализуйте эту службу в своём движке политик: (REQ-246)
service ApprovalService {
rpc Evaluate (ApprovalRequest) returns (ApprovalResponse);
}
message ApprovalRequest {
string user = 1;
repeated string roles = 2;
repeated string tables = 3;
repeated string columns = 4;
string operation = 5;
}
message ApprovalResponse {
bool approved = 1;
string reason = 2;
}
Канал gRPC постоянный — один канал на экземпляр Provisa, переиспользуемый для всех вызовов этой конечной точки хука. (REQ-555)
Запрос и ответ¶
Все три транспорта несут одну и ту же полезную нагрузку: (REQ-246)
| Поле | Тип | Описание |
|---|---|---|
user |
string | Идентичность аутентифицированного пользователя |
roles |
string[] | Роли пользователя в Provisa |
tables |
string[] | Идентификаторы таблиц, упомянутых в запросе |
columns |
string[] | Колонки, выбранные в запросе |
operation |
string | "query" или "mutation" |
Транспорты webhook и Unix-сокета обмениваются JSON. Ответ должен содержать approved (bool) и, необязательно, reason (string). (REQ-246)
Тайм-аут и резервное поведение¶
auth:
approval_hook:
type: grpc # webhook | grpc | unix_socket
url: "localhost:50051"
timeout_ms: 500 # default 5000
fallback: deny # allow | deny — applied on timeout or error
scope: "" # "" = use per-table/per-source flags; "all" = every query
При тайм-ауте или ошибке транспорта применяется политика fallback. (REQ-247) Предохранитель (по умолчанию: размыкается после 5 последовательных отказов, полуоткрыт через 30 с) не даёт медленной конечной точке хука вызвать каскадные отказы. (REQ-556)
Пример конфигурации¶
auth:
approval_hook:
type: webhook
url: "http://opa.internal:8181/v1/data/provisa/allow"
timeout_ms: 300
fallback: deny
sources:
- id: analytics_pg
approval_hook: true # all tables on this source require hook approval
tables:
- id: salary_data
approval_hook: true # this table always requires hook approval
Секреты¶
Учётные данные используют синтаксис ${env:VAR_NAME}, разрешаемый во время выполнения. (REQ-557) Пароли никогда не хранятся в базе данных конфигурации. (REQ-557)
О полной службе секретов — хранилищах, синтаксисе ссылок и провайдерах — см. Секреты.