Files
technitium-console/docs/hub-deploy.md
T
DenozordecandClaude Opus 5.5 a86d1a088a Hub: step-by-step deployment guide; Dockerfile comment the language check flagged
docs/hub-deploy.md walks through the image, a read-only hub-reader user and token on
each server, the DNS record, .env, the compose service, checks, updates and
troubleshooting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 21:38:56 +07:00

255 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Хаб: запуск по шагам
Хаб показывает статистику двух серверов Technitium DNS (`dns.shnt.top` и `ndns.shnt.top`) в одной консоли. Это один маленький контейнер. Он отдаёт ту же консоль и сам ходит в API обоих серверов по их токенам:
- запрос «All servers» хаб отправляет на оба сервера и складывает ответы;
- запрос к одному серверу хаб пересылает только ему;
- запись хаб не выполняет никогда.
Серверы Technitium не меняются, кластер не нужен. В инструкции хаб ставится на хост, где уже работают Traefik и `technitium-smart` (`docs/compose.shnt.example.yaml`). Адрес хаба: `https://hub.shnt.top`.
```
браузер ─► Traefik (hub.shnt.top, TLS) ─► technitium-hub:8080
├─► https://dns.shnt.top/api/... (DNS_TOKEN)
└─► https://ndns.shnt.top/api/... (NDNS_TOKEN)
```
Что хаб показывает: Dashboard (сумма или один сервер), Logs (View Logs и Query Logs, объединённые с колонкой Server) и About. Остальные разделы скрыты, ими управляют в консоли каждого сервера.
---
## Шаг 0. Образ хаба в реестре
Образ `git.shx.one/denozord/technitium-hub` собирает отдельный workflow `.gitea/workflows/hub.yaml`.
1. Отправьте коммиты хаба на `git.shx.one`:
```bash
git push origin main
```
2. В репозитории на `git.shx.one` откройте **Actions** и дождитесь зелёного прогона **Hub**:
- job `check` проверяет типы и тесты хаба;
- job `image` собирает образ и кладёт его в реестр.
3. Убедитесь, что образ появился: в репозитории или профиле откройте **Packages**, там должен быть `technitium-hub` с тегом `latest`.
Если job `image` упал на входе в реестр, проверьте секрет `ACTION_PAT` (или `ACTIONS_PAT`) в настройках репозитория: это токен Gitea с правом `write:package`. Он тот же, что использует образ консоли.
Выпуск версии с номером (необязательно):
```bash
git tag hub-v1.0.0 && git push origin hub-v1.0.0
```
Тег даёт образы `1.0.0` и `1.0` в дополнение к `latest`.
---
## Шаг 1. Пользователь только для чтения на каждом сервере
Сделайте это **на обоих серверах**: `https://dns.shnt.top` и `https://ndns.shnt.top`. Войдите в консоль сервера как администратор.
1. **Группа.** Administration › Groups › **Add Group**: имя `hub-readers`, описание любое.
2. **Права группы.** Administration › Permissions: у группы `hub-readers` отметьте только **View** в четырёх разделах:
| Раздел | Зачем хабу |
|---|---|
| Dashboard | статистика, топы, график по часам, живые метрики |
| Logs | список файлов логов и Query Logs |
| Apps | список приложений логирования, из которых Query Logs читает записи |
| Settings | состояние блокировки на Dashboard |
Modify и Delete не отмечайте нигде. Остальные разделы оставьте без прав.
3. **Пользователь.** Administration › Users › **Add User**: имя `hub-reader`, длинный случайный пароль. Затем в карточке пользователя добавьте его в группу `hub-readers`. Из группы `Everyone` можно убрать, если у неё есть лишние права.
4. **API-токен.** Нужен токен именно для `hub-reader`, а не для администратора:
- выйдите из консоли и войдите под `hub-reader`;
- в меню пользователя внизу слева выберите **Create API Token**, имя токена `hub`;
- скопируйте токен сразу: его показывают один раз.
Можно и под администратором: Administration › Sessions › **Create Token**, пользователь `hub-reader`.
5. Проверьте токен с любой машины. В ответе должно быть `"status":"ok"`:
```bash
curl -s "https://dns.shnt.top/api/dashboard/stats/get?type=LastHour&token=ТОКЕН" | head -c 200
```
- `"status":"invalid-token"` значит, что скопирован не тот токен;
- `"status":"error"` с сообщением об отказе в доступе значит, что у группы не хватает права View.
Результат шага: два токена, `DNS_TOKEN` (с `dns.shnt.top`) и `NDNS_TOKEN` (с `ndns.shnt.top`).
---
## Шаг 2. DNS-запись для hub.shnt.top
В Cloudflare (зона `shnt.top`) создайте запись `hub` той же формы, что `dns`: `A` на IP хоста или `CNAME` на `dns.shnt.top`.
Сертификат Traefik получит сам. В compose уже настроен `certresolver=myresolver` с DNS-challenge через Cloudflare (`CF_DNS_API_TOKEN`), отдельно открывать порт 80 для проверки не нужно.
---
## Шаг 3. Файл .env
На хосте в каталоге с `docker-compose.yml` откройте `.env` (там уже лежат `CF_DNS_API_TOKEN` и `DNS_SERVER_ADMIN_PASSWORD`) и добавьте:
```dotenv
# Хаб: токены пользователей hub-reader с шага 1
DNS_TOKEN=вставьте_токен_dns
NDNS_TOKEN=вставьте_токен_ndns
# Вход в сам хаб (это НЕ логин Technitium)
HUB_USER=admin
HUB_PASSWORD=длинный_случайный_пароль
```
Пароль хаба нужен длинный: у хаба нет ограничения числа попыток входа. Сгенерировать можно так:
```bash
openssl rand -base64 24
```
Закройте `.env` от посторонних:
```bash
chmod 600 .env
```
---
## Шаг 4. Сервис в compose
Добавьте в `docker-compose.yml` в раздел `services:` блок из `docs/compose.hub.example.yaml`:
```yaml
technitium-hub:
image: git.shx.one/denozord/technitium-hub:latest
container_name: technitium-hub
restart: unless-stopped
networks:
- dns-net
environment:
# id=адрес, по порядку; токен каждого берётся из <ID>_TOKEN
- HUB_SERVERS=dns=https://dns.shnt.top,ndns=https://ndns.shnt.top
- DNS_TOKEN=${DNS_TOKEN}
- NDNS_TOKEN=${NDNS_TOKEN}
- HUB_USER=${HUB_USER}
- HUB_PASSWORD=${HUB_PASSWORD}
- HUB_DOMAIN=hub.shnt.top
- TZ=Asia/Novosibirsk
labels:
- "traefik.enable=true"
- "traefik.docker.network=dns-net"
- "traefik.http.routers.hub.rule=Host(`hub.shnt.top`)"
- "traefik.http.routers.hub.entrypoints=websecure"
- "traefik.http.routers.hub.tls.certresolver=myresolver"
- "traefik.http.routers.hub.service=hub-svc"
- "traefik.http.services.hub-svc.loadbalancer.server.port=8080"
```
Если хаб стоит в том же файле, что Traefik, отдельный блок `networks:` с `external: true` из примера не нужен: сеть `dns-net` в этом файле уже объявлена.
Про имена в `HUB_SERVERS`:
- имя слева от `=` видно в консоли в селекторе «Server»;
- из имени получается переменная токена: `dns` даёт `DNS_TOKEN`, `ndns` даёт `NDNS_TOKEN`, `my-dns` дал бы `MY_DNS_TOKEN`;
- первый в списке сервер считается основным.
Адреса серверов указываются публичные (`https://dns.shnt.top`). API Technitium доступно по тому же адресу, что и консоль (роутер `panel-old` в Traefik), отдельный порт не нужен.
---
## Шаг 5. Запуск
Если реестр закрытый, один раз войдите в него. Нужен токен Gitea с правом `read:package`:
```bash
docker login git.shx.one
```
Скачайте образ и запустите хаб:
```bash
docker compose pull technitium-hub
docker compose up -d technitium-hub
```
Проверьте контейнер:
```bash
docker compose ps technitium-hub # STATUS: Up ... (healthy)
docker compose logs -f technitium-hub
```
В логе при старте не должно быть ошибок конфигурации. Если чего-то не хватает, хаб не запустится и назовёт переменную, например `NDNS_TOKEN is required for the server "ndns".`
---
## Шаг 6. Проверка
1. Откройте `https://hub.shnt.top` и войдите с `HUB_USER` / `HUB_PASSWORD` из `.env`.
2. На Dashboard над заголовком должен быть селектор **Server: All servers**.
3. Сверьте цифры. Total Queries в «All servers» должна равняться сумме значений в консолях `dns.shnt.top` и `ndns.shnt.top` за тот же период. Расхождение в пределах минуты нормально: хаб кеширует ответы на 15 секунд, а Last Hour обновляется на ходу.
4. Выберите в селекторе `dns`, затем `ndns`: цифры каждого сервера должны совпасть с его собственной консолью.
5. Откройте Logs › Query Logs, нажмите **Query**. Записи обоих серверов идут вместе, в колонке **Server** видно, чья запись.
6. Кнопка статуса справа в шапке показывает `hub.shnt.top`. Если какой-то сервер не ответил, там будет «1 server not answering», а по нажатию появится причина.
Проверка без браузера (подставьте пароль хаба):
```bash
T=$(curl -s -X POST https://hub.shnt.top/api/user/login \
--data-urlencode "user=admin" --data-urlencode "pass=ПАРОЛЬ_ХАБА" | sed -E 's/.*"token":"([^"]+)".*/\1/')
curl -s -H "Authorization: Bearer $T" "https://hub.shnt.top/api/dashboard/stats/get?type=LastHour" | head -c 300
curl -s -H "Authorization: Bearer $T" "https://hub.shnt.top/api/dashboard/stats/get?type=LastHour&node=ndns" | head -c 300
```
---
## Обновление
После нового прогона **Hub** в Actions:
```bash
docker compose pull technitium-hub && docker compose up -d technitium-hub
```
Сессии хаба хранятся в памяти: после перезапуска контейнера нужно войти заново.
Чтобы закрепить версию, укажите в compose тег вместо `latest`, например `technitium-hub:1.0.0`.
## Остановка и удаление
```bash
docker compose stop technitium-hub # остановить
docker compose rm -f technitium-hub # удалить контейнер
```
Чтобы отозвать доступ хаба к серверам, на каждом сервере удалите токен в Administration › Sessions или отключите пользователя `hub-reader`.
---
## Настройки хаба
| Переменная | По умолчанию | Что задаёт |
|---|---|---|
| `HUB_SERVERS` | обязательна | Серверы по порядку: `id=https://адрес,...` |
| `<ID>_TOKEN` | обязательна для каждого | API-токен сервера (`DNS_TOKEN`, `NDNS_TOKEN`, ...) |
| `HUB_USER`, `HUB_PASSWORD` | обязательны | Вход в хаб |
| `HUB_DOMAIN` | `hub` | Имя, которое консоль показывает как имя сервера |
| `HUB_CACHE_SECONDS` | `15` | Сколько секунд хранить ответы серверов (0 выключает кеш) |
| `HUB_TIMEOUT_MS` | `8000` | Сколько ждать ответа сервера, мс |
| `HUB_PORT` | `8080` | Порт внутри контейнера |
Третий сервер добавляется строкой в `HUB_SERVERS` и его токеном:
```yaml
- HUB_SERVERS=dns=https://dns.shnt.top,ndns=https://ndns.shnt.top,lab=https://lab.shnt.top
- LAB_TOKEN=${LAB_TOKEN}
```
---
## Если что-то не так
| Признак | Причина и что делать |
|---|---|
| Контейнер сразу завершается | В `docker compose logs technitium-hub` будет названа недостающая переменная. Проверьте `.env` и что compose его видит (`docker compose config \| grep TOKEN`). |
| `https://hub.shnt.top` не открывается, ошибка сертификата | Нет DNS-записи `hub` (шаг 2) или Traefik ещё получает сертификат: смотрите `docker logs traefik`. |
| 404 от Traefik | Контейнер не в сети `dns-net` или опечатка в `Host(\`hub.shnt.top\`)`. |
| Вход в хаб: «Invalid username or password» | Неверные `HUB_USER`/`HUB_PASSWORD` в `.env`. После правки `.env` нужен `docker compose up -d technitium-hub`. |
| В шапке «1 server not answering», в подсказке `ndns: its API token was refused` | Токен `NDNS_TOKEN` отозван или скопирован с ошибкой. Создайте новый (шаг 1.4) и перезапустите хаб. |
| В подсказке `... did not answer: ...` или `ECONNREFUSED` / `timeout` | Хаб не достучался до сервера. Проверьте с хоста хаба: `docker exec technitium-hub node -e "fetch('https://ndns.shnt.top/api/status').then(r=>console.log(r.status))"`. |
| В подсказке `ndns: ...` с сообщением об отказе в доступе | У группы `hub-readers` на этом сервере не хватает View в каком-то разделе (шаг 1.2). |
| Query Logs пустой или «нет приложения» | На сервере не установлено приложение логирования (Query Logs (Sqlite) и подобные) или нет права View на Apps. |
| Цифры «All servers» больше суммы на 1-2 клиента | Clients считается по каждому серверу отдельно: клиент, который ходит на оба сервера, учтён дважды. Это ожидаемо. |
| Нажатие на что-то пишет «The hub is read-only.» | Хаб только читает. Изменения делаются в консоли нужного сервера. |
## Безопасность
- Хаб только читает: любая запись получает отказ ещё в хабе, а токены выданы пользователю с одними правами View.
- Токены лежат только в `.env` на хосте и в памяти контейнера. В браузер они не попадают, в ответы и логи хаба не пишутся.
- У хаба один общий логин и нет ограничения попыток входа. Нужен длинный пароль, а если хаб не должен быть виден из интернета, добавьте к роутеру `hub` в Traefik `ipallowlist` или `basicAuth`.