diff --git a/docs/compose.hub.example.yaml b/docs/compose.hub.example.yaml index 51fa2d1..0d9663c 100644 --- a/docs/compose.hub.example.yaml +++ b/docs/compose.hub.example.yaml @@ -40,6 +40,8 @@ services: - HUB_DATA=/data # За Traefik: брать адрес клиента из X-Forwarded-For (для ограничения попыток входа и журнала) - HUB_TRUST_PROXY=1 + # Как часто проверять, что конфиги серверов не разошлись с эталоном (минуты, 0 выключает) + # - HUB_DRIFT_MINUTES=15 - HUB_DOMAIN=dnss.shnt.top - TZ=Asia/Novosibirsk labels: diff --git a/docs/compose.shnt.example.yaml b/docs/compose.shnt.example.yaml index 12fae75..ac078e1 100644 --- a/docs/compose.shnt.example.yaml +++ b/docs/compose.shnt.example.yaml @@ -198,6 +198,8 @@ services: - HUB_DATA=/data # За Traefik: брать адрес клиента из X-Forwarded-For (для ограничения попыток входа и журнала) - HUB_TRUST_PROXY=1 + # Как часто проверять, что конфиги серверов не разошлись с эталоном (минуты, 0 выключает) + # - HUB_DRIFT_MINUTES=15 - HUB_DOMAIN=dnss.shnt.top - TZ=Asia/Novosibirsk labels: diff --git a/docs/hub-deploy.md b/docs/hub-deploy.md index c0cce62..b35e48f 100644 --- a/docs/hub-deploy.md +++ b/docs/hub-deploy.md @@ -327,6 +327,12 @@ docker compose rm -f technitium-hub # удалить контейнер Файлы эталона и локальных полей лежат в `/data/apps/<имя>/` (`baseline.json`, `overrides.json`). Они содержат конфиги, поэтому том `/data` нужно беречь так же, как `users.json`. +## История, откат, контроль расхождений, перенос эталонов + +- **История.** Вкладка 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`. +- **Перенос эталонов.** Apps › All servers: **Export baselines** скачивает один файл со всеми эталонами и локальными полями, **Import baselines** загружает его в другой хаб: сначала проверка и сводка (New, Changed, Same), запись только после **Import**. На серверы импорт ничего не отправляет. Файл содержит конфиги, храните его как секрет. + ## Приложения: установка, обновление, удаление Роль `apps-admin` (или `admin`) видит в разделе Apps при «All servers» меню действий в каждой строке и кнопки **App Store** и **Install from file**: diff --git a/hub/README.md b/hub/README.md index db4797a..48541a0 100644 --- a/hub/README.md +++ b/hub/README.md @@ -22,6 +22,7 @@ apps Query Logs reads from) and Settings (the blocking state). | `_TOKEN` | required | API token of each server; the id is uppercased with non-alphanumerics as `_` (`dns` gives `DNS_TOKEN`, `my-dns` gives `MY_DNS_TOKEN`). | | `HUB_USER`, `HUB_PASSWORD` | see Users | Bootstrap: one `admin`, used only while `${HUB_DATA}/users.json` does not exist. Set both or neither. | | `HUB_DATA` | `/data` | Folder of `users.json` and `audit.log`. Mount a volume on it. | +| `HUB_DRIFT_MINUTES` | `15` | How often the hub checks that every server still has the config it should (see Drift). `0` turns the check off. | | `HUB_TRUST_PROXY` | off | `1` believes the first `X-Forwarded-For` entry as the client address (set it behind Traefik). Leave it off when clients reach the hub directly, or anyone can fake their address. | | `_ADMIN_TOKEN` | optional | A write-capable token per server (`MDNS_ADMIN_TOKEN`). Only app writes use it, and only it: a server without one is read-only for the hub (the matrix and the session list the writable servers). Never logged or returned. | | `HUB_PORT` | `8080` | Listening port. | @@ -128,6 +129,38 @@ matrix hash ignores them too. | `hub/apps/config/preview` | POST `{name, targets}` | `apps.write` | Per target: current and effective text, the diff, warnings (a missing local value, a version older than the newest installed, no admin token) and `currentHash`. | | `hub/apps/config/apply` | POST `{name, targets, expect?}` | `apps.write` | Per target: re-reads the config; refuses if its hash differs from `expect[server]`; saves the old config to the history; `apps/config/set` with the admin token; reads it back and checks it. A server that rejects the config keeps its old one. | +## History, rollback, drift, export + +**History.** Everything the hub replaces is kept in `${HUB_DATA}/apps//history/` (50 files per app): +`