The drift check now also covers the settings baseline and the Blocked and Allowed lists baseline; the drift dialog offers Open compare, Accept as baseline and Revert to baseline for them. Old drift.json files still load. HUB_WEBHOOK_URL (and an optional HUB_WEBHOOK_SECRET for an HMAC signature) receives drift.found, drift.cleared and write.failed notices, only on change, without values, never blocking a request and never logging the address beyond its host. Admins see the webhook status and can send a test from About and from the drift dialog. Profiles bundle the hub's app, settings and lists baselines. Applying one to a server runs install, app configs, settings and lists in order after a preview; a failed step stops that server's later steps and Retry failed resumes from it. Profiles export and import as a file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
182 lines
17 KiB
Markdown
182 lines
17 KiB
Markdown
# Хаб: централизованное управление серверами Technitium без кластера
|
||
|
||
Общий план. Внутри два пласта:
|
||
- **общее управление**: каждый сервер из одной консоли, действия «на все серверы», сравнение и выравнивание;
|
||
- **упор на Apps**: приложения на всех серверах и синхронизация их конфигов.
|
||
|
||
Статус: этапы 0 и 1 (безопасность, Apps по серверам), 2 (установка и обновление), 3 (сравнение и синхронизация конфигов), 4 (история, откат, расхождения), 5 (управление каждым сервером), 6 (действия на все серверы вне Apps), 7 (зоны, настройки и блокировка: сравнение и выравнивание) и 8 (профили, webhook, расхождения настроек и списков) сделаны. Этапы после 8 пока планы (`docs/hub-deploy.md`).
|
||
|
||
---
|
||
|
||
## 1. Контекст и цель
|
||
|
||
- Есть два независимых сервера Technitium DNS: `mdns.shnt.top` и `ndns.shnt.top`. Кластера нет и не будет: серверы должны оставаться самостоятельными.
|
||
- Хаб на `dnss.shnt.top` уже показывает статистику обоих: Dashboard, Logs, About, переключатель «All servers / mdns / ndns», предупреждение об отказе сервера.
|
||
- **Цель:** управлять обоими серверами из хаба. Прежде всего приложениями (Apps): ставить, обновлять и держать их конфиги одинаковыми там, где нужно, не трогая того, что у каждого сервера своё.
|
||
|
||
---
|
||
|
||
## 2. Принципы
|
||
|
||
1. **Два режима записи.**
|
||
- **На один сервер:** действие пересылается выбранному серверу (`node=mdns` или `node=ndns`). Так уже работает чтение.
|
||
- **На все серверы:** хаб выполняет действие на каждом выбранном сервере и показывает итог по каждому отдельно. Тихого «ок» при частичном успехе не бывает.
|
||
2. **Чтение и запись разделены.**
|
||
- Токены `hub-reader` (только View) остаются для чтения.
|
||
- Для записи на каждом сервере заводится отдельный `hub-admin` только с теми правами, которые нужны этапу.
|
||
3. **Хаб защищается до того, как получает запись.** Роли, ограничение попыток входа, журнал действий (этап 0).
|
||
4. **Конфиги синхронизируются, данные нет.**
|
||
- Синхронизируются тексты конфигов приложений и настройки.
|
||
- Базы логов, кеш и статистика у каждого сервера свои.
|
||
5. **Общая часть плюс локальные поля.**
|
||
- В конфигах приложений есть поля, свои для каждого сервера: сети Split Horizon, путь к базе Query Logs, локальные группы клиентов Advanced Blocking.
|
||
- Синхронизируется только общая часть. Локальные поля задаются по каждому серверу.
|
||
6. **Источник правды для конфигов: эталон в хабе.**
|
||
- Хаб хранит желаемый конфиг в своём томе (`/data`), серверы приводятся к нему.
|
||
- Первый эталон берётся импортом с любого сервера.
|
||
- Альтернатива: ведущий сервер (mdns) как эталон. Она проще, но хуже: зависит от живучести одного сервера и не даёт истории.
|
||
7. **Зоны DNS лучше синхронизировать средствами Technitium.** Primary-зона на одном сервере и secondary с передачей зоны (AXFR) на другом надёжнее рассылки каждой правки из хаба. Хаб помогает это настроить и показывает расхождения, но не дублирует записи сам.
|
||
8. **Перед изменением предпросмотр, после него итог и откат.** Каждое применение конфига сохраняет прежнюю версию.
|
||
|
||
---
|
||
|
||
## 3. Этапы
|
||
|
||
Сначала безопасность, потом Apps (главный упор), потом общее управление.
|
||
|
||
### Этап 0. Безопасность записи (обязателен первым)
|
||
|
||
- **Пользователи хаба с ролями**, в файле `/data/users.json`, пароли в виде хешей:
|
||
- `viewer`: только смотрит;
|
||
- `apps-admin`: управляет приложениями;
|
||
- `operator`: блокировка и кеш;
|
||
- `admin`: всё.
|
||
- **Защита входа:**
|
||
- ограничение попыток входа и временная блокировка после серии неудач;
|
||
- по желанию: вход через ваш auth-портал (Traefik `forwardAuth`).
|
||
- **Журнал действий** (`/data/audit.log`, раздел Audit в консоли хаба): кто, когда, на каком сервере, что сделал, итог.
|
||
- **Токены записи** `MDNS_ADMIN_TOKEN` и `NDNS_ADMIN_TOKEN`. На старте права только Modify и Delete на Apps, другие разделы добавляются по этапам.
|
||
- **Права в хабе:** вместо списка «только чтение»: таблица «эндпоинт → чтение / запись на один сервер / запись на все / нужная роль».
|
||
- **Доступ к хабу по сети:** закрыть по IP (`ipallowlist` в Traefik) или через auth-портал.
|
||
|
||
### Этап 1. Apps: обзор по серверам (только чтение)
|
||
|
||
Матрица «приложение × сервер» в разделе Apps при «All servers»:
|
||
- установлено или нет, версия, есть ли обновление;
|
||
- конфиг: «совпадает», «отличается (N полей)» или «отличается только в локальных полях»;
|
||
- фильтры: «отличаются версии», «отличаются конфиги», «есть обновление».
|
||
|
||
Отвечает на вопрос «что где стоит и где расхождения» без единой записи.
|
||
|
||
### Этап 2. Apps: установка, обновление, удаление на нескольких серверах
|
||
|
||
- Выбор цели у действий: mdns, ndns или все серверы.
|
||
- **Из магазина:** `apps/downloadAndInstall` и `apps/downloadAndUpdate` на каждом выбранном сервере.
|
||
- **Свой zip:** загружается в хаб один раз, хаб отправляет его через `apps/install` или `apps/update` на каждый сервер.
|
||
- Окно итога по серверам и кнопка «Повторить на неудавшихся».
|
||
- **«Выровнять версии»:** обновить приложение везде до самой новой версии.
|
||
- **Удаление на всех:** отдельное подтверждение. Хаб перед удалением сохраняет конфиг в историю.
|
||
|
||
### Этап 3. Apps: сравнение и синхронизация конфигов
|
||
|
||
- **Сравнение рядом:** конфиг на mdns и на ndns. Для JSON: по полям (добавлено, удалено, изменено), для не-JSON: построчно.
|
||
- **Эталон в хабе:**
|
||
- импорт эталона с выбранного сервера;
|
||
- правка эталона в редакторе хаба;
|
||
- хранение в `/data/apps/<приложение>/baseline.json`.
|
||
- **Локальные поля:**
|
||
- список путей полей, которые не синхронизируются (например, `networks`, `dbPath`), плюс их значения по каждому серверу;
|
||
- хранение в `/data/apps/<приложение>/overrides.json`;
|
||
- итоговый конфиг сервера = эталон + его локальные значения.
|
||
- **Предпросмотр:** что изменится на каждом сервере, затем применение через `apps/config/set` на выбранных серверах с итогом по каждому.
|
||
- **Проверка до записи:** JSON разбирается в хабе. Если сервер отверг конфиг, показывается его ошибка, а сервер остаётся со старым конфигом.
|
||
|
||
### Этап 4. Apps: история, откат, контроль расхождений
|
||
|
||
- **История:** каждое применение сохраняет прежний конфиг каждого сервера (`/data/apps/<приложение>/history/`). Можно посмотреть любую версию и откатить один сервер или все.
|
||
- **Проверка расхождений по расписанию** (по умолчанию раз в 15 минут). Если конфиг поменяли прямо в консоли сервера, в шапке появляется предупреждение с действиями «Принять как эталон» и «Вернуть к эталону».
|
||
- **Экспорт и импорт всех эталонов одним файлом:** резервная копия и подготовка нового сервера.
|
||
|
||
### Этап 5. Управление каждым сервером из одной консоли
|
||
|
||
- Запись на выбранный сервер открывается для разделов Zones, Cache, Blocking, DNS Client, Settings, DHCP, Logs (кроме удаления логов). Права токена `hub-admin` расширяются под эти разделы.
|
||
- В «All servers» запись по-прежнему закрыта: кнопки неактивны с подсказкой «выберите сервер».
|
||
- Администрирование пользователей, SSO и LDAP серверов остаётся в их собственных консолях.
|
||
|
||
### Этап 6. Действия «на все серверы» вне Apps
|
||
|
||
- **Блокировка:** добавить или удалить домен в Blocked и Allowed, временно отключить блокировку, обновить списки, изменить URL списков.
|
||
- **Кеш:** сбросить весь кеш или удалить домен.
|
||
- **Итог по каждому серверу** и повтор на неудавшихся.
|
||
|
||
### Этап 7. Сравнение и выравнивание остального
|
||
|
||
- **Zones в «All servers»:**
|
||
- объединённый список с отметкой, где зона есть, и признаком расхождения (разный SOA serial);
|
||
- действия «Скопировать зону на другой сервер» (экспорт и импорт) и «Сделать secondary на другом сервере» (передача зоны).
|
||
- **Blocking и Settings:** отчёт о различиях и «Принять как эталон» с предпросмотром, по той же схеме «общая часть плюс локальные поля», что и для Apps.
|
||
|
||
### Этап 8. Удобство (по желанию)
|
||
|
||
- Профили: набор приложений с конфигами и правил, который применяется к новому серверу.
|
||
- Уведомления о расхождениях наружу (webhook).
|
||
|
||
---
|
||
|
||
## 4. Что меняется в коде
|
||
|
||
**Хаб (`hub/`):**
|
||
- `server.ts`:
|
||
- таблица прав по эндпоинтам;
|
||
- пересылка POST и multipart (zip приложений, импорт зон);
|
||
- рассылка «на все» с итогами по серверам.
|
||
- Новые модули:
|
||
- `users.ts`: роли, хеши паролей, ограничение попыток входа;
|
||
- `audit.ts`: журнал действий;
|
||
- `store.ts`: файлы в `/data` (эталоны, локальные поля, история), запись атомарно через временный файл;
|
||
- `apps-sync.ts`: сравнение конфигов, слияние эталона с локальными полями, предпросмотр, применение;
|
||
- `drift.ts`: проверка расхождений по расписанию.
|
||
- `config.ts`: `<ID>_ADMIN_TOKEN`, `HUB_DATA` (по умолчанию `/data`), `HUB_DRIFT_MINUTES`.
|
||
|
||
**Консоль (`src/`), только в режиме хаба:**
|
||
- права разделов приходят от хаба по роли пользователя;
|
||
- Apps: матрица по серверам, выбор цели у действий, окно итогов;
|
||
- экран «Конфиг приложения»: сравнение рядом, эталон, локальные поля, предпросмотр, история и откат;
|
||
- предупреждение о расхождениях в шапке;
|
||
- раздел Audit.
|
||
|
||
**Развёртывание:**
|
||
- в compose: том `technitium-hub-data:/data` и токены `MDNS_ADMIN_TOKEN`, `NDNS_ADMIN_TOKEN` в `.env`;
|
||
- на каждом сервере пользователь `hub-admin` в отдельной группе с правами записи только на нужные разделы.
|
||
|
||
---
|
||
|
||
## 5. Риски и как их закрыть
|
||
|
||
| Риск | Что делаем |
|
||
|---|---|
|
||
| Хаб получает ключи на запись к обоим серверам | Этап 0 первым; токены только на нужные разделы; хаб закрыт по IP или через auth-портал |
|
||
| Частичный успех при рассылке | Итог по каждому серверу, повтор на неудавшихся, откат из истории |
|
||
| Разные версии приложения ждут разный формат конфига | Хаб показывает версии и предлагает сначала выровнять их |
|
||
| Неверно заданные локальные поля перенесут настройки одного сервера на другой | Предпросмотр перед каждым применением, история и откат |
|
||
| Конфиг поменяли мимо хаба | Проверка расхождений по расписанию и предупреждение |
|
||
| Разные версии Technitium понимают API по-разному | Версия каждого сервера в хабе, предупреждение при расхождении |
|
||
|
||
---
|
||
|
||
## 6. Как проверяется каждый этап
|
||
|
||
- **Модульные тесты хаба** (vitest): права по эндпоинтам, роли и защита входа, слияние эталона с локальными полями, сравнение JSON, история и откат.
|
||
- **Тест с двумя фейковыми серверами:** рассылка, частичный отказ, итоги.
|
||
- **Мок** (`dev/mock-api.mjs`) получает приложения и конфиги, разные на двух портах. В браузере проверяются матрица, сравнение, применение и откат.
|
||
- **Порядок работы:** код по этапам пишет Sonnet, Opus проверяет дифф, тесты и браузер, коммит после каждого принятого этапа.
|
||
- **На серверах:** сначала на одном приложении с безобидным конфигом, потом шире.
|
||
|
||
---
|
||
|
||
## 7. Решения перед стартом
|
||
|
||
1. **Источник правды для конфигов:** эталон в хабе (рекомендация) или ведущий сервер mdns.
|
||
2. **Вход в хаб:** свои пользователи хаба с ролями или ваш auth-портал через Traefik `forwardAuth`.
|
||
3. **Какие приложения стоят на серверах** и какие поля в их конфигах заведомо локальные. Это станет начальными списками локальных полей.
|