Аудитория: суперадминистратор, DPO, интегратор. Закрывает вопрос: «как сменить соль обезличивания, не разорвав связку обезличенный ID ↔ сотрудник?». Связано: 02 — ПДн и Сейф, 17 — Топология Сейфа.
Обезличивание построено так: user_id = HMAC(ANON_SALT → ключ_арендатора, табельный) (ГОСТ «Стрибог», ключ на арендатора). Смена `ANON_SALT` меняет ВСЕ `user_id`.
Один и тот же user_id хранится в двух местах, и они обязаны совпадать:
| Контур | Где user_id |
|---|---|
| Портал (db) | sam.masked_users, sam.activity_facts, sam.jml_events, sam.provisioning_jobs, sam.software_requests.requester_user_id, sam.user_entitlements |
| Сейф (vaultdb) | vault.user_identity (ключ), vault.disclosure_log |
Если просто сменить соль, новые user_id перестанут совпадать со старыми в БД → раскрытие в Сейфе даст 404, факты активности «осиротеют», метрики поедут. Поэтому ротация — это контролируемая операция обслуживания с одновременной реанонимизацией обоих контуров.
Перерасчёт может сделать только контур Сейфа: у него есть и соль, и табельный (tabel_no). У портала табельных нет (так и задумано), поэтому пересчитатьuser_idон не может — он лишь применяет готовую картустарый_user_id → новый_user_id.
Инструмент:vault-service/app/rotate_salt.py(контур Сейфа) — строит картуold→newизtabel_noподNEW_ANON_SALTи применяет её однимUPDATE-statement к Сейфу и порталу. Без--apply— СУХОЙ ПРОГОН (печатает план, ничего не меняет). Ниже — регламент с этим инструментом.
- Подозрение на компрометацию
ANON_SALT. - Регламентная смена ключевого материала по политике ИБ заказчика.
0. Резервные копии обеих БД. db и vaultdb — раздельно (deploy/backup.sh и deploy/backup-vault.sh). Без бэкапа не начинать.
1. Остановить запись. Остановить collector (прекратить приём фактов) и перевести портал в режим обслуживания/только чтение, чтобы user_id не менялись во время миграции.
2. Сухой прогон — построить и проверить карту, ничего не меняя (в контуре Сейфа):
docker compose -f docker-compose.lic.yml --env-file .env.lic \
exec -T -e NEW_ANON_SALT='<новая-соль>' \
-e ADMIN_DATABASE_URL="postgresql://postgres:<pw>@db:5432/licenziar" \
vault python -m app.rotate_salt # печатает: идентичностей / к пересчёту3. Боевой прогон — применить карту одновременно к порталу и Сейфу (флаг --apply):
docker compose ... exec -T -e NEW_ANON_SALT='<новая-соль>' \
-e ADMIN_DATABASE_URL="postgresql://postgres:<pw>@db:5432/licenziar" \
vault python -m app.rotate_salt --apply # печатает число затронутых строк по таблицамИнструмент обновит все 6 столбцов портала (sam.masked_users/activity_facts/jml_events/ provisioning_jobs/software_requests.requester_user_id/user_entitlements) и Сейф (vault.user_identity, vault.disclosure_log) — каждый одним UPDATE-statement через временную карту (уникальные ключи не нарушаются: новые id уникальны, старое/новое множества не пересекаются).
4. Сменить `ANON_SALT` в окружении воркеров контура (collector, dbinit, vault) на то же <новая-соль> и перезапустить их. Хранить только в env/секрет-менеджере (не в БД/логах/git).
5. Снять обслуживание, запустить сбор, проверить:
- раскрытие в Сейфе по новому
user_idвозвращает корректные ФИО/табельный; - метрики/инвариант целы, «осиротевших» фактов нет (нет
activity_factsбезmasked_users); - новый сбор пишет уже с новой солью (совпадает с обновлёнными хешами).
user_idвходит в уникальный ключactivity_facts (tenant, sys, user, date)и в PK
masked_users — при массовом UPDATE старого на новый возможны конфликты, если множества старых и новых значений пересекаются. Применять через таблицу-карту (UPDATE … FROM map) и при риске пересечения — двухфазно (через временный префикс), чтобы не нарушить уникальность.
- Каждый
UPDATE— в транзакции; сверять число затронутых строк с числом идентичностей. - Любая ошибка на шагах 3–4 → rollback обоих контуров и восстановление из бэкапа (шаг 0).
- Порядок важен: сначала карта (Сейф), затем портал, затем Сейф, в конце — смена env-соли.
- Не менять
ANON_SALT«на горячую» без шагов 2–4 — это тихо разорвёт связку и раскрытие
перестанет работать.
- Не выносить
tabel_no/соль за контур Сейфа ради ротации — реанонимизация выполняется внутри
Сейф-контура, наружу уходит только карта old→new (это не ПДн).
Связанные: 02 — ПДн и Сейф · 17 — Топология Сейфа · 08 — Эксплуатация и бэкап.