# PD Guard — модуль безопасности персональных данных Прокси-модуль между системой-потребителем и внешней языковой моделью (LLM). Идентифицирует персональные данные (ПДН) в тексте, маскирует их перед отправкой в модель и восстанавливает исходные значения в ответе. Ни один фрагмент ПДН не покидает контур в открытом виде. ``` Система-потребитель → Модуль (идентификация → маскирование → LLM → демаскирование) → Потребитель ``` --- ## Возможности - **Идентификация ПДН** — 30+ типов: ФИО, паспорт РФ, загранпаспорт, водительское удостоверение, военный билет, свидетельство о рождении, полис ОМС, ИНН, СНИЛС, телефон, email, адрес (страна/индекс/город/улица/дом/квартира), дата рождения, место рождения, гражданство, банковская карта, CVV, PIN, срок действия карты, держатель карты, банковские реквизиты (счёт, БИК, ОГРН, ОГРНИП, КПП), доход, биометрия. - **Три режима маскирования** — звёздочки, токенизация, синтетические данные. - **Демаскирование** — восстановление исходного текста по `payload_id`. - **Гибкая настройка под систему** — список систем-потребителей, перечень типов ПДН, факт маскирования и демаскирования настраиваются без правки кода. - **Контекстное маскирование** — типы, опасные только в сочетании с другими ПДН (PIN + номер карты), маскируются по настраиваемому правилу. - **Устойчивость к вариациям** — регистр, форматы дат (числом и текстом), разделяющие слова («серия … номер …»), уменьшительные формы имён. - **Защита от ложных срабатываний** — «поэт Александр Пушкин» и адрес отделения банка не считаются данными клиента. - **Производительность** — адаптивный лимитер конкурентности, ~9 500 RPS на одном узле при p95 ≈ 14 мс. --- ## Быстрый старт ### Требования - Java 21 - Maven 3.8+ (или обёртка `./mvnc`) - Docker + Docker Compose (для контейнерного запуска и мониторинга) ### Сборка и запуск ```bash # сборка fat-jar ./mvnc package -DskipTests # запуск java -jar target/pd-guard-spring-1.0.0.jar ``` Сервис поднимется на `http://localhost:8080`. Проверка готовности: ```bash curl http://localhost:8080/health # → OK ``` ### Запуск через Docker Compose ```bash # одиночный узел + Prometheus + Grafana docker compose up -d --build # кластер: 2 узла + nginx-балансировщик + Redis + мониторинг docker compose --profile cluster up -d --build ``` --- ## API ### Маскирование / демаскирование `POST /process` Направление определяется по `payload_id`: неизвестный идентификатор — маскируем, ранее выданный текст маски — возвращаем исходный текст. ```bash # маскирование curl -X POST http://localhost:8080/process \ -H "Content-Type: application/json" \ -H "X-System-Id: crm" \ -d '{"payload":"Клиент Иванов Иван Иванович, паспорт 4509 123456, тел +7 916 123-45-67","payload_id":"doc-1"}' # демаскирование — тот же payload_id и замаскированный текст curl -X POST http://localhost:8080/process \ -H "Content-Type: application/json" \ -H "X-System-Id: crm" \ -d '{"payload":"Клиент [FIO_1], паспорт 45** ****56, тел +7 *** ***-**-**","payload_id":"doc-1"}' ``` Заголовки: | Заголовок | Назначение | |-----------|------------| | `X-System-Id` | имя системы-потребителя; нет — применяется `default` | | `X-System-Key` | общий секрет системы; проверяется, если задан в настройках | Коды ответа: `200` — успех, `400` — невалидный запрос, `403` — система выключена или неверный ключ, `429` — перегрузка (с `Retry-After`). ### Прокси к LLM (демо-плечо) `POST /proxy` — показывает всю цепочку: что ушло в модель, что она вернула и что получил потребитель с восстановленными значениями. ```bash curl -X POST http://localhost:8080/proxy \ -H "Content-Type: application/json" \ -d '{"prompt":"Клиент Иванов Иван Иванович, карта 4111 1111 1111 1111. Кратко опишите профиль."}' ``` ### Служебные | Метод | Путь | Назначение | |-------|------|------------| | GET | `/health` | проба готовности | | GET | `/admin/config` | действующие настройки систем | | GET | `/admin/types` | список поддерживаемых типов ПДН | | POST | `/admin/reload` | перечитать `config/systems.json` без перезапуска | | GET | `/actuator/health` | Spring Boot health | | GET | `/actuator/metrics` | метрики Micrometer | | GET | `/actuator/prometheus` | метрики в формате Prometheus | --- ## Настройка систем-потребителей Файл `config/systems.json` (путь задаётся свойством `pdguard.systems-file`). Перечитывается на лету через `POST /admin/reload`. ```json { "default": { "enabled": true, "demask": true, "maskMode": "MASK", "types": ["*"], "requireCompanion": ["CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY", "ACCOUNT_NUMBER", "BIK", "OGRN", "OGRNIP", "KPP", "INCOME", "BIOMETRIC"] }, "crm": { "enabled": true, "demask": false, "maskMode": "TOKEN", "types": ["FIO", "PHONE", "EMAIL", "ADDRESS_CITY", "ADDRESS_STREET", "ADDRESS_HOUSE", "ADDRESS_FLAT"] }, "analytics": { "enabled": true, "demask": false, "maskMode": "SYNTHETIC", "types": ["*"] } } ``` Поля политики: | Поле | Назначение | |------|------------| | `enabled` | разрешено ли системе обращаться в модуль | | `demask` | выполнять ли обратное преобразование | | `maskMode` | `MASK` (звёздочки), `TOKEN` (токены), `SYNTHETIC` (синтетика) | | `types` | типы ПДН к маскированию; `"*"` — все известные | | `requireCompanion` | типы, маскируемые только вместе с ПДН другого типа | | `key` | общий секрет системы (проверяется через `X-System-Key`) | Новые типы ПДН добавляются через справочник правил (`RuleRegistry`) без переписывания ядра. --- ## Логирование и метрики ### Логи В журнал попадают только идентификатор, типы ПДН и их количество. **Значения ПДН не логируются ни на одном уровне.** ``` payload_id=doc-1 символов=64 найдено={FIO=1, PASSPORT=1, PHONE=1} ``` ### Метрики (Prometheus, `/actuator/prometheus`) | Метрика | Назначение | |---------|------------| | `pdguard.process` | длительность обработки (разрез по направлению и системе) | | `pdguard.pd.detected` | счётчик найденных ПДН по типу и системе | | `pdguard.requests.rejected` | отклонённые запросы (перегрузка/невалидные/запрет) | | `pdguard.concurrency.limit` | текущий потолок конкурентности | | `pdguard.concurrency.in.flight` | запросов в обработке | | `pdguard.store.chars` | объём хранилища соответствий | | `pdguard.tokens.processed` | оценка обработанных токенов (для TPS) | Готовый дашборд Grafana и конфигурация Prometheus — в каталоге `monitoring/`. --- ## Производительность Нагрузочное тестирование проведено инструментом k6 (профиль 500 → 1000 → 1500 → 2000 VU, одиночный узел, Docker): | Метрика | Значение | |---------|----------| | Пропускная способность | ~9 500 RPS | | Задержка p50 | 1.03 мс | | Задержка p95 | 13.87 мс | | Ошибки | 0.00 % | Целевой уровень из задания (latency ≤ 0.5 с при RPS 1000) выполнен с большим запасом. В кластерном режиме (2 узла + nginx) пропускная способность выше. --- ## Ограничения - Хранилище соответствий по умолчанию — в памяти (`pdguard.store.backend=memory`), сбрасывается при перезапуске. Для кластера используется Redis. - NER-модель второй ступени (`models/rubert-ner`) не входит в репозиторий и скачивается скриптом `tools/fetch-ner-model.sh`; без неё сервис работает на правилах. - Демаскирование доступно только системам с `demask: true` и корректным ключом. --- ## План развития - Подключение NER-модели для распознавания имён и адресов в свободном тексте. - Расширение перечня документов, удостоверяющих личность. - Настраиваемые правила контекстного маскирования через конфиг. - Шифрование хранилища соответствий. - Интеграция с CI/CD и автоматический прогон нагрузочных тестов.