Skip to content

Интеграция с Vault

В этой главе рассматривается интеграция Apiary с Hashicorp Vault. В частности:

  • Каким образом можно настроить хранение секретов в Vault;
  • Алгоритм переноса существующих секретов в Vault;
  • Особенности реализации интеграции с Vault.

Общая конфигурация интеграции с Vault

Для конфигурации работы с Vault необходимо выполнить реконфигурацию. В таблице ниже приведены свойства, которые поддерживаются механизмом интеграции Apiary и Vault.

Свойство Назначение и комментарии
vault.address Адрес сервера vault. Обязательное поле.
vault.role_id.value Значение role_id. Обязательное поле.
vault.secret_id.wrapped.file_path Путь к файлу с запакованным (wrapped) секретом.
Обязательное поле, если секрет не распаковывается (unwrapping) автоматически сторонним способом.
vault.secret_id.unwrapped.file_path Путь к файлу с распакованным (unwrapped) секретом.
Обязательное поле.
vault.secret_id.unwrap.engine_type Тип движка Vault.
Возможные значения: kv-v1, kv-v2, approle.
Дефолтное значение — kv-v2.
Обязательное поле, если используется запакованный (wrapped) секрет или авторотация секретов.
vault.secret_id.unwrap.field Наименование поля для хранения распакованного (unwrapped) секрета.
Обязательное поле, если используются kv-v1 и kv-v2.
Для approle дефолтное значение — secret_id.
vault.secret_id.unwrap.cacert Путь к файлу CA сертификата для доступа к vault серверу по https.
Если путь не задан, то используются корневые сертификаты ОС.
vault.secret_id.unwrap.capath Путь к директории с сертификатами CA для доступа к серверу vault по https.
Если путь не задан, то используются корневые сертификаты ОС.
vault.auth.mount_point Полный путь к логину.
Может быть как однословным, например, my-approle-1 — так и в виде пути, например, my/dep/approle.
Обязательное поле если используется авторотация.

Как правило, не нужно использовать все эти свойства. В следующей главе приведены примеры конкретных конфигураций. Вы можете выбрать наиболее подходящий для вас.

Примеры конфигураций

Пример 1. Если секрет попадает запакованным (wrapped) на машину с Apiary, и его нужно распаковать (unwrap) средствами Apiary:

[main]
vault.address = https://my-vault.example.com:8200 
vault.auth.mount_point = my-approle-1 
vault.role_id.value = 7d4714da-1c96-97be-a1ce-c19158d2a1ef 
vault.secret_id.unwrapped.file_path = /opt/vault_secret/secret_id 
vault.secret_id.wrapped.file_path = opt/vault_secret/wrapped_secret_id 
vault.secret_id.unwrap.engine_type = approle

Пример 2. Если секрет попадает уже распакованным (unwrapped) на машину с Apiary:

[main]
vault.address = https://my-vault.example.com:8200
vault.auth.mount_point = my-approle-1 
vault.role_id.value = 7d4714da-1c96-97be-a1ce-c19158d2a1ef 
vault.secret_id.unwrapped.file_path = /opt/vault_secret/secret_id 

Не забудьте выполнить реконфигурацию, как описано в главе Реконфигурация, чтобы изменения вступили в силу.

Распаковка секрета

Если секрет запакованный (wrapped), и Apiary настроен, как в примере 1 выше, то этот секрет автоматически распаковывается при старте приложения. Эту операцию можно выполнить вручную при помощи утилиты vault в составе Apiary:

/opt/hw-fh/bin/vault unwrap 

⚠️ Примечание: для работы этой утилиты требуется утилита jq (легкий и гибкий JSON-процессор командной строки). Убедитесь, что у вас установлена jq на машине с Apiary. В большинстве дистрибутивов jq доступен в базе пакетов.

Убедитесь, что jq и прочие необходимые утилиты установлены в вашей ОС. Выполните команду:

/opt/hw-fh/bin/vault check-config

Вы также можете распаковать секрет вашими средствами автоматизации с использованием /opt/hw-fh/bin/vault. Если вы делаете это при помощи ваших средств автоматизации, посмотрите главу Особенности взаимодействия с Vault.

Назначение путей хранения паролей в Vault для интеграций

После того как общая интеграция с Vault настроена, можно перенести хранение паролей к сервисным аккаунтам AD, SMTP сервера и Jira сервера, используемых Apiary.

Вы могли сохранить в Vault пароли к этим сервисным аккаунтам и настроить там на стороне Vault авторотацию. Это может быть отдельный секрет или поле в другом секрете — на ваш выбор.

Войдите в WebUI в Apiary как администратор, откройте настройки AD и в поле Пароль сервисного аккаунта введите параметры подключения к Vault:

engine=kv-v2,mount_point=app/apiary-1,path=ad_accounts,key=ad_service 

В данном примере:

  • kv-v2 — тип secret engine, которое используется в вашем Vault. В настоящее время в Apiary поддерживаются kv-v1 и kv-v2.
  • app/apiary-1 — путь к vault secret engine (также известно как mount point), которое вы использовали, когда активировали этот secret engine в vault.
  • ad_accounts — путь к секрету внутри secret engine, в котором хранятся ваши секреты.
  • ad_service — имя ключа, в котором хранится пароль к сервисному аккаунту.

В настоящее время все четыре поля (engine, mount_point, path, key) являются обязательными.

После того как вы изменили пароль к сервисному аккаунту к AD, выполните Проверку соединения.

Если Проверка соединения выполнена успешно, это означает, что:

  • Секрет для аутентификации в vault был успешно распакован (если предоставлялся запакованным) или просто успешно прочитан Apiary.

  • При помощи указанного role_id и этого секрета удалось корректно залогиниться в Vault.

  • Vault secret engine по указанному в mount_point пути существует и доступно на чтение для этой approle.

  • Версия этого Vault secret engine указана корректно.

  • Путь внутри этого secret engine указан корректно.

  • Указанный ключ с паролем по указанному пути нашёлся.

  • В этом ключе хранится корректный пароль к серверу AD, и сервисному аккаунту удалось залогиниться в AD и получить список пользователей.

Если Проверка соединения оказалась неуспешной, необходимо проверить корректность введенных данных на каждом шаге, указанном выше.

Аналогичным образом вы можете задать пути для секретов для соединения с сервером Jira и SMTP.

Перенос существующего секрета в Vault

Если у вас уже был настроен Apiary, и все работало, вам может потребоваться перенести секреты, которые сейчас в настоящее время хранятся в ini-файлах в Vault. В данной главе описана последовательность действий для переноса остальных секретов.

Общая последовательность действий для каждого секрета выглядит так:

  1. Получить существующее значение секрета при помощи /opt/hw-fh/bin/get-config-value из существующего в ini-файлах свойства (список свойств смотрите ниже).

  2. Создать в Vault секрет с этим значением. Это может быть отдельный секрет или поле в существующем секрете — на ваш выбор.

  3. Сформировать полный путь этого секрета в Vault, включая mount-point, path, key.

  4. Сохранить полный путь секрета в новое свойство (см. список ниже) в /opt/hw-fh/config/user.ini.

    Пример пути:

    VAULT;engine=kv_v1,mount_point=role_v1_t,path=app/apiary,key=cipher_key
    
  5. Удалить старое свойство при помощи /opt/hw-fh/bin/rm-config-value из ini-файлов.

  6. Для применения настроек выполните реконфигурацию.

Обратите внимание, что после применения настроек в файлах local.ini появятся новые значения для свойств, которые вы удалили. Эти значения автоматически генерируются при каждой реконфигурации системы, однако эти значения не применяются и не являются секретами для каких-либо сервисов.

Свойства, которые нужно задать для переноса паролей и ключа шифрования в Vault:

  • из sensdata.encrypt.key в sensdata.encrypt.key_path.

  • из f.postgres.password в f.postgres.password_path.

  • из d.postgres.password в d.postgres.password_path.

  • из rmq.password в rmq.password_path.

Теперь подробнее о каждом свойстве.

Свойство sensdata.encrypt.key_path

  • Путь в хранилище секретов Vault к ключу шифрования чувствительных данных, таких как пароли соединений проектов HiveApiary. Если путь не задан, используется ключ шифрования, указанный в свойстве sensdata.encrypt.key. При некорректном указании пути появляется сообщение об ошибке в логе приложения.

  • Указывается как строка в формате <ENGINE>;<PATH>, где ENGINE — в настоящее время только VAULT, а PATHengine=<engine_type>,mount_point=<mount_point>,path=<path>,key=<key>.

  • Пример: VAULT;engine=kv_v1,mount_point=role_v1_t,path=new/default,key=cipher_key

Примечания:

  • Используйте get-config-value sensdata.encrypt.key, чтобы получить существующий ключ шифрования.
  • После переноса ключа шифрования в Vault рекомендуем удалить свойство при помощи утилиты rm-config-value sensdata.encrypt.key.
  • Ключи шифрования для Hive и Apiary различаются, хотя свойство имеет одинаковое имя.
  • Изменение ключа шифрования для существующих коннектов не предусмотрено.

Свойство f.postgres.password_path

  • Путь в хранилище секретов Vault к паролю встроенной БД postgresql сервиса. Если путь не задан, используется пароль, указанный в свойстве f.postgres.password. При некорректном указании пути появляется сообщение об ошибке в логе Apiary.

  • Указывается как строка в формате <ENGINE>;<PATH>, где ENGINE — в настоящее время только VAULT, а PATHengine=<engine_type>,mount_point=<mount_point>,path=<path>,key=<key>.

  • Пример: VAULT;engine=kv_v2,mount_point=role_v2_t,path=app/customer-portal/main_pg,key=password

Примечания:

  • Используйте get-config-value f.postgres.password, чтобы получить существующий пароль.
  • После переноса пароля в Vault рекомендуем удалить свойство при помощи утилиты rm-config-value f.postgres.password.
  • Изменить пароль в контейнере со встроенным postgresql можно вручную, если требуется.

Свойство d.postgres.password_path

  • Путь в хранилище секретов Vault к паролю встроенной БД postgresql depot сервиса. Если путь не задан, используется пароль, указанный в свойстве d.postgres.password. При некорректном указании пути появляется сообщение об ошибке в логе Apiary.

  • Указывается как строка в формате <ENGINE>;<PATH>, где ENGINE — в настоящее время только VAULT, а PATHengine=<engine_type>,mount_point=<mount_point>,path=<path>,key=<key>.

  • Пример: VAULT;engine=kv_v2,mount_point=role_v2_t,path=app/customer-portal/depot_pg,key=password

Примечания:

  • Используйте get-config-value d.postgres.password, чтобы получить существующий пароль.
  • После переноса пароля в Vault рекомендуем удалить свойство при помощи утилиты rm-config-value d.postgres.password.
  • Изменить пароль в контейнере со встроенным postgresql можно вручную, если требуется.

Свойство rmq.password_path

  • Путь в хранилище секретов Vault к паролю встроенного брокера сообщений rabbitmq. Если путь не задан, используется пароль, указанный в свойстве rmq.password. При некорректном указании пути появляется сообщение об ошибке в логе Apiary.

  • Указывается как строка в формате <ENGINE>;<PATH>, где ENGINE — в настоящее время только VAULT, а PATHengine=<engine_type>,mount_point=<mount_point>,path=<path>,key=<key>.

  • Пример: VAULT;engine=kv_v2,mount_point=role_v2_t,path=app/customer-portal/rmq,key=password

Примечания:

  • Используйте get-config-value rmq.password_path, чтобы получить существующий пароль.
  • После переноса пароля в Vault рекомендуем удалить свойство при помощи утилиты rm-config-value rmq.password.
  • Изменить пароль в контейнере со встроенным rabbitmq можно вручную, если требуется.

Особенности взаимодействия с Vault

Как Apiary логинится в Vault:

  • role_id — хранится на диске в ini-файле и передаётся в контейнер в виде переменной среды окружения.

  • Секрет для аутентификации распаковывается только один раз при старте Apiary.

  • Запакованный secret_id — хранится на диске в директории, определённой администратором системы. Используется только для распаковки секретов.

  • Распакованный secret_id — хранится на диске в директории, определённой администратором системы. Передаётся в контейнер как ReadOnly том (volume).

  • Поскольку при старте Apiary запускается несколько контейнеров, и в каждом контейнере запускается несколько процессов, которым требуется логин в Vault, файл с unwrapped_secret_id может быть прочитан несколько раз. Логин в Vault будет совершён каждым из этих процессов. Поэтому мы рекомендуем использовать большое или бесконечное число раз, с которым Apiary может логиниться в Vault.

  • При этом часть процессов (для background-обработки) могут запускаться не сразу при старте Apiary, а по мере потребности.

  • После успешного логина каждый процесс получает токен для доступа к секрету и далее работает с ним.

Поведение при истечении времени использования токена и секрета AppRole:

  • При истечении времени использования токена Apiary выполняет автоматический повторный логин в Vault при помощи того же самого секрета.

  • При невозможности повторного логина с тем же секретом производится попытка повторного чтения распакованного секрета.

Особенности поведения при ротации секрета для аутентификации в Vault:

  • Повторная автоматическая распаковка запакованного секрета в случае, если Apiary не смогла залогиниться в Vault с имеющимся секретом, не предусмотрен.

  • Если ваша система автоматического развёртывания меняет секрет для логина в Vault и доставляет запакованный секрет на машину с Apiary, та же самая система должна будет распаковывать его, вызвав скрипт наподобие /opt/hw-fh/bin/vault unwrap.

  • Если распакованный секрет будет доставляться на машину другими способами (не через наш скрипт), убедитесь, что inode этого файла не изменяется.

Пример sh-скрипта для изменения содержимого файла без изменения его inode:

touch "${unwrapped_secret_file}"
truncate -s 0 "${unwrapped_secret_file}"
chmod 644 "${unwrapped_secret_file}"
echo "${new_unwrapped_value}" >> "${unwrapped_secret_file}"

Автоматическое обновление секрета доступа к Vault

Требования к окружению для автоматического обновления секрета Vault

Для того, чтобы автоматическое обновление (авторотация) секрета работала корректно требуется обеспечить:

  • Пользователь Vault имеющий права на обновление секрета.

    • Пример прав в формате ACL:
    path "auth/${APPROLE_MOUNT_POINT}/role/${APPROLE_NAME}/secret-id" {
      capabilities = ["update"]
    }
    
    • В данном примере значение ${APPROLE_MOUNT_POINT} нужно заменить на mount point для аутентификации роли Apiary. По умолчанию это значение approle но в вашей организации может использоваться другое значение.

    • Значение ${APPROLE_NAME} нужно заменить на имя роли Apiary выделенного в вашем Vault под Apiary.

  • Актуальный пароль пользователя должен храниться в Vault и должен быть доступен для вашей роли приложения на чтение.

  • Требуется указать в конфигурации параметры, перечисленные в параграфе ниже.

  • Требуется настроить на уровне ОС регулярный вызов скрипта авторотации. Например, при помощи cron.

Описание процесса авторотации

Процесс авторотации реализован следующим образом:

  • Скрипт авторотации секрета Apiary проверяет, что пришла пора ротировать. Если с момента обновления распакованного секрета approle прошло дней меньше, чем задано, ротация секрета не производится. Но если задана опция --force это проверка не проводится и ротация производится.
  • Скрипт авторотации секрета Apiary аутентифицируется в Vault как approle с имеющимся секретом. Обратите внимание: что бы ротация прошла успешно, секрет должен быть актуальным. Если действие секрета истекло, то автоматическая ротация не возможна. Нужно произвести ручную доставку секрета, как это описано выше.
  • Скрипт авторотации секрета Apiary получает из Vault пароль для пользователя, при помощи которого будет проводить ротацию. Требования к правам этого пользователя описаны ниже. Местоположение секрета (mount point, имя секрета и имя поля) читаются из конфигурации Apiary.
  • Скрипт авторотации секрета Apiary аутентифицируется в Vault как пользователь. Логин этого пользователя хранится в конфигурации Apiary, а пароль как раз был получен на предыдущем шаге.
  • Скрипт авторотации секрета Apiary с правами пользователя при помощи Vault получает новый секрет для approle. Секрет запакованный (wrapped) с коротким TTL (90 секунд).
  • Скрипт авторотации секрета Apiary при помощи Vault распаковывает (unwrap) его и сохраняет в тот же файл.

Конфигурации ротации секрета

Свойство
Назначение и комментарии
vault.secret.rotate
.type
Типа авторотации.
В настоящее время поддерживается
только один тип password-in-vault.
Если свойство не задано, то авторотация использоваться не будет.
vault.secret.rotate
.password.kv_type
Тип KV хранилище. По умолчанию kv-v2.
vault.secret.rotate
.password.kv_path
путь к KV хранилищу.
vault.secret.rotate
.password.secret_path
путь к секрету в KV хранилище.
vault.secret.rotate
.password.property_name
имя свойства, которое содержит пароль.
vault.secret.rotate
.username.value
значение имени пользователя.
vault.secret.rotate
.auth.path
путь к аутентификации (например, ldap).
vault.secret.rotate
.period_days
периодичность ротации в днях.
vault.approle.name Имя approle.
vault.secret_id.unwrap.field Имя поля для распаковки секрета.
Если не задано и в vault.secret_id.unwrap.engine_type задано approle
то используется значение по умолчанию secret_id.
В остальных случаях нужно задавать.

Прочие свойства, необходимые и для ротации секрета

Эти свойства перечислены выше в таблице. Для использования авторотации все эти свойства обязательные, даже если в таблице выше сказано иное.

  • vault.address
  • vault.role_id.value
  • vault.auth.mount_point
  • vault.secret_id.unwrapped.file_path
  • vault.secret_id.unwrap.engine_type

Пример конфигурации авторотации секрета

[main]
vault.address = https://my-vault.example.com:8200 
vault.secret.rotate.type = password-in-vault
vault.role_id.value = 7d4714da-1c96-97be-a1ce-c19158d2a1ef 
vault.auth.mount_point = my-approle-1 
vault.approle.name = app-app/apiary-role
vault.secret_id.unwrapped.file_path = /opt/vault_secret/secret_id 
vault.secret_id.unwrap.engine_type = approle
vault.secret.rotate.password.kv_type = kv-v2
vault.secret.rotate.password.kv_path = kv-apiary
vault.secret.rotate.password.secret_path = app/apiary
vault.secret.rotate.password.property_name = rotate_user_password
vault.secret.rotate.username.value =  user1234
vault.secret.rotate.auth.path = myldap
vault.secret.rotate.period_days = 90

После настройки (в данном случае reconfig делать не обязательно) запустите скрипт:

/opt/hw-fh/bin/vault renew

Если не заданы какие то обязательные опции, то скрипт покажет сообщение об этом.

После формирования конфигурации вы можете настроить регулярный запуск этого скрипта при помощи инструментов вашей ОС, например, cron.

Мы рекомендуем настроить периодичность запуска скрипта так, что бы гарантированно попадать в требования по частоте ротации секретов и с учётом свойства vault.secret.rotate.period_days. Например, если в вашей организации принято ротировать секреты не реже, чем 1 раз в 180 дней и не чаще, чем 150 дней то логично, будет указать в vault.secret.rotate.period_days значение 150, и запускать vault renew хотя бы 1 раз в две недели или чаще. Вы так же можете настроить и ежедневный запуск vault renew - если метка времени файла с распакованным секретом не менялась, и указанное в vault.secret.rotate.period_days количество дней не прошло, то он vault renew не будет ничего делать.

Если есть необходимость ротировать секрет игнорируя количество прошедших дней, то используйте опцию --force. Так же вы можете настроить расписание обновления секрета средствами вашей ОС и всегда вызывать vault renew --force.