feat: ui + docs: для жюри
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# Схема архитектуры и настройки
|
||||
|
||||
Модуль стоит между системой-потребителем и внешней языковой моделью. Потребитель отдаёт текст один раз на вход и один раз на выход. В модель уходит уже подменённый текст, потребителю возвращается текст с восстановленными значениями.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
consumer["Система-потребитель"]
|
||||
api["POST /process и POST /proxy"]
|
||||
policy["Политика системы\nconfig/systems.json"]
|
||||
rules["Правила и словари"]
|
||||
ner["Вторая ступень\nNER, если включена"]
|
||||
filters["Отсев ложных\nсрабатываний"]
|
||||
masker["Маскирование\nMASK / STRICT / TOKEN / SYNTHETIC"]
|
||||
store["Хранилище соответствий\nпамять, AES-GCM"]
|
||||
llm["Внешняя LLM\nили заглушка"]
|
||||
|
||||
consumer --> api --> policy --> rules --> ner --> filters --> masker --> store
|
||||
masker -->|"только /proxy"| llm
|
||||
llm -->|"демаскирование по токенам"| consumer
|
||||
store -->|"демаскирование по payload_id"| consumer
|
||||
```
|
||||
|
||||
`POST /process` — контракт для системы-потребителя: маскирование и демаскирование. `POST /proxy` — демонстрация всей цепочки до модели и обратно; проверяющий контур может его не использовать.
|
||||
|
||||
Настройки систем, перечень типов и вид маски задаются файлом `config/systems.json`. Код для смены политики пересобирать не нужно. Файл перечитывается при изменении и по `POST /admin/reload`.
|
||||
|
||||
## Что происходит с одним текстом
|
||||
|
||||
1. По заголовку `X-System-Id` выбирается политика. Неизвестное имя получает политику `default`. Выключенная система и неверный `X-System-Key` получают `403` до обработки текста.
|
||||
2. Если `payload_id` уже есть в хранилище этой системы, направление определяется сравнением текста с сохранённой маской и с исходником.
|
||||
3. Иначе текст проходит детекцию. Сначала правила: контрольные суммы (карта, ИНН, СНИЛС, ОГРН), однозначные форматы (email, телефон), затем шаблоны с якорным словом (документ, адрес, дата, ФИО). Цифровые значения с нестандартными разделителями дополнительно собираются в кластер и проверяются той же контрольной суммой.
|
||||
4. Если включена вторая ступень, модель смотрит только непокрытые кандидаты: цепочки слов с заглавной буквы и, для юридической модели, цифровые кластеры. В поставке по умолчанию ступень выключена (`pdguard.ner.*-engine: off`). В `compose.yaml` она включается, если в каталог `models/` положены веса.
|
||||
5. Ложные срабатывания снимаются до маски: имя в составе организации и на вывеске, общеизвестное имя без других персональных данных рядом, адрес отделения, типы-спутники без самостоятельного персонального данного.
|
||||
6. Оставшиеся фрагменты заменяются по `maskMode` системы. Пара «исходный текст ↔ маска» пишется в хранилище этой системы. Исходный текст в хранилище шифруется AES-GCM, ключ — `pdguard.store.encryption-key`.
|
||||
|
||||
Демаскирование не запускает детектор заново: по `payload_id` (и запасным отпечатком маски) достаётся сохранённый исходник. Поэтому звёздочки тоже обратимы, пока жива запись.
|
||||
|
||||
## Системы-потребители
|
||||
|
||||
Файл `config/systems.json`, путь переопределяется свойством `pdguard.systems-file`.
|
||||
|
||||
| Система | Включена | Демаскирование | Режим | Типы |
|
||||
|---------|----------|----------------|-------|------|
|
||||
| `default` | да | да | `MASK` | все (`*`) |
|
||||
| `crm` | да | нет | `TOKEN` | ФИО, телефон, email, город, улица, дом, квартира |
|
||||
| `analytics` | да | нет | `SYNTHETIC` | все (`*`) |
|
||||
| `strict` | да | да | `STRICT` | все (`*`) |
|
||||
| `legacy-billing` | нет | нет | `MASK` | все (`*`), запросы отклоняются |
|
||||
|
||||
Запрос без заголовка идёт в `default`. Хранилище у каждой системы своё: одна система не читает соответствия другой.
|
||||
|
||||
Поля политики:
|
||||
|
||||
| Поле | Назначение |
|
||||
|------|------------|
|
||||
| `enabled` | `false` — модуль отвечает `403` и текст не обрабатывает |
|
||||
| `demask` | разрешено ли обратное преобразование |
|
||||
| `maskMode` | чем заменяется найденное значение |
|
||||
| `types` | какие типы маскировать; `"*"` — все, которые знает сборка |
|
||||
| `requireCompanion` | типы, которые маскируются только рядом с самостоятельным персональным данным |
|
||||
| `key` | общий секрет; если задан, заголовок `X-System-Key` обязан совпасть. Только ASCII |
|
||||
|
||||
Пример добавления системы — дописать объект и перечитать файл:
|
||||
|
||||
```json
|
||||
"dms": {
|
||||
"enabled": true,
|
||||
"demask": true,
|
||||
"maskMode": "MASK",
|
||||
"types": ["FIO", "PHONE", "MEDICAL_POLICY", "BIRTH_DATE"],
|
||||
"key": "dms-secret"
|
||||
}
|
||||
```
|
||||
|
||||
После `POST /admin/reload` запросы с `X-System-Id: dms` и `X-System-Key: dms-secret` маскируют только перечисленные типы, звёздочками, и умеют демаскировать.
|
||||
|
||||
Новый тип персональных данных добавляется правилом в реестре (`DocumentRules`, `FinanceRules`, `DateRules`, `FioRules`, `ContactRules`, `AddressRules`) и именем в `PdTypes`. Политики, где указано `"*"`, подхватывают его без правки конфига. Политика с явным списком — только если имя типа туда добавить.
|
||||
|
||||
## Типы персональных данных
|
||||
|
||||
Список отдаёт `GET /admin/types`. Группы:
|
||||
|
||||
| Группа | Типы |
|
||||
|--------|------|
|
||||
| Человек | `FIO`, `CARDHOLDER` |
|
||||
| Документы | `PASSPORT`, `PASSPORT_ISSUER`, `PASSPORT_DATE`, `DEPT_CODE`, `FOREIGN_PASSPORT`, `DRIVER_LICENSE`, `MILITARY_ID`, `BIRTH_CERTIFICATE`, `MEDICAL_POLICY` |
|
||||
| Контакты | `PHONE`, `EMAIL` |
|
||||
| Адрес | `ADDRESS_COUNTRY`, `ADDRESS_POSTCODE`, `ADDRESS_CITY`, `ADDRESS_STREET`, `ADDRESS_HOUSE`, `ADDRESS_FLAT`. `ADDRESS_REGION` и `ADDRESS_DISTRICT` размечает только модель второй ступени |
|
||||
| Даты и гражданство | `BIRTH_DATE`, `BIRTH_PLACE`, `DATE`, `CITIZENSHIP` |
|
||||
| Платёжные данные | `CARD`, `CARD_EXPIRY`, `CVV`, `PIN` |
|
||||
| Реквизиты | `INN`, `SNILS`, `ACCOUNT_NUMBER`, `BIK`, `OGRN`, `OGRNIP`, `KPP` |
|
||||
| Прочее | `INCOME`, `BIOMETRIC` |
|
||||
|
||||
Правила устойчивы к регистру, к дате числом и словами, к вставке слов между серией и номером документа, к уменьшительным формам имён. Карта, ИНН, СНИЛС, ОГРН и ОГРНИП без верной контрольной суммы не маскируются.
|
||||
|
||||
## Режимы маскирования
|
||||
|
||||
Режим задаётся полем `maskMode` и действует на все типы, которые политика разрешила.
|
||||
|
||||
| Режим | Пример для `Иванов Иван Иванович` и паспорта `4509 123456` | Когда уместен |
|
||||
|-------|--------------------------------------------------------------|---------------|
|
||||
| `MASK` | `И. И. И.`, паспорт `45** ****56` | человеку остаётся узнаваемый контур, середина закрыта. Края коротких серий (загранпаспорт, военный билет, свидетельство о рождении) не открываются. CVV и PIN закрываются целиком |
|
||||
| `STRICT` | сплошные звёздочки на всю длину, включая ФИО | ничего из исходных знаков не остаётся |
|
||||
| `TOKEN` | `[FIO_1]`, `[PASSPORT_1]` | однозначная обратимая подстановка, удобная и для демаскирования ответа модели |
|
||||
| `SYNTHETIC` | вымышленные ФИО и номер той же формы | модель видит правдоподобный текст. Для типов без своей подстановки остаётся токен |
|
||||
|
||||
Одинаковое исходное значение внутри одного текста получает одну и ту же замену.
|
||||
|
||||
В `POST /proxy` режим системы для отправки в модель заменяется на `TOKEN`: одинаковые звёздочки нельзя однозначно вернуть на место в ответе модели.
|
||||
|
||||
## Контекстное правило
|
||||
|
||||
Типы из `requireCompanion` сами по себе персональными данными не считаются. Они маскируются, только если в том же тексте есть находка самостоятельного типа.
|
||||
|
||||
В политиках `default` и `strict` спутники такие: `CVV`, `PIN`, `DATE`, `BIRTH_PLACE`, `ADDRESS_COUNTRY`, `ACCOUNT_NUMBER`, `BIK`, `OGRN`, `OGRNIP`, `KPP`, `INCOME`, `BIOMETRIC`.
|
||||
|
||||
Два спутника друг друга не подтверждают. Дата рядом с ОГРН без имени и документа человека не маскируется. PIN рядом с номером карты — маскируется. Список спутников у каждой системы свой; пустой список означает, что маскируется всё найденное из `types`.
|
||||
|
||||
Отдельно от этого списка снимаются ложные ФИО и адреса: «Александр Пушкин» без других персональных данных, «Институт Склифосовского», адрес отделения банка. Если рядом с общеизвестным именем есть другой тип персональных данных, имя остаётся замаскированным: однофамилец защиту не теряет.
|
||||
|
||||
## Состояние и наблюдаемость
|
||||
|
||||
Хранилище соответствий по умолчанию — память процесса (`pdguard.store.backend=memory`): потолок `pdguard.store.max-chars` (512 МБ символов), срок жизни записи `pdguard.store.ttl-minutes` (30 минут). При `backend=redis` та же запись дублируется в Redis, чтобы демаскирование попало на другой узел. В текущем `compose.yaml` поднят один узел, Redis не используется.
|
||||
|
||||
Одновременные запросы ограничивает адаптивный предел: он растёт, пока задержка укладывается в `pdguard.target-latency-ms` (200 мс), и сжимается, когда перестаёт. Лишние запросы получают `429`, очередь не копится.
|
||||
|
||||
Метрики отдаёт Micrometer на `/actuator/prometheus`. Готовые Prometheus и Grafana лежат в `monitoring/` и поднимаются тем же `docker compose`.
|
||||
Reference in New Issue
Block a user