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"}