Секреты — Versola Docs
VersolaVersola/docs
До 1.00.7.1versola.kzGitHub

Секреты

Какие секреты использует Versola, как versola-cli хранит их в OpenBao, что configure и secrets plan решают для каждого из них и какие секреты можно, а какие нельзя ротировать.

Versola нужно около тридцати секретов: ключи шифрования и MAC, пары ключей подписи, пароль Postgres и bootstrap-пароль администратора. Эта страница объясняет, где они лежат, что versola configure делает с каждым из них и какие из них можно безопасно заменить. Прочитайте её, прежде чем восстанавливать сервер, переезжать на новый OpenBao или ротировать значение.

Где лежат секреты

  • Таргеты local и vps. В OpenBao (Vault-совместимое хранилище секретов, KV v2, смонтированное в secret), по пути secret/versola/<target>/<path>, где <path> — одно из auth, central, edge и utils. versola configure запускает OpenBao, читает эти пути, определяет, чего не хватает, записывает новые значения и затем разрешает все плейсхолдеры ${VAR} сгенерированной конфигурации в файлы <service>.secrets.env (права 600) в бандле деплоя. Сервисы во время работы с OpenBao не общаются; он должен быть запущен только на время configure.
  • Kubernetes. В Kubernetes Secret, которые создаёте вы; chart не генерирует секреты и не требует конкретного бэкенда. См. Kubernetes.

Делайте бэкап данных OpenBao, а также root token и ключа распечатки из ~/.versola/openbao/vps-admin.json. Секрет, защищающий хранимые данные, пересоздать нельзя: если он потерян, эти данные нечитаемы.

Путь в OpenBaoЧто хранит
secret/versola/<target>/authСекреты, которые загружаются в контейнер auth.
secret/versola/<target>/centralСекреты, которые загружаются в central, включая публичные половины пар ключей.
secret/versola/<target>/edgeСекреты, которые загружаются в edge.
secret/versola/<target>/utilsПриватный ключ клиента utils; он хранится отдельно от сервисов намеренно: central не должен владеть ключом, которым аутентифицируются как utils.

Секреты

Это секреты из схемы vps. «При отсутствии» — политика, объясняемая ниже.

СекретСервисЧто защищаетПри отсутствии
ACCESS_TOKENS_SECRETauthключ access-токеновgenerate
AUTH_CODES_SECRETauthкоды авторизацииgenerate
SESSIONS_SECRETauthSSO-сессииgenerate
CONVERSATION_COOKIE_SECRET, SESSION_COOKIE_SECRET, USER_AGENT_COOKIE_SECRETauthcookiegenerate
PAR_REQUESTS_SECRETauthсохранённые pushed authorization requestsgenerate
DPOP_NONCES_SECRETauthDPoP-noncegenerate
PASSWORDS_SECRETauthключ, которым вычислены все сохранённые хеши паролейтолько первая установка
REFRESH_TOKENS_SECRETauthMAC-ключ каждого сохранённого refresh-токенатолько первая установка
CLIENT_SECRETS_SECRETauth, centralAES-ключ для секретов клиентов, ключей подписи, ключей edge и секретов ресурсов в centralтолько первая установка
CENTRAL_SECRET_KEYauth, centralобщий ключ этих двух сервисовgenerate
JWT_PRIVATE_KEY и JWKS_JSONauth, centralключ подписи токенов (приватная половина в auth, публичный набор JWK в central)только первая установка, как группа
ACCOUNT_RESOURCE_SECRETcentralучётные данные listener’а Account Settingsgenerate
CENTRAL_RESOURCE_SECRETcentralучётные данные административного API central, хранятся зашифрованными, сохраняются central при первом запускетолько первая установка
EDGE_PRIVATE_KEY, EDGE_KEY_ID и EDGE_PUBLIC_JWKedge, centralпара ключей edge для вызовов central; публичная половина — в centralтолько первая установка, как группа
EDGE_TOKEN_ENC_KEYedgeAES-ключ refresh-токенов в таблице сессий edgeтолько первая установка
EDGE_SESSIONS_SECRET, EDGE_INTERNAL_SECRET, EDGE_DPOP_NONCE_SALT, EDGE_NATIVE_BLOB_KEYedgeзначения для сессий, внутренних вызовов, DPoP-nonce и native-blobgenerate
POSTGRES_PASSWORDauth, central, edgeпароль роли базы данных, на vps одно значение для всех трёхтолько первая установка
ADMIN_BOOTSTRAP_PASSWORDauthвременный пароль первого администратораgenerate
UTILITY_CLIENT_PUBLIC_JWK (central) и UTILS_PRIVATE_KEY_JWK (utils)central, utilsпара ключей клиента utilsтолько первая установка, как группа

В Kubernetes пароль Postgres и пароль администратора задаёт оператор (external), и у каждого сервиса свой POSTGRES_PASSWORD.

Схема

versola-tools пишет secrets.schema.json рядом со сгенерированной конфигурацией. Это единый декларативный список, по которому versola-cli строит план; в нём имена и правила, но не значения.

ПолеЗначение
schemaVersionВерсия формата файла (1).
revisionВерсия содержимого схемы: какие секреты она перечисляет, счёт с 1. Одно число на весь файл.
name, servicesИмя секрета и сервисы, которые его получают. Секрет, перечисленный для нескольких сервисов, — одно значение и должен совпадать везде. Одно и то же имя в отдельных записях — разные значения.
typebase64url (с size в байтах), rsa-private-key, jwk-set, public-jwk, private-jwk, key-id или opaque.
groupСекреты, которые работают только набором, например пара ключей, разнесённая по сервисам. Берутся все члены группы или ни один.
sinceРевизия, в которой секрет впервые появился.
onMissingЧто делать, если в хранилище нет значения: generate, generate-on-first-install-only или external.
fileФайл, в который пишется значение вместо записи в env (приватный ключ utils).

Значения onMissing:

ЗначениеКогда секрета нет в OpenBao
generateГенерируется новое значение. Замена лишь разлогинивает пользователей или обнуляет коды и запросы, находящиеся в обработке.
generate-on-first-install-onlyГенерируется при первой установке. При обновлении это ошибка, потому что от настоящего значения уже что-то зависит: сохранённые данные, зашифрованные им, ключ, которому central уже доверяет, или пароль, который уже есть у роли базы.
externalНикогда не генерируется: сначала задайте его в OpenBao.

По since и записанной ревизии деплоя CLI отличает секрет, новый в релизе (его нет в хранилище, потому что туда ещё нечему было попасть: сгенерировать), от потерянного (остановиться). Ревизию схемы, применённую в последний раз, CLI записывает в state.json; деплой без записи считается ревизией 1.

Что configure делает с каждым секретом

Существующее значение в OpenBao всегда побеждает. Политика решает только то, что делать, когда значения нет. Сначала configure определяет состояние деплоя по записи этой машины (~/.versola/active/state.json) и по тому, что хранится в OpenBao:

Запись на этой машинеOpenBaoСостояниеЭффект для секретов generate-on-first-install-only
нетпустпервая установкагенерируются
естьесть секретыобновлениегенерируется, только если секрет новый в этой ревизии; иначе остановка (потерян)
естьпусттревогаостановка: OpenBao выглядит стёртым или это не тот OpenBao, что использовал деплой
нетесть секретынеизвестноеостановка: новый и потерянный секреты не различить (другая машина, потерян ~/.versola)
нет, но предыдущая первая установка не завершиласьлюбоеустановка идётобрабатывается как первая установка; на этих секретах ещё ничего не запускалось
таргет localлюбоеlocalгенерируются: local — одноразовый стек

Для каждого секрета результат — одно из:

ДействиеЗначение
keepВ OpenBao уже есть значение. Ничего не пишется.
createЗаписывается значение: свежесгенерированное или, для общего секрета, который есть у одного сервиса и нет у другого, — сохранённая копия.
replaceТолько для группы ключей, оставшейся наполовину записанной после прерванной первой установки (или новой в этой ревизии): вся группа генерируется заново.
orphanХранится, но отсутствует в схеме. Сообщается, но не удаляется.
skip-utilsГруппа ключей utils, обрабатывается отдельно (см. ниже).

Проблема останавливает запуск, ничего не записав:

ПроблемаЗначение
lostСекрет существовал в развёрнутой ревизии, OpenBao его не хранит, а сохранённые данные от него зависят. Восстановите из бэкапа.
group-partialСохранены только некоторые члены группы. Ничего не генерируется, потому что члены работают только набором.
shared-conflictСервисы с общим секретом хранят разные значения. Задайте одно и то же верное значение у каждого.
unknown-stateЗаписи на этой машине нет, OpenBao не пуст.
vault-empty-with-stateЗапись есть, а OpenBao ничего не хранит.
external-missingСекрет external не задан.
no-candidate, candidate-conflict, invalid-schemaОбраз tools и его схема расходятся или схема использует политику, неизвестную этому CLI; обновите versola-cli.
unknown-secretИмя, переданное в --generate-missing, отсутствует в схеме — скорее всего, опечатка.

POSTGRES_PASSWORD на vps должен быть одинаковым у auth, central и edge; configure отказывается продолжать, если значения различаются.

Как читать план

versola secrets plan <target> <version> печатает те же решения, ничего не записывая, и завершается с ненулевым кодом, если есть проблемы.

versola secrets plan vps <version>
Secrets plan: vps, Versola <version>
Schema revision 2, deployed revision 1 — upgrade (a deployment record exists, OpenBao holds secrets)

  auth     ACCESS_TOKENS_SECRET   keep
  auth     PASSWORDS_SECRET       keep
  edge     EDGE_NATIVE_BLOB_KEY   create (generated, new in this revision)

Result: OK

(Сокращено: в настоящем выводе по строке на каждый секрет.) С --json тот же отчёт печатается одним объектом. Значения секретов не появляются ни в одном формате.

{
  "version": "<version>",
  "ok": false,
  "target": "vps",
  "state": "upgrade",
  "schemaRevision": 2,
  "deployedRevision": 1,
  "actions": [
    { "service": "auth", "name": "ACCESS_TOKENS_SECRET", "action": "keep" },
    { "service": "edge", "name": "EDGE_NATIVE_BLOB_KEY", "action": "create", "source": "generated", "reason": "new-in-revision" }
  ],
  "problems": [
    { "kind": "lost", "name": "PASSWORDS_SECRET", "services": ["auth"] }
  ]
}

reason — одно из first-install, new-in-revision, policy-generate, forced-by-flag, fill-shared, local-throwaway или restart-install. source — generated или stored.

Генерация того, что план отклоняет

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

versola configure vps <version> --auth-url https://id.example.com --postgres-host 127.0.0.1:5432 --generate-missing EDGE_NATIVE_BLOB_KEY
  • Флаг принимает имена через запятую. Он никогда сам не делает группу ключей полной: группа генерируется, только если названы все её члены.
  • Генерация секрета, к которому привязаны данные (PASSWORDS_SECRET, REFRESH_TOKENS_SECRET, CLIENT_SECRETS_SECRET, EDGE_TOKEN_ENC_KEY), делает нечитаемым всё, что он защищает: перестают работать все входы, либо все refresh-токены, либо все сохранённые секреты клиентов.
  • --allow-legacy-secrets — для релиза, чей образ tools не пишет схему. Тогда CLI не отличает новое от потерянного и на vps отказывается, если только OpenBao не пуст и деплой не записан. С флагом применяется старое правило: всё, чего нет, генерируется, потеряно оно или нет.

Ротация секрета

Существующее значение всегда побеждает, поэтому ротация — это запись нового значения в OpenBao и повторный configure. Используйте bao kv patch, потому что kv put заменяет весь путь:

docker exec -it -e BAO_ADDR=http://127.0.0.1:8200 -e BAO_TOKEN=<root token> versola-openbao-vps \
  bao kv patch -mount=secret versola/vps/auth SESSIONS_SECRET='<new value>'

Затем выполните versola configure vps <current-version> с вашими обычными флагами и versola up. Сначала посмотрите результат через versola secrets plan.

Что можно заменить, зависит от секрета:

ВидПримерыРотация
generateсекреты сессий, cookie, кодов авторизации, DPoP-nonce и PAR, EDGE_SESSIONS_SECRET, EDGE_INTERNAL_SECRETЗаменяйте свободно. Пользователи разлогиниваются, коды и запросы в обработке падают; они всё равно истекают за минуты. Прежде чем заменять ACCESS_TOKENS_SECRET, проверьте, как это влияет на выданные токены.
ADMIN_BOOTSTRAP_PASSWORDВременные учётные данные: первый вход с ним требует задать новый пароль, так что после этого ротировать нечего.
Привязанные к даннымPASSWORDS_SECRET, REFRESH_TOKENS_SECRET, CLIENT_SECRETS_SECRET, EDGE_TOKEN_ENC_KEYНе заменяйте. Новое значение не ротирует старые данные, а уничтожает их (все хеши паролей, refresh-токены, сохранённые секреты клиентов или сессии edge). Процедуры перешифровки сохранённых данных новым ключом в Versola нет.
POSTGRES_PASSWORDСмените пароль роли в Postgres и значение по всем трём путям OpenBao одновременно, затем configure и up.
Пары ключейJWT_PRIVATE_KEY/JWKS_JSON, группа ключей edge, группа utilsЗаменяйте группу целиком, никогда не одну половину. central уже доверяет сохранённому публичному ключу: новая пара edge заставляет каждый вызов edge к central получать 401. Смена ключей подписи делается через JWKS в админ-консоли, а не перезаписью этих значений.
CENTRAL_RESOURCE_SECRETЧерез хранилище не ротируется. central сохраняет свою копию, зашифрованную, при первом запуске и больше в хранилище не заглядывает; ротация через административный API меняет копию central, а не OpenBao, поэтому инструменты, предъявляющие сохранённое значение (cert-sync, скрипты оператора), получают 401 (#380). В Kubernetes задайте bootstrap.resource-secret до первого запуска.

Пара ключей utils

central регистрирует конфиденциального клиента utils для инструментов вроде loadgen provision и versola-cli; он аутентифицируется через private_key_jwt и DPoP. Его пара ключей разделена: публичная половина (UTILITY_CLIENT_PUBLIC_JWK) попадает в конфигурацию central, а приватная хранится в secret/versola/<target>/utils (поле UTILITY_CLIENT_PRIVATE_JWK) и записывается в utils.private-key.jwk в бандле.

central применяет публичную половину при каждом запуске, поэтому пересозданный приватный ключ не совпал бы с ней и заблокировал бы всё, что аутентифицируется как utils. Поэтому CLI обрабатывает эту группу сам (skip-utils в плане): приватный ключ, уже лежащий в OpenBao, побеждает, а публичная половина для central выводится из него. Чтобы использовать существующий ключ, сохраните его по пути utils до первого configure либо, с генератором в Kubernetes, передайте его переменной окружения UTILS_PRIVATE_KEY_JWK (содержимое utils.private-key.jwk). Ключ должен быть приватным JWK типа EC P-256.

Клиент utils, который старый релиз создал с client_secret, нельзя переключить на ключ: метод аутентификации клиента фиксируется при создании. Удалите клиент utils, чтобы следующий запуск зарегистрировал его заново, либо сохраните старый bootstrap.utility-client.

Bootstrap-значения применяются один раз

Значения блока bootstrap у central заполняют базу при первом запуске на пустой базе: клиент и пресет central-admin, edge, набор JWK, документ метаданных, секрет ресурса central и клиент utils. Последующие изменения конфигурации не меняют сохранённое. Меняйте эти объекты в админ-консоли, а для секретов выше следуйте таблице в разделе Ротация секрета.

Первый вход администратора — отдельная история: auth создаёт пользователя admin с временным ADMIN_BOOTSTRAP_PASSWORD, действующим 24 часа. Перезапуск auth после истечения устанавливает новый.

Дальше