Hub stage 8: settings drift, webhook notices, profiles
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
392b644faa
commit
9742f0fe69
32 files changed
+3123
-106
No files matched your search
@@ -312,6 +312,8 @@ docker compose rm -f technitium-hub # удалить контейнер
|
||||
| `HUB_CACHE_SECONDS` | `15` | Сколько секунд хранить ответы серверов (0 выключает кеш) |
|
||||
| `HUB_TIMEOUT_MS` | `8000` | Сколько ждать ответа сервера, мс |
|
||||
| `HUB_PORT` | `8080` | Порт внутри контейнера |
|
||||
| `HUB_WEBHOOK_URL` | нет | Адрес http или https, куда хаб шлёт уведомления (см. ниже). Адрес может содержать токен, храните его как секрет |
|
||||
| `HUB_WEBHOOK_SECRET` | нет | Подписывает уведомления (`X-Hub-Signature`). В логи не пишется |
|
||||
|
||||
Третий сервер добавляется строкой в `HUB_SERVERS` и его токеном:
|
||||
```yaml
|
||||
@@ -361,8 +363,41 @@ docker compose rm -f technitium-hub # удалить контейнер
|
||||
|
||||
- **История.** Вкладка History в окне Config: все прежние версии, которые хаб сохранил (конфиг сервера перед применением или сохранением, прежний эталон). Выберите версию, чтобы прочитать её и увидеть отличие от текущего конфига сервера. Роли `apps-admin` и `admin`: **Roll back…** (выбор серверов, предпросмотр, итог по каждому) и **Restore as baseline** для прежнего эталона. Хранится по 50 версий на приложение.
|
||||
- **Контроль расхождений.** Раз в 15 минут хаб читает конфиги и сравнивает их с тем, что должно быть (эталон плюс локальные поля сервера). Если конфиг поменяли прямо в консоли сервера, в шапке появляется «N config drift». В списке у каждого расхождения есть **Open config**, а для `apps-admin` и `admin` ещё **Accept as baseline** (конфиг этого сервера становится эталоном; локальные поля остаются локальными) и **Revert to baseline** (через предпросмотр). Период меняется переменной `HUB_DRIFT_MINUTES`, `0` отключает проверку. Сервер, который не ответил, показывается как «Cannot read» и в счётчик не входит. Последний результат лежит в `/data/drift.json`.
|
||||
- **Что проверяется.** Конфиги приложений, общие поля настроек (против эталона настроек) и списки Blocked/Allowed (против эталона списков). Проверка настроек и списков идёт только если соответствующий эталон принят. Расхождение настроек или списков показывает только названия полей, без значений. В списке у него **Open compare** (окно сравнения Settings или Blocking), **Accept as baseline** и **Revert to baseline** (через тот же предпросмотр); эти действия только для роли `admin`.
|
||||
- **Перенос эталонов.** Apps › All servers: **Export baselines** скачивает один файл со всеми эталонами и локальными полями, **Import baselines** загружает его в другой хаб: сначала проверка и сводка (New, Changed, Same), запись только после **Import**. На серверы импорт ничего не отправляет. Файл содержит конфиги, храните его как секрет.
|
||||
|
||||
## Уведомления (webhook)
|
||||
|
||||
Если задан `HUB_WEBHOOK_URL`, хаб отправляет POST с JSON `{event, at, hub, items}`:
|
||||
|
||||
- `drift.found`: что-то только что разошлось с эталоном (сервер, вид, имя). Шлётся один раз при изменении, а не каждые 15 минут;
|
||||
- `drift.cleared`: расхождение исчезло (сервер, который перестал отвечать, не считается исчезнувшим);
|
||||
- `write.failed`: изменение, записанное в журнал с исходом `failed` (без значений). Сбои одного момента идут одним уведомлением.
|
||||
|
||||
Пример:
|
||||
```json
|
||||
{"event":"drift.found","at":"2026-11-01T09:15:00.000Z","hub":"dnss.shnt.top",
|
||||
"items":[{"kind":"settings","server":"ndns","since":"2026-11-01T09:15:00.000Z","fields":["blockingType"]}]}
|
||||
```
|
||||
|
||||
Если задан `HUB_WEBHOOK_SECRET`, заголовок `X-Hub-Signature: sha256=<hex>` содержит HMAC-SHA256 точных байт тела. Проверка на стороне получателя (Node):
|
||||
```js
|
||||
const expected = 'sha256=' + createHmac('sha256', SECRET).update(rawBody).digest('hex')
|
||||
// сравните с заголовком через timingSafeEqual
|
||||
```
|
||||
Отправка не блокирует работу хаба: таймаут 5 секунд, не больше одного повтора. Ошибка пишется в лог хаба с именем хоста (без параметров адреса). Администратор видит состояние (настроен ли, хост, результат последней отправки) и кнопку **Send test** в окне «Config drift» (оно открывается из шапки, когда есть расхождения).
|
||||
|
||||
## Профили
|
||||
|
||||
Профиль нужен, чтобы быстро привести новый или сброшенный сервер к общему виду. Это именованный набор в `/data/profiles/`: приложения магазина (имя и версия) с эталонным конфигом каждого, общие поля настроек (поля, относящиеся к самому хосту, не входят) и эталон списков Blocked/Allowed. Файл содержит секреты настроек (ключи TSIG), берегите том `/data`.
|
||||
|
||||
Все действия только для роли `admin` (`apps-admin`, `operator` и `viewer` получают отказ). Кнопка **Profiles** на экране Apps › All servers:
|
||||
|
||||
- **Create from current baselines**: профиль собирается из эталонов, которые хаб держит сейчас; можно не брать приложения, настройки или списки. Приложение не из магазина (поставленное из zip) в профиль не входит;
|
||||
- **View**: состав профиля; секреты показаны как `<secret>`, тексты конфигов не показываются;
|
||||
- **Apply to servers…**: выбор серверов, предпросмотр по шагам для каждого сервера, затем итог по каждому шагу. Шаги идут по порядку, ошибка шага останавливает следующие шаги этого сервера: 1) установка недостающих приложений из магазина самого сервера, 2) конфиги приложений (эталон плюс локальные поля сервера, прежний конфиг сохраняется в историю), 3) общие поля настроек (через те же проверки, что и обычное сохранение), 4) списки: недостающие домены добавляются, лишние удаляются только по галочке **Also remove extras**. **Retry failed** начинает заново с шага, который не удался;
|
||||
- **Export** и **Import profiles**: перенос между хабами (файл содержит секреты, храните его как секрет; сначала проверка, запись только после **Import**).
|
||||
|
||||
## Приложения: установка, обновление, удаление
|
||||
|
||||
Роль `apps-admin` (или `admin`) видит в разделе Apps при «All servers» меню действий в каждой строке и кнопки **App Store** и **Install from file**:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
- **общее управление**: каждый сервер из одной консоли, действия «на все серверы», сравнение и выравнивание;
|
||||
- **упор на Apps**: приложения на всех серверах и синхронизация их конфигов.
|
||||
|
||||
Статус: этапы 0 и 1 (безопасность, Apps по серверам), 2 (установка и обновление), 3 (сравнение и синхронизация конфигов), 4 (история, откат, расхождения), 5 (управление каждым сервером), 6 (действия на все серверы вне Apps) и 7 (зоны, настройки и блокировка: сравнение и выравнивание) сделаны. Этапы 8 и далее пока планы (`docs/hub-deploy.md`).
|
||||
Статус: этапы 0 и 1 (безопасность, Apps по серверам), 2 (установка и обновление), 3 (сравнение и синхронизация конфигов), 4 (история, откат, расхождения), 5 (управление каждым сервером), 6 (действия на все серверы вне Apps), 7 (зоны, настройки и блокировка: сравнение и выравнивание) и 8 (профили, webhook, расхождения настроек и списков) сделаны. Этапы после 8 пока планы (`docs/hub-deploy.md`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user