Stronghold совместим с API HashiCorp Vault, поэтому миграция сводится к переносу конфигурации и данных и к переключению клиентов на новый адрес. Шифрованное хранилище Vault нельзя подключить к Stronghold напрямую: данные переносятся через API.

Цель

Перенести в Stronghold секреты KV, политики, методы аутентификации, PKI и динамические секреты из работающего HashiCorp Vault, переключить клиентов и сохранить возможность отката.

Предварительные требования

Для выполнения примера нужны:

  • Развёрнутый и распечатанный кластер Stronghold.
  • Токен HashiCorp Vault с правами read и list на переносимые пути, а также на sys/mounts, sys/auth и sys/policies/acl.
  • Токен Stronghold с административными правами.
  • Сетевая связанность между Stronghold и Vault, если используется KV-репликация.
  • Утилиты vault, d8 и jq на рабочей станции.

В примерах ниже адреса и токены задаются переменными окружения:

export VAULT_ADDR=https://vault.example.com:8200
export VAULT_TOKEN=<VAULT_TOKEN>
export STRONGHOLD_ADDR=https://stronghold.example.com
export STRONGHOLD_TOKEN=<STRONGHOLD_TOKEN>

Где:

  • <VAULT_TOKEN> — токен HashiCorp Vault с правами на чтение переносимых данных;
  • <STRONGHOLD_TOKEN> — токен Stronghold с административными правами.

Утилита vault использует только переменные VAULT_* и отправляет запросы в HashiCorp Vault. Утилита d8 stronghold копирует переменные STRONGHOLD_* в VAULT_*, поэтому STRONGHOLD_ADDR и STRONGHOLD_TOKEN переопределяют VAULT_ADDR и VAULT_TOKEN, и команды d8 stronghold отправляются в Stronghold. Задайте и STRONGHOLD_ADDR, и STRONGHOLD_TOKEN: иначе d8 stronghold отправит в Stronghold токен Vault или в Vault — токен Stronghold.

Шаг 1. Проведите инвентаризацию

Составьте перечень объектов, которые нужно перенести. Выполните команды на стороне Vault:

vault secrets list -detailed -format=json > vault-mounts.json
vault auth list -detailed -format=json > vault-auth.json
vault policy list -format=json > vault-policies.json
vault namespace list -format=json > vault-namespaces.json

Команда vault namespace list доступна только в Vault Enterprise. Если в Vault используются пространства имён, повторите инвентаризацию для каждого из них с параметром -namespace.

Для каждого mount зафиксируйте тип (kv версии 1 или 2, pki, database, transit и другие), путь и параметры (default_lease_ttl, max_lease_ttl). Для каждого метода аутентификации — тип, путь, роли и привязанные политики.

Пространства имён поддерживаются только в Stronghold EE. Если в Vault используются пространства имён, а целевая инсталляция — базовый Stronghold, перенесите содержимое каждого пространства имён в отдельные пути монтирования.

Шаг 2. Воссоздайте пространства имён и механизмы секретов

Воссоздайте в Stronghold пространства имён и механизмы секретов Vault:

  1. Если нужны пространства имён (Stronghold EE), создайте их:

    d8 stronghold namespace create team-a
  2. Включите механизмы секретов по тем же путям, что и в Vault. Например, для KV версии 2:

    d8 stronghold secrets enable -path=secret -version=2 kv

    Сохраняйте прежние пути монтирования: тогда политики и клиентский код не придётся менять.

Шаг 3. Перенесите секреты KV

Выберите одну из стратегий.

Стратегия A. Копирование скриптом

Подходит для базового Stronghold и для разового переноса. Скрипт обходит дерево секретов в Vault и записывает последнюю версию каждого секрета в Stronghold. История версий KV2 не переносится.

#!/usr/bin/env bash
set -euo pipefail

SRC_MOUNT=secret
DST_MOUNT=secret

copy_tree() {
  local path="$1"
  for key in $(vault kv list -mount="$SRC_MOUNT" -format=json "$path" | jq -r '.[]'); do
    if [[ "$key" == */ ]]; then
      copy_tree "${path}${key}"
    else
      vault kv get -mount="$SRC_MOUNT" -format=json "${path}${key}" | jq '.data.data' > /tmp/secret.json
      d8 stronghold kv put -mount="$DST_MOUNT" "${path}${key}" @/tmp/secret.json
      echo "copied ${path}${key}"
    fi
  done
  rm -f /tmp/secret.json
}

copy_tree ""

Для KV версии 1 используйте поле .data вместо .data.data. Перед запуском убедитесь, что временный файл создаётся в директории, доступной только вам, либо замените его на передачу через стандартный ввод.

Стратегия B. KV-репликация из внешнего Vault

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

KV-репликация доступна только в Stronghold EE.

Репликация KV1/KV2 работает по pull-модели: Stronghold периодически забирает секреты из источника через API. Источником может быть Vault CE, Vault Enterprise или другое хранилище с совместимым API KV/KV2. Такой вариант позволяет держать данные синхронизированными на всё время переключения клиентов.

  1. На стороне Vault создайте политику и токен для чтения исходного хранилища:

    vault policy write replicate-secret - <<'POLICY'
    path "secret/*" {
      capabilities = ["read", "list"]
    }
    path "sys/mounts/secret" {
      capabilities = ["read"]
    }
    path "auth/token/lookup-self" {
      capabilities = ["read"]
    }
    path "auth/token/renew-self" {
      capabilities = ["update"]
    }
    POLICY
    
    vault token create -policy=replicate-secret -orphan=true -period=30d -wrap-ttl=5m -field=wrapping_token

    Команда выводит токен обёртывания. Используйте его как <WRAPPING_TOKEN> на следующем шаге.

  2. В Stronghold смонтируйте реплицируемое хранилище:

    d8 stronghold secrets enable -path=secret -src-address="$VAULT_ADDR" -src-wrapping-token=<WRAPPING_TOKEN> -src-mount-path=secret -src-ca-cert=@vault-ca.pem -sync-period-min=5 -version=2 kv

    Параметр -src-address получает адрес HashiCorp Vault из переменной VAULT_ADDR. Версии KV в источнике и в Stronghold должны совпадать.

  3. Проверьте параметры репликации:

    d8 stronghold read sys/mounts/secret/tune

Пока репликация включена, хранилище в Stronghold работает только на чтение. После переключения клиентов отключите репликацию, чтобы разрешить запись:

d8 stronghold secrets tune -sync-enable=false secret

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

Шаг 4. Перенесите политики

Синтаксис ACL-политик совместим, поэтому политики переносятся без изменений:

for p in $(vault policy list | grep -v -E '^(root|default)$'); do
  vault policy read "$p" > "policy-$p.hcl"
  d8 stronghold policy write "$p" "policy-$p.hcl"
done

Проверьте политики, которые ссылаются на accessor методов аутентификации (шаблоны {{identity.entity.aliases.<MOUNT_ACCESSOR>...}}). После воссоздания методов аутентификации accessor изменится: замените его в политиках на новое значение. Подробнее — в разделе «Шаблонные политики».

Шаг 5. Воссоздайте методы аутентификации

Конфигурацию методов аутентификации и их роли перенесите вручную или через IaC-инструмент: секретные параметры (например, client_secret OIDC, bindpass LDAP) Vault не возвращает при чтении.

  1. Включите методы по прежним путям:

    d8 stronghold auth enable -path=oidc oidc
    d8 stronghold auth enable -path=kubernetes kubernetes
    d8 stronghold auth enable -path=approle approle
  2. Прочитайте несекретные параметры ролей в Vault и запишите их в Stronghold:

    vault read -format=json auth/approle/role/my-app | jq '.data' > role-my-app.json
    d8 stronghold write auth/approle/role/my-app @role-my-app.json
  3. Для AppRole выпустите новые secret_id: существующие значения из Vault перенести нельзя. role_id можно задать явно, записав его в auth/approle/role/<ROLE_NAME>/role-id, чтобы не менять конфигурацию клиентов.

  4. Для OIDC добавьте в список разрешённых redirect URI у провайдера идентификации адрес Stronghold.

Настройка методов описана в разделах OIDC, Kubernetes, AppRole, LDAP.

Шаг 6. Перенесите PKI

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

  • Перевыпуск. Создайте в Stronghold новый промежуточный CA, подпишите его существующим корневым CA и выпускайте новые сертификаты из Stronghold. Старые сертификаты продолжают действовать до истечения срока. При этом варианте закрытый ключ CA не покидает Vault. Процедура описана в руководстве «Внутренний PKI».

  • Импорт CA. Если ключ CA экспортируемый (сгенерирован как exported или хранится вне Vault), импортируйте пакет «сертификат + ключ» в Stronghold:

    d8 stronghold secrets enable -path=pki pki
    d8 stronghold write pki/issuers/import/bundle pem_bundle=@ca-bundle.pem

    Затем воссоздайте роли (pki/roles/<ROLE_NAME>) и параметры pki/config/urls. Если адреса CRL и OCSP в выпущенных сертификатах указывают на Vault, сохраните доступность старых адресов до истечения этих сертификатов.

Шаг 7. Перенастройте динамические секреты

Динамические учётные данные (database, ldap, kubernetes) не переносятся: они привязаны к арендам исходного кластера.

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

  2. Выполните rotate-root для новой учётной записи, чтобы её пароль знал только Stronghold:

    d8 stronghold write -force database/rotate-root/my-database
  3. После переключения клиентов отзовите аренды в Vault: vault lease revoke -prefix database/creds/.

Шаг 8. Переключите клиентов

Большинство клиентов Vault (CLI, SDK, Vault Agent, External Secrets Operator, Terraform-провайдер) определяют адрес сервера по переменной VAULT_ADDR или по параметру конфигурации. В окружении клиента укажите адрес Stronghold:

export VAULT_ADDR=https://stronghold.example.com

Не меняйте VAULT_ADDR в оболочке, в которой выполняете команды миграции: команды vault из этого руководства, например отзыв аренд на шаге 7, должны по-прежнему отправляться в HashiCorp Vault. Для утилиты d8 stronghold используйте переменную STRONGHOLD_ADDR.

Переключайте клиентов поэтапно: сначала тестовые окружения, затем некритичные сервисы, затем остальные. Для сервисов с DNS-именем Vault можно переключить CNAME на Stronghold, если сертификат Stronghold содержит это имя в SAN.

Проверка

Проверьте результат:

  1. Сравните количество секретов в Vault и Stronghold для каждого KV-хранилища.

  2. Прочитайте несколько секретов через клиента приложения и через CLI:

    d8 stronghold kv get -mount=secret myapp/config
  3. Выполните вход каждым перенесённым методом аутентификации и проверьте набор политик в выданном токене: d8 stronghold token lookup.

  4. Выпустите тестовый сертификат и динамические учётные данные базы данных.

  5. Убедитесь по журналам Vault, что к нему больше не обращаются рабочие клиенты.

План отката

Держите Vault в рабочем состоянии, пока не завершится период наблюдения (обычно одна–две недели).

  • При стратегии A не удаляйте данные в Vault. Если после переключения секреты менялись в Stronghold, перед откатом перенесите изменения обратно тем же скриптом, поменяв местами источник и приёмник.
  • При стратегии B, пока репликация включена, Vault остаётся источником, а клиенты возвращаются на прежний адрес без переноса данных.
  • Верните VAULT_ADDR клиентов или DNS-запись на Vault.
  • Динамические учётные данные, выданные Stronghold, отзовите: d8 stronghold lease revoke -prefix database/creds/.

Очистка

После завершения периода наблюдения:

  1. Отключите KV-репликацию (-sync-enable=false) и отзовите токен репликации в Vault.
  2. Удалите временные файлы с политиками и ролями (policy-*.hcl, role-*.json).
  3. Выведите Vault из эксплуатации в соответствии с регламентом организации: сделайте финальный снимок, отзовите токены и аренды, остановите серверы.

Дополнительные ресурсы