pg_anon#
pg_anon
pg_anon — инструмент анонимизации для PostgreSQL
Обзор
pg_anon
является эффективным инструментом для
анонимизации данных Postgres, специально разработанным для ИТ
компаний. Эти компании часто хранят “конфиденциальные” данные, которые
включают как коммерческие тайны, так и персональную информацию пользователей,
такую как контактные номера, данные паспортов и т.д.
Инструмент оказывается полезным, когда необходимо перенести
содержимое базы данных из производственной среды в другие
среды для тестирования производительности или отладки функциональности
в процессе разработки. С pg_anon
никакие конфиденциальные данные не раскрываются, что предотвращает возможные утечки данных.
Терминология
Термин | Описание |
---|---|
Исходная база данных (исходная БД) | База данных, содержащая поля, которые необходимо анонимизировать. |
Целевая база данных (целевая БД) |
Пустая база данных для --mode=restore .
Она также может содержать структуру для
--mode=sync-data-restore .
|
Персональные (конфиденциальные) данные | Данные, содержащие информацию, которая не должна передаваться другим получателям хранения или третьим лицам и которая составляет коммерческую тайну. |
Подготовленный файл словаря с конфиденциальными данными |
Файл с расширением *.py , содержащий
объект, который описывает таблицы, поля и методы для
замены значений этих полей. Файл словаря может быть
написан вручную разработчиком или сгенерирован
автоматически.
|
Подготовленный файл словаря без конфиденциальных данных |
Файл *.py содержит список объектов,
структурированных как схема, таблица и поля. Этот словарь
используется для повторного сканирования в режиме create-dict и может
быть создан вручную разработчиком или сгенерирован
автоматически. Использование этого словаря в
режиме create-dict может значительно
ускорить последующие сканирования.
|
Мета-словарь |
Файл с расширением *.py , содержащий
объект, который описывает правила для идентификации персональных
(конфиденциальных) данных. Мета-словарь создается вручную
пользователем. На основе мета-словаря затем создается
подготовленный файл конфиденциального словаря. Процесс
автоматического создания словаря называется
“разведка” или --mode=create-dict .
|
Создание словаря (или сканирование) | Процесс сканирования исходной базы данных и поиска конфиденциальных полей на основе мета-словаря. В результате создается подготовленный файл sens dict и, при необходимости, подготовленный файл no sens dict. |
Дамп | Процесс записи содержимого исходной базы данных в файлы с использованием указанного словаря. Дамп может быть частичным или полным. На самом деле, это этап, на котором происходит маскирование. |
Восстановление | Процесс загрузки данных из файлов в целевую базу данных. Целевая база данных не должна содержать никаких объектов. |
Анонимизация (маскирование) |
Процесс клонирования базы данных, состоящий из шагов
dump -> restore , в ходе которого
конфиденциальные данные будут заменены случайными или хешированными
значениями.
|
Функция анонимизации |
Встроенная функция PostgreSQL или функция из схемы
anon_funcs , которая изменяет
входное значение на случайное или хешированное. Схема
anon_funcs содержит готовую
библиотеку функций. В эту схему можно добавлять новые
функции для преобразования полей, подлежащих анонимизации,
для последующего использования в словарях.
|
Визуальное представление терминов
Анонимизация (маскировка)
Диаграмма ниже показывает процесс передачи данных из
исходной БД
в
целевую БД
. Исходная база данных содержит
конфиденциальные данные, как правило, эта база данных находится в
промышленной среде, и строго фиксированное количество
сотрудников имеет доступ к базе данных.
Диаграмма G.2. Dump-Resore-Terms.drawio.png
pg_anon
запускается доверенным
администратором с учетными данными для подключения к
исходной базе данных
и на основе указанного
словаря выполняется дамп в указанную директорию на
файловой системе. Вопросный процесс использует заранее подготовленный
словарь, согласованный с командой безопасности.
Затем полученная директория с файлами должна быть передана
на хост целевой базы данных
. Нет необходимости
использовать сжатие при передаче директории с дампом, так как
файлы данных уже сжаты.
Когда каталог размещен на хосте с
целевой базой данных
, процесс восстановления должен
быть запущен с учетными данными
целевой базы данных
.
Целевая база данных
должна быть подготовлена заранее
с помощью команды CREATE DATABASE и не содержать никаких
объектов. Если в
целевой базе данных
есть пользовательские таблицы, процесс восстановления
не начнется. Когда восстановление будет успешно завершено, база данных
будет готова для задач разработки или тестирования,
в ходе которых к базе данных будет подключаться произвольное количество сотрудников
без риска утечки конфиденциальных данных.
P.S. --mode=sync-data-restore
можно запустить
в непустую целевую БД
с подготовленной
структурой.
Процесс создания словаря
Есть два процесса создания словаря:
Автоматическое создание словаря
Создание словаря вручную
Диаграмма ниже показывает оба процесса создания словаря.
Диаграмма G.3. Create-dict-Terms.drawio.png
Какую работу выполняет pg_anon во время дампа и восстановления? Самое простое представление.
Например, у нас есть данные, которые мы хотим анонимизировать:
Создайте
исходную
таблицу:create table users ( id bigserial, email text, login text ); -- Checking the contents of the source table select * from users;
>> id | email | login ----+---------+-------
Заполните
исходную
таблицу:insert into users (email, login) select 'user' || generate_series(1001, 1020) || '@example.com', 'user' || generate_series(1001, 1020); -- Checking the contents of the source table select * from users;
>> id | email | login ----+----------------------+---------- 1 | [email protected] | user1001 2 | [email protected] | user1002 ...
Поле «email» содержит
конфиденциальные данные
. Нам нужно
анонимизировать
их.
Каков процесс создания дампа с маскированием?
Создайте
дамп
данных изисходной
таблицы в CSV файл (без маскирования):copy ( select * from users ) to '/tmp/users.csv' with csv;
cat /tmp/users.csv >> 1,[email protected],user1001 2,[email protected],user1002 ...
Запустите
маскирование
содержимогоисходной
таблицы:select id, md5(email) || '@abc.com' as email, -- hashing the email (masking rule in prepared sens dict file) login from users;
>> id | email | login ----+------------------------------------------+---------- 1 | [email protected] | user1001 2 | [email protected] | user1002 ...
Создайте
дамп
данных изисходной
таблицы в CSV файл (смаскированием
):copy ( select id, md5(email) || '@abc.com' as email, -- hashing the email (masking rule in prepared sens dict file) login from users ) to '/tmp/users_anonymized.csv' with csv;
cat /tmp/users_anonymized.csv >> 1,[email protected],user1001 2,[email protected],user1002 ...
Файл подготовленного sens dict
содержит правила маскировки, такие как хеширование
Каков процесс восстановления замаскированного дампа?
Воспроизведите структуру. Создайте
целевую
таблицу:create table users_anonymized ( id bigserial, email text, login text ); -- Checking the contents of the target table select * from users_anonymized;
>> id | email | login ----+---------+-------
Загрузите данные
исходной
таблицыдампа
данных (CSV файл) вцелевую
таблицу:copy users_anonymized from '/tmp/users_anonymized.csv' with csv; -- Checking the contents of the target table select * from users_anonymized;
>> id | email | login ----+------------------------------------------+---------- 1 | [email protected] | user1001 2 | [email protected] | user1002 ...
Различия между оригинальной работой pg_anon и этим представлением:
pg_anon
работает со всей базой данных (а не только с одной таблицей)pg_anon
использует файлы.bin.gz
для сохранения данных (а не csv)Правила маскирования предоставляются
pg_anon
черезподготовленный файл словаря чувствительных данных
Особенности
pg_anon
работает в нескольких режимах:
init
— создает схемуanon_funcs
с функциями анонимизации.create-dict
— сканирует данные БД и создает подготовленный файл словаря с чувствительными данными с профилем анонимизации и подготовленный файл словаря без чувствительных данных для более быстрой работы в другой раз в режимеcreate-dict
.view-fields
— отображает таблицу с полями, которые будут анонимизированы, и какие правила будут использоваться для этого. Таблица содержитschema
,table
,field
,type
,dict_file_name
,rule
поля, которые основаны на подготовленном чувствительном словаре.view-data
— показывает скорректированную таблицу с примененными правилами анонимизации из подготовленного файла словаря конфиденциальных данных.дамп
— создает дамп структуры базы данных с использованием инструментаpg_dump
Postgres, и дампы данных с использованием запросовCOPY ...
с функциями анонимизации. Шаг дампа данных сохраняет данные локально в формате*.bin.gz
. Во время этого шага данные анонимизируются на стороне базы данных с помощьюanon_funcs
.восстановление
— восстанавливает структуру базы данных с помощью инструментаpg_restore
Postgres и данных из дампа в целевую БД. Режимвосстановление
может отдельно восстанавливать структуру базы данных или данные.sync-struct-dump
— создает дамп структуры базы данных с использованием инструмента Postgrespg_dump
.sync-data-dump
— создает дамп данных базы данных с использованием запросовCOPY ...
с функциями анонимизации. Шаг создания дампа данных сохраняет данные локально в формате*.bin.gz
. Во время этого шага данные анонимизируются на стороне базы данных с помощьюanon_funcs
.sync-struct-restore
— восстанавливает структуру базы данных с использованием инструмента Postgrespg_restore
.sync-data-restore
— восстанавливает данные базы данных из дампа в целевую БД.
Требования и зависимости
pg_anon
основан на Python3
и также требует сторонние библиотеки, перечисленные в
requirements.txt
.
Он использует следующие инструменты:
Tantor SE pg_dump инструмент для дампа структуры базы данных.
Tantor SE pg_restore инструмент для восстановления структуры базы данных.
Руководство по установке
Предварительные условия
Инструмент поддерживает Python3.11 и более новые версии. Код размещен в следующем репозитории: репозиторий pg_anon на Github.
Инструкции по установке
Процессы установки немного различаются в зависимости от вашей операционной системы.
macOS
Установите Python3, если он не установлен:
Установите Homebrew.
Клонируйте репозиторий:
git clone https://github.com/TantorLabs/pg_anon.git
.Перейдите в каталог проекта:
cd pg_anon
.Настройте виртуальную среду:
Установите виртуальную среду:
python3 -m venv venv
.Активируйте виртуальную среду:
source venv/bin/activate
.
Установите зависимости:
pip install -r requirements.txt
.
Ubuntu/Redhat/CentOS
Установите Python3, если он не установлен:
sudo apt-get install python3.11
(для Ubuntu),sudo yum install python311
(для Redhat/Centos).Клонируйте репозиторий:
git clone https://github.com/TantorLabs/pg_anon.git
.Перейдите в каталог проекта:
cd pg_anon
.Настройте виртуальную среду:
Установите виртуальную среду:
python3 -m venv venv
.Активируйте виртуальную среду:
source venv/bin/activate
.
Установите зависимости:
pip install -r requirements.txt
.
Windows 7/Windows 11
Установите Python3, если он не установлен: Скачайте его с официального сайта Python.
Клонируйте репозиторий:
git clone https://github.com/TantorLabs/pg_anon.git
.Перейдите в каталог проекта:
cd pg_anon
.Настройте виртуальную среду:
Установите виртуальную среду:
py -m venv venv
.Активируйте виртуальную среду:
.\venv\Scripts\activate
.
Установите зависимости:
pip install -r requirements.txt
.
Тестирование
Чтобы протестировать pg_anon
, вам необходимо иметь установленную локальную
базу данных. Этот раздел охватывает установку
postgres и запуск набора тестов.
Настройка PostgreSQL
Чтобы облегчить тестирование, вот инструкции по установке PostgreSQL на Ubuntu:
Добавьте конфигурацию репозитория:
echo "deb [arch=amd64] http://apt.postgresql.org/pub/repos/apt focal-pgdg main" >> /etc/apt/sources.list.d/pgdg.list wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
Обновите пакеты и установите PostgreSQL:
apt -y install postgresql-15 postgresql-client-15
Разрешите подключения к серверу PostgreSQL:
sed -i '/listen_addresses/s/^#//g' /etc/postgresql/15/main/postgresql.conf sed -ie "s/^listen_addresses.*/listen_addresses = '127.0.0.1'/" /etc/postgresql/15/main/postgresql.conf sed -i -e '/local.*peer/s/postgres/all/' -e 's/peer\|md5/trust/g' /etc/postgresql/${PG_VERSION}/main/pg_hba.conf
Перезапустите экземпляр PostgreSQL, чтобы изменения вступили в силу:
pg_ctlcluster 15 main restart
Создайте тестового пользователя с правами суперпользователя, чтобы разрешить выполнение команд COPY:
psql -c "CREATE USER anon_test_user WITH PASSWORD 'mYy5RexGsZ' SUPERUSER;" -U postgres
Выполнение тестов
Чтобы убедиться, что ваша настройка работает правильно, запустите модульные тесты:
export PYTHONPATH=$(pwd) python tests/test_full.py -v
При успешном выполнении вывод должен выглядеть следующим образом:
Ran N tests in ... OK
Если все тесты пройдены, приложение готово к использованию.
Чтобы запустить конкретный тестовый случай, используйте следующий шаблон:
export PYTHONPATH=$(pwd) python tests/test_full.py -v PGAnonValidateUnitTest
Конфигурация тестовой базы данных
Параметры подключения к тестовой базе данных могут быть переопределены с помощью переменных окружения:
set TEST_DB_USER=anon_test_user set TEST_DB_USER_PASSWORD=mYy5RexGsZ set TEST_DB_HOST=127.0.0.1 set TEST_DB_PORT=5432 set TEST_SOURCE_DB=test_source_db set TEST_TARGET_DB=test_target_db
Использование
Чтобы отобразить сообщение справки для CLI, выполните:
python pg_anon.py --help
Общие параметры pg_anon:
Опция | Описание |
---|---|
--debug
| Включает режим отладки (по умолчанию false) |
--verbose
| Настройка подробного режима: [info, debug, error] (по умолчанию info) |
--db-connections-per-process
| Количество подключений для операций ввода-вывода для каждого процесса (по умолчанию 4) |
--processes
| Количество процессов для многопроцессорных операций (по умолчанию 4) |
Параметры конфигурации базы данных:
Опция | Описание |
---|---|
--db-host
| Указывает ваш хост базы данных |
--db-port
| Указывает порт вашей базы данных |
--db-name
| Указывает имя вашей базы данных |
--db-user
| Указывает вашего пользователя базы данных |
--db-user-password
| Указывает пароль пользователя базы данных |
--db-passfile
| Путь к файлу, содержащему пароль, который будет использоваться при подключении к базе данных |
--db-ssl-key-file
| Путь к файлу ключа SSL клиента для безопасных подключений к базе данных |
--db-ssl-cert-file
| Путь к файлу клиентского SSL-сертификата для безопасных подключений к базе данных |
--db-ssl-ca-file
| Путь к корневому файлу SSL-сертификата. Этот сертификат используется для проверки сертификата сервера |
Запустить режим инициализации
Чтобы инициализировать схему “anon_funcs”, запустите pg_anon в режиме ‘init’:
python pg_anon.py --mode=init \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db
Запустить режим create_dict
Предварительные условия:
Сгенерированный или созданный вручную словарь
*.py
файл с профилем анонимизации“anon_funcs” создан в режиме инициализации
Чтобы создать словарь:
python pg_anon.py --mode=create-dict \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --meta-dict-file=test_meta_dict.py \ --prepared-sens-dict-file=test_sens_dict_output_previous_use.py \ --prepared-no-sens-dict-file=test_no_sens_dict_output_previous_use.py \ --output-sens-dict-file=test_sens_dict_output.py \ --output-no-sens-dict-file=test_no_sens_dict_output.py \ --processes=2
Опция | Описание |
---|---|
--meta-dict-file
| Входной файл или список файлов с правилами сканирования чувствительных и нечувствительных полей. В случае конфликта приоритет имеет первый файл в списке |
--prepared-sens-dict-file
|
Входной файл или список файлов с конфиденциальными полями, который
был получен при предыдущем использовании опции
--output-sens-dict-file или подготовлен
вручную (необязательно)
|
--prepared-no-sens-dict-file
|
Входной файл или список файлов с не чувствительными полями,
который был получен при предыдущем использовании опции
--output-no-sens-dict-file или подготовлен вручную (необязательно)
|
--output-sens-dict-file
| Файл вывода с конфиденциальными полями будет сохранен в это значение |
--output-no-sens-dict-file
| Файл вывода с несекретными полями будет сохранен в это значение (необязательно) |
--scan-mode
| Определяет, сканировать ли все данные или только их часть [“full”, “partial”] (по умолчанию “partial”) |
--scan-partial-rows
|
В --scan-mode partial определяет
количество строк для сканирования (по умолчанию 10000)
|
Требования к вводу --meta-dict-file (metadict):
Входной файл metadict.py должен содержать такую структуру:
var = { "field": { # Which fields to anonymize without scanning the content "rules": [ # List of regular expressions to search for fields by name "^fld_5_em", "^amount" ], "constants": [ # List of constant field names "usd", "name" ] }, "skip_rules": [ # List of schemas, tables, and fields to skip { # possibly some schema or table contains a lot of data that is not worth scanning. Skipped objects will not be automatically included in the resulting dictionary. Masks are not supported in this object. "schema": "schm_mask_ext_exclude_2", # Schema specification is mandatory "table": "card_numbers", # Optional. If there is no "table", the entire schema will be skipped. "fields": ["val_skip"] # Optional. If there are no "fields", the entire table will be skipped. } ], "include_rules": [ # List of schemas, tables, and fields which will be scanning { # possibly you need specific fields for scanning or you can debug some functions on specific field "schema": "schm_other_2", # Required. Schema specification is mandatory "table": "tbl_test_anon_functions", # Optional. If there is no "table", the entire schema will be included. "fields": ["fld_5_email"] # Optional. If there are no "fields", the entire table will be included. } ], "data_regex": { # List of regular expressions to search for sensitive data "rules": [ """[A-Za-z0-9]+([._-][A-Za-z0-9]+)*@[A-Za-z0-9-]+(\.[A-Za-z]{2,})+""", # email "7?[\d]{10}" # phone 7XXXXXXXXXX ] }, "data_const": { # List of constants in lowercase, upon detection of which the field will be included in the resulting dictionary. If a text field contains a value consisting of several words, this value will be split into words, converted to lowercase, and matched with the constants from this list. Words shorter than 5 characters are ignored. Search is performed using set.intersection "constants": [ # When reading the meta-dictionary, the values of this list are placed in a set container "simpson", "account" ], # List of partial constants. If field value has substring from this list, it will considered as sensitive "partial_constants": [ # When reading the meta-dictionary, the values of this list are placed in a set container "@example.com", "login_" ] }, "data_func": { # List of functions for specific field types "text": [ # Field type, which will be checked by functions bellow. Can use custom types. Also, can use common type "anyelement" for all field types. Rules for "anyelement" will be added for all types rules after their own rules. { "scan_func": "my_custom_functions.check_by_users_table", # Function for scanning field value. Scan function has fixed call signature: (value, schema_name, table_name, field_name). Also , this function must return boolean result. "anon_func": "anon_funcs.digest(\"%s\", 'salt_word', 'md5')", # Function will be called for anonymization in dump step "n_count": 100, # How many times "scan_func" have to returns "True" by values in one field. If this count will be reached, then this field will be anonymized by "anon_func" }, ], }, "data_sql_condition": [ # List of rules for define data sampling for specific tables by custom conditions { "schema": "schm_mask_ext_exclude_2", # Can use "schema" for full name matching or "schema_mask" for regexp matching. Required one of them "table_mask": "*", # Can use "table" for full name matching or "table_mask" for regexp matching. Required one of them "sql_condition": # Condition in raw SQL format. For example, we need data sample created by 2024 year """ WHERE created > '2024-01-01' AND created < '2024-12-31' """ } ], "sens_pg_types": [ # List of field types which should be checked (other types won't be checked). If this massive is empty program set default SENS_PG_TYPES = ["text", "integer", "bigint", "character", "json"] "text", "integer", "bigint", "varchar", # better write small names, because checker find substrings in original name. For example types varchar(3) contains varchar, so varchar(3) will be checked in program. "json" ], "funcs": { # List of field types (int, text, ...) and functions for anonymization # If a certain field is found during scanning, a function listed in this list will be used according to its type. "text": "anon_funcs.digest(\"%s\", 'salt_word', 'md5')", "numeric": "anon_funcs.noise(\"%s\", 10)", "timestamp": "anon_funcs.dnoise(\"%s\", interval '6 month')" } }
Запустить режим дампа
Предварительные условия:
anon_funcs
схема с функциями анонимизации должна быть создана. См. --mode init пример.Выходной файл словаря с метаинформацией о полях базы данных должен быть создан, и его анонимизация должна быть выполнена.
См. --mode create-dict.
Режимы дампа:
Чтобы создать дамп структуры и дамп данных:
python pg_anon.py --mode=dump \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --prepared-sens-dict-file=test_sens_dict_output.py
Чтобы создать дамп только структуры:
python pg_anon.py --mode=sync-struct-dump \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --output-dir=test_sync_struct_dump \ --prepared-sens-dict-file=test_sens_dict_output.py
Чтобы создать только дамп данных:
python pg_anon.py --mode=sync-data-dump \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --output-dir=test_sync_data_dump \ --prepared-sens-dict-file=test_sens_dict_output.py
Этот режим может быть полезен для планирования синхронизации базы данных, например, с
cron
.
Возможные параметры в mode=dump:
Опция | Описание |
---|---|
--prepared-sens-dict-file
|
Входной файл или список файлов с конфиденциальными полями, который
был получен при предыдущем использовании опции
--output-sens-dict-file или подготовлен
вручную
|
--dbg-stage-1-validate-dict
| Проверить словарь, показать таблицы и выполнить SQL запросы без экспорта данных (по умолчанию false) |
--dbg-stage-2-validate-data
| Проверить данные, показать таблицы и выполнить SQL-запросы с экспортом данных в подготовленную базу данных (по умолчанию false) |
--dbg-stage-3-validate-full
| Выполняет всю логику с “limit” в SQL запросах (по умолчанию false) |
--clear-output-dir
| В режиме дампа очищает выходной каталог от предыдущего дампа или других файлов (по умолчанию true) |
--pg-dump
|
Путь к инструменту Postgres pg_dump
(по умолчанию /usr/bin/pg_dump )
|
--output-dir
| Каталог для файлов дампа (по умолчанию "") |
Запустить режим восстановления
Предварительные условия:
Каждый режим требует дампа для восстановления.
Режимы восстановления:
Чтобы восстановить структуру и данные:
Этот режим требует вывода дампа, созданного в
--mode=dump
.python pg_anon.py --mode=restore \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_target_db \ --input-dir=test_dict_output \ --verbose=debug
Чтобы восстановить только структуру:
Этот режим требует вывода дампа, созданного в
--mode=sync-struct-dump
.python pg_anon.py --mode=sync-struct-restore \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_target_db \ --input-dir=test_sync_struct_dump \ --verbose=debug
Чтобы восстановить только данные:
Этот режим требует вывода дампа, созданного в
--mode=sync-data-dump
.python pg_anon.py --mode=sync-data-restore \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_target_db \ --input-dir=test_sync_data_dump \ --verbose=debug
Возможные параметры в --mode restore
:
Опция | Описание |
---|---|
--input-dir
| Входной каталог с файлами дампа, созданными в режиме дампа |
--disable-checks
| Отключает проверки дискового пространства и версии PostgreSQL (по умолчанию false) |
--seq-init-by-max-value
| Инициализирует последовательности на основе максимальных значений. В противном случае последовательности будут инициализированы на основе значений исходной базы данных. |
--drop-custom-check-constr
| Удаляет все ограничения CHECK, содержащие пользовательские процедуры, чтобы избежать снижения производительности на этапе загрузки данных. |
--pg-restore
|
Путь к инструменту Postgres pg_dump .
|
Запустить режим просмотра полей
Предварительные условия:
Выходной файл словаря с метаинформацией о полях базы данных должен быть создан, и его анонимизация должна быть выполнена.
См. --mode create-dict.
Чтобы увидеть поля в базе данных и правила анонимизации по подготовленному словарю:
python pg_anon.py --mode=view-fields \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --prepared-sens-dict-file=test_sens_dict_output.py
Предупреждение
Этот режим может обрабатывать только ограниченное количество полей без фильтров, для повышения производительности. Он задается опцией
--fields-count
с значением по умолчанию = 5000 полей. Чтобы избежать этого, используйте фильтры (--schema-name
,--schema-mask
,--table-name
,--table-mask
) или опцию--fields-count
.Если вывод будет обрезан, вы получите уведомление об этом. Но с опцией
--json
это уведомление будет отключено.
Опция | Описание |
---|---|
--prepared-sens-dict-file
|
Входной файл или список файлов с конфиденциальными полями, который
был получен при предыдущем использовании опции
--output-sens-dict-file или подготовлен
вручную
|
--view-only-sensitive-fields
| Для возврата только чувствительных полей. По умолчанию результаты содержат все поля базы данных |
--json
| Для возврата результатов в формате JSON. По умолчанию используется табличный вывод |
--fields-count
| Укажите, сколько полей будет обработано для вывода. По умолчанию = 5000 |
--schema-name
| Фильтр по имени схемы |
--schema-mask
| Фильтр по маске схемы. Может принимать регулярные выражения |
--table-name
| Фильтр по имени таблицы |
--table-mask
| Фильтр по маске таблицы. Может принимать регулярные выражения |
Запустить режим просмотра данных
Предварительные условия:
Выходной файл словаря с метаинформацией о полях базы данных должен быть создан, и его анонимизация должна быть выполнена.
См. --mode create-dict.
Чтобы увидеть таблицу в базе данных с полями анонимизации по подготовленному словарю:
python pg_anon.py --mode=view-data \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --prepared-sens-dict-file=test_prepared_sens_dict_result_expected.py \ --schema-name=public \ --table-name=contracts \ --limit=10 \ --offset=0
Предупреждение
Этот режим может обрабатывать только ограниченное количество полей, для повышения производительности. Это указывается с помощью
--limit
со значением по умолчанию = 100 и--offset
со значением по умолчанию = 0.В этом режиме вы должны указать имя схемы
--schema-name
и таблицы--table-name
.Если вывод будет обрезан, вы получите уведомление об этом. Но с опцией
--json
это уведомление будет отключено.
Опция | Описание |
---|---|
--prepared-sens-dict-file
|
Входной файл или список файлов с конфиденциальными полями, который
был получен при предыдущем использовании опции
--output-sens-dict-file или подготовлен
вручную
|
--json
| Для возврата результатов в формате JSON. По умолчанию используется табличный вывод |
--schema-name
| Имя схемы |
--table-name
| Имя таблицы |
--limit
| Сколько строк будет показано. По умолчанию limit=100 |
--offset
| Какая часть данных будет показана. По умолчанию offset=0 |
Создание словаря из строк таблицы
Если у вас есть таблица, содержащая объекты и поля для анонимизации, вы можете использовать этот SQL-запрос для создания словаря в формате json:
select jsonb_pretty( json_agg(json_build_object('schema', T.schm, 'table', T.tbl, 'fields', flds ))::jsonb ) from ( select T.schm, T.tbl, JSON_OBJECT_AGG(fld, mrule) as flds from ( select 'schm_1' as schm, 'tbl_a' as tbl, 'fld_1' as fld, 'md5(fld_1)' as mrule union all select 'schm_1', 'tbl_a', 'fld_2', 'md5(fld_2)' union all select 'schm_1','tbl_b', 'fld_1', 'md5(fld_1)' union all select 'schm_1','tbl_b', 'fld_2', 'md5(fld_2)' ) T group by schm, tbl ) T
>> [ { "table": "tbl_b", "fields": { "fld_1": "md5(fld_1)", "fld_2": "md5(fld_2)" }, "schema": "schm_1" }, { "table": "tbl_a", "fields": { "fld_1": "md5(fld_1)", "fld_2": "md5(fld_2)" }, "schema": "schm_1" } ]
Этапы отладки в режимах дампа и восстановления
Этапы отладки:
Этап 1: проверка словаря
Этот этап проверяет словарь, показывает таблицы и выполняет SQL-запросы без экспорта данных на диск или в базу данных. Таким образом, если программа работает без ошибок => этап пройден.
Диаграмма G.4. dbg-stage-1.png
python pg_anon.py --mode=dump \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --output-dir=test_dbg_stages \ --prepared-sens-dict-file=test_dbg_stages.py \ --clear-output-dir \ --verbose=debug \ --debug \ --dbg-stage-1-validate-dict
Этап 2: проверка данных
Проверка данных, отображение таблиц и выполнение SQL-запросов с экспортом данных и ограничением 100 в подготовленной базе данных. Этот этап требует базу данных со всей структурой только с условием предварительных данных, которое описано в
--prepared-sens-dict-file
.Если вы хотите создать базу данных с необходимой структурой, просто выполните:
Одноразовый дамп структуры:
python pg_anon.py --mode=sync-struct-dump \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --output-dir=test_stage_2 \ --prepared-sens-dict-file=test_dbg_stages.py \ --clear-output-dir \ --verbose=debug \ --debug \ --dbg-stage-3-validate-full
И затем столько раз, сколько вы хотите восстановить структуру:
su - postgres -c "psql -U postgres -d postgres -c \"DROP DATABASE IF EXISTS test_target_db_7\"" su - postgres -c "psql -U postgres -d postgres -c \"CREATE DATABASE test_target_db_7\"" python pg_anon.py --mode=sync-struct-restore \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_target_db_7 \ --input-dir=test_stage_2 \ --verbose=debug \ --debug
Проверка данных на этапе дампа:
Диаграмма G.5. dbg-stage-2.png
python pg_anon.py --mode=dump \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --output-dir=test_dbg_stages \ --prepared-sens-dict-file=test_dbg_stages.py \ --clear-output-dir \ --verbose=debug \ --debug \ --dbg-stage-2-validate-data
Проверка данных на этапе восстановления данных:
python pg_anon.py --mode=sync-data-restore \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_target_db_7 \ --input-dir=test_dbg_stages \ --verbose=debug \ --debug # And for example view all data in every table: su - postgres -c "psql -U postgres -d test_target_db_7 -c \"SELECT * FROM public.contracts\""
Этап 3: полная проверка
Диаграмма G.6. dbg-stage-3.png
Делает всю логику с “limit 100” в SQL запросах. На этом этапе вам не нужна подготовленная база данных, просто выполните:
su - postgres -c "psql -U postgres -d postgres -c \"DROP DATABASE IF EXISTS test_target_db_8\"" su - postgres -c "psql -U postgres -d postgres -c \"CREATE DATABASE test_target_db_8\""
Проверка полной стадии в дампе:
python pg_anon.py --mode=dump \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_source_db \ --output-dir=test_dbg_stages \ --prepared-sens-dict-file=test_dbg_stages.py \ --clear-output-dir \ --verbose=debug \ --debug \ --dbg-stage-3-validate-full
Проверка полного этапа восстановления:
python pg_anon.py --mode=restore \ --db-host=127.0.0.1 \ --db-user=postgres \ --db-user-password=postgres \ --db-name=test_target_db_8 \ --input-dir=test_dbg_stages \ --verbose=debug \ --debug # And for example view all data in every table: su - postgres -c "psql -U postgres -d test_target_db_8 -c \"SELECT * FROM public.contracts\""
Как экранировать/разэкранировать сложные имена объектов
import json j = {"k": "_TBL.$complex#имя;@&* a'2"} json.dumps(j)
>> '{"k": "_TBL.$complex#\\u0438\\u043c\\u044f;@&* a\'2"}'
s = '{"k": "_TBL.$complex#\\u0438\\u043c\\u044f;@&* a\'2"}' u = json.loads(s) print(u['k'])
>> _TBL.$complex#имя;@&* a'2
Вклад
Зависимости
pg_anon использует Poetry инструмент управления зависимостями для управления зависимостями и создания пакетов. Для добавления новых зависимостей установите Poetry и выполните команду:
poetry add <package_name>
Для блокировки зависимостей используйте команду:
poetry lock --no-update
Дополнительно, экспортируйте последние пакеты в requirements.txt с помощью плагина poetry export:
poetry export -f requirements.txt --output requirements.txt
Сборка пакета
Для сборки пакета используйте команду:
poetry build
Дополнительно пакет можно собрать с использованием setuptools:
python3 setup.py sdist
Структура проекта и каталогов
Основные каталоги:
dict/
— каталог с meta-dict, prepared-sens-dict-file и prepared-no-sens-dict-file файлами.docker/
— каталог с docker.pg_anon/
— основные модули Python.tests/
— содержит test_full.py - основной модуль тестирования.
Основная логика pg_anon содержится в следующих модулях Python:
pg_anon/pg_anon.py
— создает экземпляр класса Context и направляет программу в соответствующий модуль.pg_anon/context.py
— содержит класс Context (ctx).pg_anon/create_dict.py
— логика для--mode=create-dict
.pg_anon/dump.py
— логика для--mode=dump
,--mode=sync-struct-dump
и--mode=sync-data-dump
.pg_anon/restore.py
— логика для--mode=restore
,--mode=sync-struct-restore
и--mode=sync-data-restore
.pg_anon/view_fields.py
— логика для--mode=view-fields
.pg_anon/view_data.py
— логика для--mode=view-data
.
tree pg_anon/ -L 3
pg_anon/ ├── dict # Dir with meta-dict, prepared-sens-dict-file and prepared-no-sens-dict-file files. │ ├── mask_test.py │ ├── test_dbg_stages.py │ ├── test_empty_dictionary.py │ ├── test_empty_meta_dict.py │ ├── test_exclude.py │ ├── test_meta_dict.py │ ├── test_prepared_no_sens_dict_result_expected.py │ ├── test_prepared_sens_dict_result_expected.py │ ├── test.py │ ├── test_sync_data_2.py │ ├── test_sync_data.py │ └── test_sync_struct.py ├── docker # Dir with docker │ ├── Dockerfile │ ├── entrypoint_dbg.sh │ ├── entrypoint.sh │ ├── Makefile │ ├── motd │ └── README.md ├── examples │ ├── 1.test_db_create.sql │ ├── 2.test_db_str.sql │ ├── 3.test_db_data.sql │ └── README.md ├── __init__.py ├── init.sql ├── pg_anon # Main python modules │ ├── common │ │ ├── db_queries.py │ │ ├── db_utils.py │ │ ├── dto.py │ │ ├── enums.py │ │ ├── __init__.py │ │ └── utils.py │ ├── context.py │ ├── create_dict.py │ ├── dump.py │ ├── __init__.py │ ├── __main__.py │ ├── pg_anon.py │ ├── restore.py │ ├── version.py │ ├── view_data.py │ └── view_fields.py ├── pg_anon.py ├── poetry.lock ├── pyproject.toml ├── README.md ├── requirements.txt ├── setup.py ├── tests # Contains test_full.py - main testing module │ ├── init_env.sql │ ├── __init__.py │ ├── init_stress_env.sql │ ├── PGAnonMaskUnitTest_source_tables.result │ ├── PGAnonMaskUnitTest_target_tables.result │ └── test_full.py └── tools └── metadata_history.sql
Описание библиотеки функций для анонимизации
Все функции содержатся в файле init.sql
. После выполнения команды --mode=init
, они будут находиться в схеме anon_funcs
в исходной базе данных. Если вы хотите написать новую функцию, просто создайте ее в схеме anon_funcs
в вашей исходной базе данных.
Список некоторых функций, доступных для использования в словарях:
Добавить шум к вещественному числу:
SELECT anon_funcs.noise(100, 1.2); >> 123
Добавить шум к дате или временной метке:
SELECT anon_funcs.dnoise('2020-02-02 10:10:10'::timestamp, interval '1 month'); >> 2020-03-02 10:10:10
Хэшировать строковое значение с указанной хэш-функцией:
SELECT anon_funcs.digest('text', 'salt', 'sha256'); >> '3353e....'
Оставить первые несколько символов (2-й аргумент) и последние несколько символов (4-й аргумент) указанной строки, добавив константу (3-й аргумент) между ними:
SELECT anon_funcs.partial('123456789', 1, '***', 3); >> 1***789
Замаскировать адрес электронной почты:
SELECT anon_funcs.partial_email('[email protected]'); >> ex*****@gm*****.com
Сгенерировать случайную строку указанной длины:
SELECT anon_funcs.random_string(7); >> H3ZVL5P
Сгенерировать случайный почтовый индекс:
SELECT anon_funcs.random_zip(); >> 851467
Сгенерировать случайные дату и время в указанном диапазоне:
SELECT anon_funcs.random_date_between( '2020-02-02 10:10:10'::timestamp, '2022-02-05 10:10:10'::timestamp ); >> 2021-11-08 06:47:48.057
Сгенерировать случайные дату и время:
SELECT anon_funcs.random_date(); >> 1911-04-18 21:54:13.139
Сгенерировать случайное целое число в указанном диапазоне:
SELECT anon_funcs.random_int_between(100, 200); >> 159
Сгенерировать случайный bigint в указанном диапазоне:
SELECT anon_funcs.random_bigint_between(6000000000, 7000000000); >> 6268278565
Сгенерировать случайный номер телефона:
SELECT anon_funcs.random_phone('+7'); >> +7297479867
Сгенерировать случайный хеш, используя указанную функцию:
SELECT anon_funcs.random_hash('seed', 'sha512'); >> b972f895ebea9cf2f65e19abc151b8031926c4a332471dc5c40fab608950870d6dbddcd18c7e467563f9b527e63d4d13870e4961c0ff2a62f021827654ae51fd
Выбрать случайный элемент из массива:
SELECT anon_funcs.random_in(array['a', 'b', 'c']); >> a
Преобразовать шестнадцатеричное значение в десятичное:
SELECT anon_funcs.hex_to_int('8AB'); >> 2219
В дополнение к существующим функциям в схеме anon_funcs, также могут использоваться функции из расширения pgcrypto.
Пример использования шифрования с кодированием base64 для хранения зашифрованного значения в текстовом поле:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
SELECT encode((SELECT encrypt('data', 'password', 'bf')), 'base64'); >> cSMq9gb1vOw= SELECT decrypt( ( SELECT decode('cSMq9gb1vOw=', 'base64') ), 'password', 'bf'); >> data
Также добавление новых функций анонимизации может быть выполнено путем
добавления init.sql
в файл, а затем вызова
pg_anon в режиме --mode=init
.
Дополнительные материалы:
Презентация и практические файлы из доклада "Анонимизация данных с использованием pg_anon" на PG BootCamp Russia 2024.