HashiCorp Vault
Общие сведения
Хранилище HashiCorp реализует в платформе централизованное и безопасное управление конфиденциальными данными (секретами), такими как пароли, ключи доступа и токены.
Особенности:
Безопасность: Снижается риск утечки секретов. Данные не хранятся в открытом виде в конфигурационных файлах (таких как
backend.properties) и не передаются через переменные окружения вручную.Централизация: Все секреты хранятся в одном защищенном месте (Vault), что упрощает их управление, аудит и ротацию.
Динамичность: Платформа может автоматически получать актуальные версии секретов, которые могут периодически меняться (например, в соответствии с политиками безопасности).
Гибкость: Предоставляется механизм для создания обработчиков и для других систем хранения секретов в будущем.
Ранее для подстановки значений в файл backend.properties использовались два вида интерполяций:
Через переменные окружения:
org.unidata.mdm.system.password.postgres = ${POSTGRES_PASSWORD}
Через указание на другое свойство в этом же файле. В такое случае подставлялось значение другой переменной:
org.unidata.mdm.data.nodes = 0:node0:postgres@#[org.unidata.mdm.system.password.postgres]
Для управления и изменения файла backend.properties воспользуйтесь конфигурациями и логами.
Ссылки на внешние источники
Добавлен новый способ - ссылки на внешние источники. Для указания, что значение свойства должно быть получено из внешнего источника, используется специальный формат ссылки.
Синтаксис ссылки:
#[scheme:path#field]
scheme— схема, идентифицирующая обработчик (источник). Для Hashicorp Vault используется схемаvault.path— путь к секрету в хранилище.field(опционально) — конкретное поле внутри секрета. Если секрет представляет собой единое значение, а не JSON-объект, этот параметр может не потребоваться.
Примеры ссылок:
#[vault:/unidata/postgres#password]— получить значение поля password из секрета, расположенного по пути/unidata/postgres.
#[another-vault:property_name]— пример ссылки для другого обработчика.
Как это работает:
При старте платформы значения всех свойств в
backend.properties, которые соответствуют формату ссылки, будут заменены на реальные значения, полученные от соответствующего обработчика.
Примеры использования
В файле backend.properties:
# Ссылаемся на пароль от БД в Vault
org.unidata.mdm.system.password.postgres = #[vault:/unidata/postgres#password]
# Используйте это свойство для построения строки подключения.
# Платформа сначала разрешит ссылку выше, а затем подставит итоговое значение.
org.unidata.mdm.data.nodes = 0:node0:postgres@#[org.unidata.mdm.system.password.postgres]
Внимание
На данный момент ссылки на внешние источники поддерживаются через файлы backend.properties, .env , docker-compose.yaml. Указание их в пользовательских, системных параметрах или параметрах операций обработано не будет.
Получение значений из хранилища реализовано для:
Параметров URL подключений к базе данных.
Параметров логина и пароля OpenSearch.
Camel-плейсхолдеров (параметры в двойных фигурных скобках в .xml файлах Camel).
Ссылки на хранилище допустимы в параметрах:
org.unidata.mdm.system.password.postgres=${POSTGRES_PASSWORD}– пароль PostgreSQL.
org.unidata.mdm.system.extension.schema=${EXTENSION_SCHEMA:public}– схема PostgreSQL.
org.unidata.mdm.search.admin.login=${SEARCH_ADMIN_LOGIN:admin}– логин суперпользователя OpenSearch.
org.unidata.mdm.search.admin.password=${SEARCH_ADMIN_PASSWORD:admin}– пароль суперпользователя OpenSearch.
org.unidata.mdm.core.email.server_host=${EMAIL_SERVER_HOST:localhost}– адрес почтового сервера.
org.unidata.mdm.core.email.server_port=${EMAIL_SERVER_PORT:5025}– порт почтового сервера.
org.unidata.mdm.core.email.username=${EMAIL_USERNAME:unidata@example.com}– логин почтового сервера.
org.unidata.mdm.core.email.password=${EMAIL_PASSWORD:password}– пароль почтового сервера.
org.unidata.mdm.core.email.ssl.enable=${EMAIL_SSL_ENABLE:true}– включение SSL/TLS для соединения с почтовым сервером.
org.unidata.mdm.core.email.starttls.enable=${EMAIL_STARTTLS_ENABLE:false}– включение STARTTLS для соединения с почтовым сервером.
com.universe.mdm.notifications.messaging.endpoint=${SMART_ETL_NOTIFICATION_ENDPOINT:smart-etl-consumer}– адрес получателя ETL нотификаций.
Переменные среды:
POSTGRES_ADDRESS– адрес PostgreSQL.
POSTGRES_USERNAME– логин PostgreSQL.
DATABASE_NAME– имя базы данных PostgreSQL.
Инструменты для реализации собственного обработчика
Для создания обработчика секретов из внешней системы, отличной от HashiCorp Vault, в модуль org.unidata.mdm.system добавлены следующие интерфейсы и классы. Они позволяют интегрировать с платформой новые источники секретов, используя единый механизм разрешения ссылок.
Компонент |
Тип |
Назначение |
SecretStore |
Интерфейс |
Базовый интерфейс, определяющий обработчик. |
SecretLifecycleService |
Интерфейс |
Сервис высокоуровневого управления жизненным циклом секретов. |
SecretStoreRegistry |
Класс |
Реестр всех зарегистрированных в системе обработчиков. |
SecretUpdateService |
Класс |
Сервис для уведомления подписчиков об обновлении секретов. |
Интерфейс org.unidata.mdm.system.service.SecretStore
Это основной интерфейс, который должен быть реализован для создания нового обработчика. Он определяет следующие ключевые методы:
getScheme(): Возвращает строку с именем схемы, которую обрабатывает данный обработчик (например, vault). Именно по этой схеме в ссылке #[scheme:...] будет выбран данный обработчик.read(String link): Основной метод для получения значения свойства по его полной ссылке. Именно здесь реализуется логика обращения к внешней системе за секретом.evict(String link): Метод, который уведомляет обработчик о том, что значение по данной ссылке более не актуально и должно быть удалено из любых внутренних кэшей.
Интерфейс org.unidata.mdm.system.service.SecretLifecycleService
Это сервис более высокого уровня абстракции, чем SecretStore. Его готовая реализация уже присутствует в платформе.
Назначение: Этот сервис должны использовать другие классы внутри платформы, когда им требуется получить значение свойства по его ссылке.
Взаимодействие: Он не реализует логику обращения к конкретным хранилищам, а делегирует запрос в
SecretStoreRegistry, чтобы тот вызвал соответствующий схеме ссылки обработчикSecretStore.
Класс org.unidata.mdm.system.service.impl.secret.SecretStoreRegistry
Этот класс выступает в роли центрального реестра или диспетчера всех зарегистрированных в системе обработчиков SecretStore.
Функция: Содержит коллекцию всех реализаций
SecretStore.Использование: Когда
SecretLifecycleServiceтребуется получить значение по ссылке, реестр ищет обработчик, схема которого(getScheme())соответствует схеме в ссылке, и вызывает его методread.Прозрачность: Благодаря этому классу, сервисам верхнего уровня не нужно знать о конкретных реализациях обработчиков.
Класс org.unidata.mdm.system.service.impl.secret.SecretUpdateService
Этот класс реализует механизм уведомлений об изменениях в значениях секретов.
Функция: Содержит список зарегистрированных слушателей (listeners), которые хотят быть уведомлены об обновлении конкретных свойств.
Метод
notifyUpdate(String link): При вызове этого метода сервис находит всех слушателей, заинтересованных в получении обновлений для данной ссылки, и оповещает их. Это полезно, например, для очистки кэшей или перезагрузки конфигурации на лету.
Настройка обработчика для HashiCorp Vault
Для активации обработчика HashiCorp Vault необходимо выполнить предварительную настройку.
Предварительные требования:
Настроенный и доступный кластер HashiCorp Vault;
Включенный KV2 secret engine;
Настроенный способ аутентификации AppRole;
На стороне платформы должна быть установлена переменная окружения
VAULT_ENABLED="true". Без этого обработчик не будет инициализирован.
Конфигурационные параметры
Параметры настройки задаются в файле backend.properties или через соответствующие переменные окружения.
Основные настройки подключения:
# Адрес HashiCorp Vault /
org.unidata.mdm.system.vault.uri = ${VAULT_URI:}. По умолчанию отсутствует.# Префикс KV2 хранилища /
org.unidata.mdm.system.vault.kv-prefix = ${VAULT_KV_PREFIX:kv}. Префикс KV2 (key-value version 2) хранилища. По умолчанию "kv".# Имя secret engine (допустимы kv1, kv2) /
org.unidata.mdm.system.vault.secret-engine = ${VAULT_SECRET_ENGINE:}. Имя хранилища, в котором находятся секреты. Допустимые значения: "kv1", "kv2".# Namespace (только для Enterprise версии) /
org.unidata.mdm.system.vault.namespace = ${VAULT_NAMESPACE:}. Значение заголовка "X-Vault-Namespace" (поддерживается только в Enterprise версии).# Способ авторизации /
org.unidata.mdm.system.vault.auth.type = ${VAULT_AUTH_TYPE:appRole}. Способ авторизации. По умолчанию "appRole".
Настройки аутентификации AppRole:
# Путь к файлу с role_id /
org.unidata.mdm.system.vault.role-id.file = ${VAULT_ROLE_ID_FILE:}. Путь до файла с role_id - аналог логина для авторизации в AppRole.# Путь к файлу с secret_id /
org.unidata.mdm.system.vault.secret-id.file = ${VAULT_SECRET_ID_FILE:}. Путь до файла, содержащего secret_id - аналог пароля для авторизации в AppRole. Файл может отсутствовать - в таком случае будет предпринята попытка получить secret_id из wrapped token, указываемого в файле, за путь до которого отвечает следующее свойство. Если файл присутствует, а хранимый в нем secret_id был использован для успешной авторизации, то файл будет удален.# Путь к файлу с wrapped токеном (обязателен, если не указан secret-id.file) /
org.unidata.mdm.system.vault.wrapped-token.file = ${VAULT_WRAPPED_TOKEN_FILE:}. Путь до файла с wrapped токеном - токен, который позволяет получить secret_id. Одноразовый, не может быть использован после истечения TTL. Значение параметра не может быть пустым, если HashiCorp Vault активирован. Значение всегда должно соответствовать пути до файла в формате JSON. Самого файла может не существовать, если присутствует secret_id, но по указанному пути будет периодически записываться обновленное значение токена, то есть файл будет создан.# TTL wrapped токена в секундах /
org.unidata.mdm.system.vault.wrapped-token.ttl.seconds = ${VAULT_WRAPPED_TOKEN_TTL_SECONDS:3600}. TTL для wrapped токена в секундах. Указывается при создании нового wrapped token из существующего для актуализации. По умолчанию соответствует 1 часу.
Настройки производительности и обновлений:
# Cron-выражение для периодического обновления wrapped токена /
org.unidata.mdm.system.vault.wrapped-token.rewrap.cronex = ${VAULT_WRAPPED_TOKEN_REWRAP_CRONEX:0 0 * * * ?}. Cron-выражение, по которому работает операция обновления wrapped токена. По умолчанию операция запускается каждый час.# Cron-выражение для проверки обновлений версий секретов в Vault /
org.unidata.mdm.system.vault.secret.scan.cronex = ${VAULT_SECRET_SCAN_CRONEX:0 0/5 * * * ?}. Сron-выражение, по которому работает операция обновления версии значения свойства. KV2 - это версионированное хранилище секретов. После каждого обновления значения, версия увеличивается, поэтому для отслеживания обновления нужно периодически сравнивать версии. По умолчания операция запускается каждые 5 минут.# TTL кэша секретов на стороне платформы (в секундах) /
org.unidata.mdm.system.vault.secret.cache.default.ttl.seconds = ${VAULT_SECRET_CACHE_DEFAULT_TTL:30}. TTL значения свойства в кэше, чтобы не так часто запрашивать значение у хранилища.
Настройки для Transit Secret Engine:
# Имя маунта (префикс пути), по которому монтировано transit хранилище (используется только с KV1) /
org.unidata.mdm.system.vault.secret.transit.mount = ${VAULT_TRANSIT_MOUNT:}. Имя маунта (префикс пути), по которому монтировано transit хранилище (используется только с KV1)# Имя ключа в transit хранилище, которым будут шифроваться значения /
org.unidata.mdm.system.vault.secret.transit.fingerprint.key = ${VAULT_TRANSIT_FINGERPRINT_KEY:}. Имя ключа в transit хранилище, которым будут шифроваться значения.# Название HMAC механизма, которым будут шифроваться значения /
org.unidata.mdm.system.vault.secret.transit.hmac.algorithm = ${VAULT_TRANSIT_HMAC_ALGO:}. Название HMAC механизма, которым будут шифроваться значения. Подробнее в оф. документации
Форматы файлов с учетными данными
Файл role_id: обычный текстовый файл в кодировке UTF-8, содержащий только role_id:
8a033270-c348-4199-46dd-815ed32e51f0
Файл secret_id: обычный текстовый файл в кодировке UTF-8, содержащий только secret_id:
b21f50a9-c678-7492-cf72-d179405825ac
Файл wrapped_token: файл в формате JSON:
{"secret_id":"hvs.CAESICUvwh0-_ThkiDvNaBbxHRzs9IyvWQS9IO9cmdI8X3f1Gh4KHGh2cy5DS0pxTGlGU3RlQWZma09qeGFkT3FYRU4"}