Встроенный удостоверяющий центр¶
Поставка для случая, когда удостоверяющего центра (УЦ) у вас ещё нет и заводить его отдельно на время пилота не хочется. Стек поднимает УЦ внутри себя — но настоящий, а не демонстрационный.
Всё нужное лежит в этом же репозитории:
docker-compose.selfhosted.yml
и служебный скрипт
vault-bootstrap.sh.
Какой из файлов брать¶
| Файл | Vault | Когда брать |
|---|---|---|
docker-compose.yml |
внешний (VAULT_ADDR) |
УЦ у вас уже есть |
docker-compose.demo.yml |
внутри, режим -dev |
показать продукт за пять минут |
docker-compose.selfhosted.yml |
внутри, файловое хранилище | пилот со своим УЦ, данные которого должны пережить перезапуск |
Разница между вторым и третьим — не в степени аккуратности. vault server
-dev держит всё в памяти: перезапуск контейнера уничтожает корневой ключ
УЦ, и все выданные сертификаты остаются без издателя, которого можно
проверить. Для демонстрации это приемлемо, для пилота — нет.
Шаг 1. Задайте обязательные значения¶
Все команды на этой странице выполняются в корне клона репозитория —
там, где лежит сам docker-compose.selfhosted.yml. Файл .env должен лежать
рядом с ним.
git clone https://github.com/s3m4rgl/alatyr-docs.git
cd alatyr-docs
cat > .env <<'EOF'
ALATYR_IMAGE_TAG=<версия поставки> # тега latest у нас нет намеренно
DB_PASSWORD=<сильный пароль>
ALATYR_LOCAL_AUTH_ENABLED=true
ALATYR_LOCAL_JWT_SECRET=<openssl rand -hex 32>
ALATYR_LOCAL_ADMIN_EMAIL=admin@corp.example
ALATYR_LOCAL_ADMIN_PASSWORD=<сильный пароль>
ALATYR_PKI_ALLOWED_DOMAINS=corp.example
EOF
Ни у одного из этих значений нет умолчания: без них состав откажется стартовать, а не поднимется с паролем, известным всем.
ALATYR_PKI_ALLOWED_DOMAINS — домены вашей организации через запятую. Отказ
при его отсутствии намеренный: роль Vault служит вторым рубежом проверки
имени после сервера, сузить её больше нечем, а роль «на любое имя» в рабочей
установке недопустима.
Результат. В корне клона лежит .env, и docker compose -f
docker-compose.selfhosted.yml config печатает состав без единой строки
variable is not set.
Шаг 2. Поднимите стек¶
docker compose -f docker-compose.selfhosted.yml up -d
Сервер и админка поднимаются из готовых образов — собирать у себя нечего. Откуда их взять и что ещё входит в поставку — «Установка».
Результат. Все контейнеры в состоянии running или (для служебного) в
exited (0), а сервер отвечает:
docker compose -f docker-compose.selfhosted.yml ps
curl -s http://localhost:8090/api/v1/version
Версия в ответе должна совпадать с ALATYR_IMAGE_TAG из .env. Если нет —
дальше разбираться бессмысленно.
Шаг 3. Заберите ключ распечатывания¶
Служебный контейнер печатает предупреждение один раз и кладёт init.json
(ключ распечатывания и корневой токен) в том alatyr_vault_keys:
docker compose -f docker-compose.selfhosted.yml cp \
vault-bootstrap:/vault/keys/init.json ./init.json
Уберите файл в хранилище секретов организации и удалите локальную копию.
Это осознанный компромисс пилота: стек поднимается без участия человека, но ключ от сейфа лежит рядом с сейфом — кто получил доступ к тому, получил корневой УЦ целиком. Варианты ужесточения, по возрастанию усилий:
- убрать
init.jsonиз тома совсем. Тогда после каждого перезапуска Vault остаётся запечатанным и распечатывается руками:docker compose exec vault vault operator unseal <ключ>; - заменить на автоматическое распечатывание через KMS или HSM (
seal "awskms",seal "transit"вvault-selfhosted.hcl) — тогда файла не будет вовсе.
Результат. init.json у вас на руках, в хранилище секретов, и его нет ни
в рабочем каталоге, ни в истории команд.
Шаг 4. Направьте цели в их издатели¶
Служебный контейнер поднимает точки монтирования и роли, но какая цель
куда ходит — запись в базе, и без неё заявки уедут в состояние
vault_failed.
Откройте админку, Настройки → Удостоверяющие центры, и задайте каждой цели её точку монтирования и роль:
| Цель | Точка монтирования | Роль |
|---|---|---|
wifi |
pki_wifi |
alatyr |
user_mtls |
pki_user_mtls |
user-mtls |
k8s |
pki_k8s |
k8s |
vpn |
pki_vpn |
vpn |
ssh |
движок ssh-client-signer |
ssh-user |
Цепочку из pki_k8s понадобится отдать ещё и самому кластеру Kubernetes —
как именно, описано в разделе «Доступ к
Kubernetes».
Цели ad_logon в этом списке нет и быть не может
Вход в домен по смарт-карте требует расширения SID (KB5014754), которого Vault PKI не выпускает в принципе. Эта цель работает только через SCEP/ADCS. Как направить её в ваш ADCS — Вход в домен по смарт-карте → Настройка.
Результат. В списке профилей у каждой цели указаны своя точка монтирования и своя роль. Кнопка проверки связи на вкладке отвечает успехом.
Шаг 5. Смените пароль первого администратора¶
Сервер напоминает об этом в журнале при первом запуске. После смены уберите
ALATYR_LOCAL_ADMIN_PASSWORD из .env.
Результат. Вход под прежним паролем не проходит, под новым — проходит.
Почему у каждой цели свой промежуточный УЦ¶
Корень ничего не выписывает: под ним четыре промежуточных, по одному на цель, плюс отдельный движок для SSH. Это не стилистика.
Цели wifi и user_mtls несут одинаковое имя в сертификате —
непрерывность личности между целями сделана намеренно, — и приёмник mTLS
различает их только по издателю. Пока обе выписываются одним издателем,
машинный сертификат, который подписывается молча, без PIN и биометрии,
является годной заменой пользовательскому.
Замеры на живых nginx, haproxy и Keycloak, а также рецепты настройки приёмника — «Приёмники».
Что проверено прогоном¶
Снято на живом стеке (Docker 29.6.1, Vault 1.17):
| Проверка | Результат |
|---|---|
Корневой УЦ переживает down и up |
отпечаток совпадает побайтово |
| Vault распечатывается сам после перезапуска | да |
| Повторный запуск служебного контейнера | «уже настроен — ключ не трогаю», ничего не перевыпускает |
| Сервер берёт креды AppRole из файлов | здоровье 200 |
| Подпись имени в домене организации | подписано |
| Подпись имени вне домена организации | отказ 400 — роль действительно сужена |
Тем же токеном pki/root/sign-intermediate |
отказ 403 |
Тем же токеном чтение sys/auth |
отказ 403 |
| Подпись SSH-сертификата тем же токеном | выдан ssh-ed25519-cert-v01 |
Два отказа в середине таблицы — главное, ради чего стоит смотреть на эту поставку: сервер ходит в Vault не корневым токеном, а ролью, которой хватает ровно на подпись и отзыв.
Резервная копия¶
Копировать нужно оба тома с данными — по отдельности они бесполезны:
| Том | Что в нём | Потеря означает |
|---|---|---|
alatyr_vault_data |
корневой ключ УЦ и промежуточные | ни один выданный сертификат больше не проверяется по цепочке; перевыпуск УЦ равен переустановке на всём парке |
alatyr_postgres_data |
устройства, заявки, выданные сертификаты, аудит | УЦ жив, но продукт не знает, кому что выдавал |
alatyr_vault_keys |
ключ распечатывания | без него копия vault_data бесполезна — распечатать нечем |
# Копия, снятая на остановленном стеке, надёжнее.
docker compose -f docker-compose.selfhosted.yml stop
docker run --rm -v alatyr_vault_data:/d -v "$PWD:/b" alpine \
tar czf /b/vault-data.tgz -C /d .
docker run --rm -v alatyr_postgres_data:/d -v "$PWD:/b" alpine \
tar czf /b/postgres-data.tgz -C /d .
docker compose -f docker-compose.selfhosted.yml start
Результат. Оба архива существуют и не пусты (ls -l *.tgz), а стек
снова отвечает на /api/v1/version.
Восстановление — обратной командой в пустые тома и до первого up.
Если стек уже поднялся, служебный контейнер увидит неинициализированный
Vault и выпустит НОВЫЙ корень.
Задание по расписанию для этих команд заводите у себя: сама поставка резервных копий не делает.
Переезд на ваш Vault¶
Правок кода не требует: у каждой цели свой профиль издателя, и в нём задаются адрес Vault, точка монтирования, роль и креды.
- В вашем Vault поднимите ту же структуру: корень, промежуточные и роли.
- В админке перепишите профили издателей на новый адрес.
- Убедитесь, что новые заявки уходят туда: сертификат, выпущенный после переезда, должен строить цепочку к новому корню.
- Старый корень не выключайте, пока живы выданные им сертификаты. Их проверяют по цепочке, а цепочка ведёт к нему; выключенный УЦ ещё и перестаёт публиковать список отзыва, то есть приёмник, настроенный на CRL, начнёт отвергать всех (замеры — «Приёмники»).
Чего эта поставка не делает¶
- Не реплицирует ничего. Одна реплика сервера, один PostgreSQL, один Vault. Что именно небезопасно на нескольких репликах — «Отказоустойчивость (HA)».
- Не делает резервных копий сама. Команды выше — ваши, задание по расписанию заводите у себя.
- Не заменяет автоматическое распечатывание через KMS. По умолчанию ключ распечатывания лежит в томе; см. шаг 3.
- Не публикует Vault наружу. У сервиса
vaultнет опубликованных портов, и слушатель работает без TLS ровно поэтому. Как только Vault выносится на отдельный хост — TLS обязателен, иначе токен AppRole и подписанные сертификаты идут по сети открытым текстом.