Перейти к содержимому

Онбординг душ

Это руководство проводит путь от пустого реестра до нескольких хостов в статусе connected — готовых получать Destiny. Quick Start подключал один хост; здесь та же механика масштабируется на множество душ, плюс разложены по полочкам covens, которыми вы потом таргетируете сценарии.

Предполагается, что Keeper уже поднят, первый Архонт создан и его JWT лежит в переменной TOKEN. Если нет — пройдите Quick Start (шаги 1–3) или Установку из пакетов.

Онбординг каждого хоста — двусторонний обмен:

  1. Оператор регистрирует хост в Keeper-е (POST /v1/souls) и получает одноразовый bootstrap-токен.
  2. На хосте soul init обменивает токен + CSR на постоянную mTLS-идентичность (SoulSeed). Приватный ключ генерируется на хосте и никогда его не покидает.
  3. На хосте soul run поднимает демон, который держит долгоживущий стрим к Keeper-у — хост переходит в connected.
sequenceDiagram
    participant Op as Оператор
    participant K as Keeper
    participant H as Хост (Soul)
    Op->>K: POST /v1/souls
    K-->>Op: bootstrap_token
    Note over Op,H: доставка токена на хост
    H->>K: soul init (CSR + токен)
    K-->>H: SoulSeed
    H->>K: soul run (mTLS-стрим)
    K-->>H: status: connected

Идентификатор хоста (SID) равен его FQDN. Дальше по тексту примеры используют host-01.example.comhost-NN.example.com.

Coven — стабильная логическая метка хоста: кластер, проект, окружение, ЦОД, тип железа. По covens вы позже таргетируете сценарии (on: [web, prod]). Метку назначают при регистрации, и она остаётся стабильной — поэтому продумать схему covens стоит до онбординга.

Ключевое правило: Coven — это только стабильные теги. Волатильная роль хоста (кто сейчас master, кто replica) — не Coven; она определяется живой проверкой во время прогона (Оркестрация → probe-роль). В Coven кладут то, что не меняется от прогона к прогону.

Пример схемы для нескольких душ:

ХостCovensСмысл
host-01.example.com[web, prod, eu]веб-узел, прод, регион EU
host-02.example.com[web, prod, eu]веб-узел, прод, регион EU
host-03.example.com[db, prod, eu]узел БД, прод, регион EU

Один хост может нести несколько covens. Таргетинг по нескольким меткам берёт их пересечение (on: [web, prod] → только хосты, у которых есть обе метки).

Шаг 2. Зарегистрировать хосты и выпустить токены

Заголовок раздела «Шаг 2. Зарегистрировать хосты и выпустить токены»

Регистрация — POST /v1/souls со стороны Keeper-а. На каждый хост:

Окно терминала
curl -s -X POST http://keeper.example.com:8080/v1/souls \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"sid": "host-01.example.com", "transport": "agent", "covens": ["web", "prod", "eu"]}'

В ответе — bootstrap_token (возвращается ровно один раз, TTL по умолчанию 24 часа) и expires_at. Запись хоста появляется в статусе pending.

Регистрировать десятки хостов вручную неудобно. Типовой паттерн — цикл по списку FQDN, складывающий выпущенные токены в защищённый файл (mode 0600):

Окно терминала
# hosts.txt: по одному FQDN на строку
while read -r sid; do
token=$(curl -s -X POST http://keeper.example.com:8080/v1/souls \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"sid\": \"$sid\", \"transport\": \"agent\", \"covens\": [\"web\", \"prod\"]}" \
| jq -r '.bootstrap_token')
printf '%s\t%s\n' "$sid" "$token" >> souls-tokens.tsv
done < hosts.txt
chmod 0600 souls-tokens.tsv

Дальше каждый токен доставляется на свой хост (см. шаг 4). Способ доставки — выбор оператора: SSH/SCP, cloud-init, CI/CD-пайплайн, конфиг-менеджмент.

Bootstrap-фаза (soul init) идёт по server-only TLS: хост обязан проверить серверный сертификат Keeper-а до того, как получит собственную идентичность. Доверие устанавливается не «как-нибудь», а явной предзагрузкой PKI-корня — того же CA, которым подписан серверный сертификат Keeper-а.

На каждый хост заранее положите этот CA-файл по пути, который укажете в soul.yml (keeper.tls.ca):

Окно терминала
# ca.crt — корневой/issuing CA вашего PKI (тот же, что серверный cert Keeper-а)
sudo install -d -m 0750 /var/lib/soul-stack/seed
sudo install -m 0644 ca.crt /var/lib/soul-stack/seed/ca.crt

Откуда взять ca.crt? Это issuing CA вашего PKI, полученный при выпуске серверного сертификата Keeper-а — для локального знакомства возьмите его быстрыми vault-командами из Quick Start → локальное знакомство; для прода — Установка из пакетов.

Если файл не положить — soul init остановится с keeper.tls.ca is empty. Если положить CA от другого корня — certificate validation failed.

Минимальный конфиг агента — адрес Keeper-а, два порта (bootstrap и event-stream) и путь к доверенному CA:

# /etc/soul/soul.yml на host-01.example.com
sid: host-01.example.com # = FQDN; по умолчанию берётся из hostname
paths:
modules: /var/lib/soul-stack/modules
seed: /var/lib/soul-stack/seed # сюда soul init положит SoulSeed
keeper:
endpoints:
- host: keeper.example.com
bootstrap_port: 9442 # server-only TLS, фаза soul init
event_stream_port: 9443 # mTLS, фаза soul run
priority: 1
tls:
ca: /var/lib/soul-stack/seed/ca.crt # предзагружен на шаге 3

Оба порта обязательны и явны — молчаливого ухода bootstrap на event-stream-порт нет. Несколько Keeper-ов перечисляются как несколько записей endpoints[] с разными priority (агент использует их как fallback-list).

Шаг 5. soul init — обменять токен на идентичность

Заголовок раздела «Шаг 5. soul init — обменять токен на идентичность»

soul init определяет SID, генерирует приватный ключ + CSR, подключается к bootstrap-listener-у Keeper-а, предъявляет токен + CSR и атомарно раскладывает полученный SoulSeed в paths.seed.

Bootstrap-токен из шага 2 передаётся одним из двух способов:

Окно терминала
SOUL_BOOTSTRAP_TOKEN='<bootstrap_token хоста>' soul init --config /etc/soul/soul.yml

Безопаснее: токен не виден в выводе ps и не попадает в историю shell.

При успехе SoulSeed (cert/key/ca) лежит в paths.seed, запись хоста переходит из pending. Если SoulSeed на хосте уже есть, init остановится — это защита от случайного перевыпуска идентичности.

Окно терминала
soul run --config /etc/soul/soul.yml

Демон поднимает долгоживущий EventStream к Keeper-у по mTLS (порт 9443) — после этого хост становится connected. В проде soul работает как systemd-сервис (systemctl enable --now soul); ручной запуск выше удобен для первой проверки.

Опросить конкретный хост:

Окно терминала
curl -s http://keeper.example.com:8080/v1/souls/host-01.example.com \
-H "Authorization: Bearer $TOKEN"
# в ответе status: connected

Опросить весь реестр и отфильтровать по covens (например, увидеть все prod-хосты):

Окно терминала
curl -s 'http://keeper.example.com:8080/v1/souls?coven=prod' \
-H "Authorization: Bearer $TOKEN"

То же самое доступно в web-UI на /ui (вкладка с реестром хостов) и через CLI soulctl — см. soulctl.

СимптомВероятная причинаЧто проверить
soul init: connection refusedKeeper не слушает bootstrap-порт / firewall режет 9442Keeper запущен; открыт входящий bootstrap-порт с хоста; host/bootstrap_port в soul.yml верны
soul init: certificate validation failedпредзагруженный CA не от того PKI-корня; или FQDN Keeper-а не в SAN серверного cert-аkeeper.tls.ca = тот же CA, что серверный cert; FQDN из endpoints[].host в SAN
soul init: keeper.tls.ca is emptyCA-файл не указан/не положензаполнить keeper.tls.ca и положить файл (шаг 3)
soul init: bootstrap token invalid / expired / usedтокен уже использован, истёк (TTL 24h) или SID не совпалперевыпустить токен (POST /v1/souls/{sid}/issue-token, force при активном); сверить SID = FQDN
soul init: invalid sidFQDN не матчит ^[a-z0-9][a-z0-9.-]{0,253}$привести hostname к валидному lower-case FQDN либо задать sid: явно в soul.yml
хост остаётся pending, не connectedEventStream-фаза (mTLS на event-stream-порту) не проходитоткрыт входящий event-stream-порт; SoulSeed разложен в paths.seed; серверный cert и SoulSeed от одного PKI-корня