Skip to content

Шифрование

Чувствительные значения в БД шифруются envelope-паттерном: данные шифрует DEK (AES-256-GCM), сам DEK хранится в БД обёрнутым (wrapped) корневым ключом KMS-провайдера.

flowchart TB
    KMS["KMS-провайдер<br/>(локальный ключ или Vault Transit)"] -->|wrap / unwrap| DEK["data_keys.wrapped_dek"]
    DEK -->|AES-256-GCM| Data["🔒-поля таблиц:<br/>ciphertext + nonce + data_key_id"]

Зашифрованное поле — тройка колонок *_ciphertext, *_nonce, *_data_key_id (Схема БД). DEK-и разделены по scope (credentials, certificates, node_secrets, billing) — компрометация одного ключа не раскрывает все категории данных.

Корень доверия — интерфейс с двумя операциями:

type KMSProvider interface {
Wrap(ctx, dek []byte) (wrapped []byte, keyVersion string, err error)
Unwrap(ctx, wrapped []byte) (dek []byte, err error)
}
Провайдер Корневой ключ Когда выбирать
local (default) 32 байта в файле (ASTRAL_MASTER_KEY_FILE); генерируется при первом старте, если файла нет Типовая self-hosted-инсталляция
vault Vault Transit: ключ не покидает Vault, автотротация версий, аудит обращений Инсталляции, где Vault уже есть

Обязательный Vault был бы правильнее криптографически, но нереалистичен для целевого пользователя self-hosted-панели: это второй stateful-сервис с собственной процедурой unseal ради одного ключа. Поэтому корень — плагин: по умолчанию локальный мастер-ключ (с явным предупреждением в документации о бэкапе), Vault — опция без изменения схемы данных (data_keys.kms_key_version абстрагирует версию корня у обоих).

Данные Где
Секреты протоколов подписок protocol_credentials.secret
Токены ссылок подписок subscriptions.token
Приватные ключи сертификатов certificates.key_pem, internal_ca.key_pem
Reality private keys inbound_profiles, chain_hops
Секреты хопов цепочек chain_hops.secret
Креденшелы DNS-провайдеров, ACME-аккаунтов, EAB dns_providers, acme_accounts
API-ключи платёжных агрегаторов payment_providers.credentials

Не шифруются, а хэшируются (одностороннее): пароли админов (Argon2id), refresh-токены, magic-link-токены, enrollment-токены — их не нужно читать обратно.

Запись: активный DEK своего scope (кэшируется в памяти процесса после unwrap) → AES-256-GCM со случайным nonce → тройка колонок. Чтение: по data_key_id → unwrap (или кэш) → расшифровка. DEK в открытом виде существует только в памяти процесса.

  • DEK — фоновая задача раз в 30 дней на scope: новый DEK, старый помечается retired_at (новые записи — новым ключом), ленивая перешифровка существующих строк батчами, удаление старого DEK, когда ссылок не осталось.
  • Корневой ключ — зависит от провайдера. vault: Transit ротирует версии сам, задача rewrap периодически переобёртывает wrapped_dek свежей версией (DEK-ов мало, операция дешёвая). local: смена файла ключа + команда astral rewrap-keys (переобёртка всех DEK старым→новым ключом; сами данные не перешифровываются).

Восстановление корневого ключа

Section titled “Восстановление корневого ключа”

Провайдер local использует модель слотов (как LUKS): один мастер-ключ, несколько независимых способов его получить (ADR-011).

Слот Носитель Секрет
Файл Сервер панели (ASTRAL_MASTER_KEY_FILE) Сам ключ
Recovery БД: мастер-ключ, обёрнутый ключом из recovery-кода (Argon2id) Recovery-код
  • Мастер-ключ и recovery-код генерируются панелью при первом старте; код (~130 бит энтропии, формат XXXX-XXXX-…) показывается один раз на экране установки. Код именно генерируется, а не выбирается: пользовательская парольная фраза свела бы свойство «дамп БД сам по себе бесполезен» к стойкости пароля.
  • Потерян файл ключаastral recover --recovery-code …: слот из БД восстанавливает файл. Бэкап БД + recovery-код = полное восстановление инсталляции.
  • Потерян recovery-код (файл жив) → astral recovery-code new: старый слот заменяется новым кодом.
  • Ротация мастер-ключа (astral rewrap-keys) обновляет оба слота атомарно.

Потеряно всё (файл + код) — 🔒-поля невосстановимы, но худший случай автоматизирован: astral crypto reset генерирует новый мастер-ключ и запускает перевыпуск — новые секреты всех подписок (bump ревизий), ACME-перевыпуск сертификатов, новый внутренний CA с re-enrollment нод. Существующие ссылки подписок продолжают работать: поиск идёт по незашифрованному token_hash, клиенты получают новые секреты по старой ссылке. Руками восстанавливаются только внешние креденшелы (DNS-провайдеры, платёжные агрегаторы) и MANUAL-сертификаты.

Недоступность KMS-провайдера (для vault) блокирует операции с 🔒-полями, но не работу нод: desired state уже применён, VPN-трафик не зависит от backend. Кэш unwrapped DEK смягчает короткие недоступности для чтения.