Тип: проектный документ границы (аналог docs/admin/16-integration-boundary.md, но граница здесь — аттестационная, а не интеграционная). Основание: docs/23-enclave-tokenization-gap.md (дефициты D8, D9), docs/22 Эпик F. Цель документа: зафиксировать целевую архитектуру выноса функций токенизации и схемы vault в отдельный сервис идентификации, чтобы основной backend оперировал только хеш-суррогатами. Это инженерная задача минимизации области аттестации ФСТЭК по 152-ФЗ. Статус: проект топологии (до реализации F3). Код в этом шаге не меняется.
Сейчас функция токенизации (anonymize) и схема vault живут в одном процессе и одной БД с основным backend. По строгому чтению 152-ФЗ обработка идентификаторов в оперативной памяти узла включает этот узел в область аттестации → в область попадает весь backend + БД. Цель F — сжать область до компактного сервиса идентификации (1–2 ВМ), оставив портал, аналитику, дашборды и фронт вне области. Архитектура это уже допускает без пересборки: токенизация — один чокпойнт, схема sam уже хеш-онли, vault — уже отдельная схема со своей RLS-политикой (docs/23 §4).
| Зона | Что внутри | Видит сырьё? | В области аттестации? |
|---|---|---|---|
| Сервис идентификации (новый) | anonymize + схема vault + 5 кромочных точек приёма | да (единственный носитель) | да — и только он |
| Основной backend (портал) | схема sam, аналитика, эффективность, заявки/ЖЦ, отчёт (A), экспорт (C) | нет (только хеши) | нет (цель F3) |
| Фронт / Next | контракт, дашборды, печать | нет | нет |
| Инфраструктура (IaaS) | железо/гипервизор/ЦОД | нет | наследуемый аттестат (D12) |
Принцип: сырой идентификатор поступает сразу в сервис идентификации, превращается в хеш-суррогат и дальше по системе идёт только суррогат. Прямой запрос DPO на раскрытие связи идёт в сервис идентификации напрямую, без транзита через портал.
коллектор/клиент ──сырой tabel_no──► [Сервис идентификации: anonymize + vault] ──hash──► backend (sam)
DPO-консоль ──────── запрос раскрытия ───────────────────────────────────────► [Сервис идентификации]| Свойство | Сейчас (логический изолят) | Цель F3 (физический сервис) |
|---|---|---|
| Процесс | тот же FastAPI, что и портал | отдельный FastAPI-процесс/контейнер |
| Роль БД | licenziar_app (общая) | выделенная роль vault_app (только схема vault) |
| Порт | общий с API | отдельный внутренний порт, наружу не публикуется |
| Сетевой сегмент | сеть backend | выделенный сегмент (VLAN/VPC), доступ только от портала и DPO-канала |
| Схема БД | vault в общей БД | vault в отдельной БД/инстансе (предпочтительно) либо отдельная роль с FORCE RLS |
| Контракт | внутренние вызовы anonymize() | внутренний API: tokenize(tenant, id) → hash, resolve(tenant, hash, basis) → identity |
Неизменно (гардрейлы): токенизация — ГОСТ Р 34.11-2012 (Стрибог-256), соль из env, не в БД и не в логах; vault под RLS FORCE + политика vault_dpo_only; раскрытие пишет аудит ФЗ-152 всегда; ноль внешних обращений; ноль копилефта.
Точные адреса в коде (подтверждено чтением, docs/23 §3):
| # | Точка | Файл:строка | Что переедет |
|---|---|---|---|
| 1 | Приём телеметрии | ingest_routes.py:65 | вызов anonymize → запрос tokenize к сервису |
| 2 | Создание заявки | workflow.py:42 | то же |
| 3 | Приём кадровых событий | workflow.py:274 | то же |
| 4 | Раскрытие связи (DPO) | workflow.py:390 | resolve идёт прямо в сервис, минуя портал |
| 5 | Загрузка идентификаций | init_db.py:213 | переезжает в инициализацию сервиса |
После переезда основной backend получает на вход уже хеши; вызовов anonymize и обращений к vault.* в коде портала не остаётся.
Внутренний API (вызывается только из доверенного сегмента, наружу не публикуется):
| Метод | Вход | Выход | Кто вызывает |
|---|---|---|---|
POST /tokenize | {tenant, tabel_no} | {user_id_hash} | backend на кромке приёма (точки 1–3) |
POST /tokenize/batch | {tenant, [tabel_no]} | {[user_id_hash]} | пакетный приём телеметрии/HR |
POST /resolve | {tenant, user_id_hash, basis, principal} | {tabel_no, full_name} + запись аудита | DPO-консоль напрямую |
Свойства контракта:
tokenizeдетерминирован для пары (соль, tenant, tabel_no) — те же суррогаты, что сейчас
(инвариант artek 0.6585/6 не двигается).
resolveдоступен только ролиdpo, всегда пишет записьVAULT_RESOLVEв аудит ФЗ-152.- Контракт двусторонне совместим с текущим
anonymize()→ переезд не меняет значения хешей.
| Слой | Чья ответственность | Аттестация |
|---|---|---|
| ЦОД, железо, гипервизор, сеть провайдера | IaaS-провайдер | аттестат провайдера (наследуется) |
| Гостевая ОС сервиса идентификации | наша | СЗИ-от-НСД ставим и сопровождаем мы |
| PostgreSQL сервиса + прикладной код | наша | аттестуется наш прикладной контур |
| Сегментация сети контура (VLAN/VPC) | совместно | фиксируется в модели угроз (docs/24) |
При проверке нужна подписанная матрица разделения ответственности (этот раздел — её черновик).
- В области аттестации остаётся сервис идентификации (1–2 ВМ) вместо «портал + БД +
аналитика + фронт».
- Единый доверенный сегмент (портал и сервис в одном контуре одного ЦОД) делает требование
канального шифрования между ними неактуальным — это обосновывается в модели угроз (docs/24), а не закрывается тяжёлой крипто-инфраструктурой.
- Аналитика, дашборды, отчёт (A), экспорт (C), весь контракт фронта — не трогаются.
- Завести роль БД
vault_appс доступом только к схемеvault(FORCE RLS сохраняется). - Поднять сервис идентификации (отдельный контейнер, внутренний порт, сегмент).
- Реализовать контракт
tokenize/resolve(§4) поверх существующегоanonymizeиvault. - Перенаправить 5 кромочных точек (§3) на вызовы сервиса; убрать
anonymize/vault.*из портала. - Включить контракт-гейт F2 «backend принимает/отдаёт только хеши» (следующий шаг).
- Прогнать: инвариант artek
0.6585/6цел, Static==Http, все тесты зелёные.
- F1/F2 — топология и контракт-гейты «портал отдаёт/принимает только хеши» (готово).
- F3a — сервис идентификации поднят аддитивно (роль
vault_app, контракт tokenize/identity/
resolve, golden-vector). Миграция 011.
- F3b — полный cutover: портал-
apiвыведен из ИСПДн (принимает только хеши, resolve в
Сейфе, migration 012 отзывает у licenziar_app доступ к vault, api без ANON_SALT).
- F3c — контур аккредитации сжат до Сейф-образа. Весь код, касающийся сырых ПДн
(anonymize, коллекторы, фикстуры, сидинг), физически вынесен в подпакет app/enclave/ + seed_data.py/init_db.py. Docker-таргет `portal` (api/web) ФИЗИЧЕСКИ удаляет эти модули из образа (rm -rf app/enclave app/seed_data.py app/init_db.py app/make_fixture.py); таргет `enclave` (dbinit/collector) их содержит. Доказательно: в portal-образе grep -r anonymize app/ --include='*.py' → нет кода (только доккомментарии), нет .pyc удалённых модулей; гейт test_api_import_graph_excludes_enclave_and_seed держит границу.
- F4-db — отдельный инстанс БД Сейфа (готово, проверено live). Носитель ПДн и журнал
раскрытий вынесены в отдельный Postgres `vaultdb` (свой том, сегмент vaultnet): таблица vault.user_identity (FK на sam.tenants снят), собственный журнал vault.disclosure_log, дублированные RLS-функции vault.current_*, роль vault_app заводит vault-init. В БД портала схемы `vault` НЕТ вовсе (миграция 001 очищена, 011/012 — тостоуны). Сейф пишет раскрытия в свой журнал (ноль egress: 0 строк VAULT_RESOLVE в sam.fz152_audit); DPO смотрит историю через GET /disclosures. Схема: vault-service/migrations/001_vault_schema.sql.
Контур ИСПДн (рантайм, после F4-db): vault-service (носитель связки) + vaultdb (инстанс ПДн) + collector/dbinit (enclave-образ, токенизируют/сидят хеши в sam). Портал api/web/nginx и его БД db — вне ИСПДн (нет кода токенизации, нет ANON_SALT, нет схемы vault ни в коде, ни в БД).
Остаётся инфра-шагом (не код): разместить инстанс Сейфа на отдельной ВМ/аттестованном IaaS-152, настроить бэкап-контур. Это финализирует независимо аттестуемый узел.
Связанные документы: docs/23-enclave-tokenization-gap.md (карта 5 точек, дефициты), docs/24-private-threat-model-draft.md (модель угроз, обоснование единого контура), docs/admin/16-integration-boundary.md (аналог для интеграционной границы), docs/admin/01-not-skzi-crypto.md (позиционирование «не СКЗИ»), docs/admin/02-pii-fz152-vault.md (текущая работа vault и аудита).