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_SECRET | auth | ключ access-токенов | generate |
AUTH_CODES_SECRET | auth | коды авторизации | generate |
SESSIONS_SECRET | auth | SSO-сессии | generate |
CONVERSATION_COOKIE_SECRET, SESSION_COOKIE_SECRET, USER_AGENT_COOKIE_SECRET | auth | cookie | generate |
PAR_REQUESTS_SECRET | auth | сохранённые pushed authorization requests | generate |
DPOP_NONCES_SECRET | auth | DPoP-nonce | generate |
PASSWORDS_SECRET | auth | ключ, которым вычислены все сохранённые хеши паролей | только первая установка |
REFRESH_TOKENS_SECRET | auth | MAC-ключ каждого сохранённого refresh-токена | только первая установка |
CLIENT_SECRETS_SECRET | auth, central | AES-ключ для секретов клиентов, ключей подписи, ключей edge и секретов ресурсов в central | только первая установка |
CENTRAL_SECRET_KEY | auth, central | общий ключ этих двух сервисов | generate |
JWT_PRIVATE_KEY и JWKS_JSON | auth, central | ключ подписи токенов (приватная половина в auth, публичный набор JWK в central) | только первая установка, как группа |
ACCOUNT_RESOURCE_SECRET | central | учётные данные listener’а Account Settings | generate |
CENTRAL_RESOURCE_SECRET | central | учётные данные административного API central, хранятся зашифрованными, сохраняются central при первом запуске | только первая установка |
EDGE_PRIVATE_KEY, EDGE_KEY_ID и EDGE_PUBLIC_JWK | edge, central | пара ключей edge для вызовов central; публичная половина — в central | только первая установка, как группа |
EDGE_TOKEN_ENC_KEY | edge | AES-ключ refresh-токенов в таблице сессий edge | только первая установка |
EDGE_SESSIONS_SECRET, EDGE_INTERNAL_SECRET, EDGE_DPOP_NONCE_SALT, EDGE_NATIVE_BLOB_KEY | edge | значения для сессий, внутренних вызовов, DPoP-nonce и native-blob | generate |
POSTGRES_PASSWORD | auth, central, edge | пароль роли базы данных, на vps одно значение для всех трёх | только первая установка |
ADMIN_BOOTSTRAP_PASSWORD | auth | временный пароль первого администратора | 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 | Имя секрета и сервисы, которые его получают. Секрет, перечисленный для нескольких сервисов, — одно значение и должен совпадать везде. Одно и то же имя в отдельных записях — разные значения. |
type | base64url (с 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 после истечения устанавливает новый.
Дальше
- Деплой на VPS: настройте OpenBao сами и сохраните учётные данные AppRole.
- Справочник CLI: флаги
secrets login,testиplan. - Конфигурация: где каждый секрет попадает в файлы конфигурации.