Files
HY2XS_flamy/docs/12-operations-and-troubleshooting.md
T
founder b22b4b0d99 fix(v1): сделать read-only свойством doctor, а sentinel-ошибки — решением
Два свойства были описаны в документации, но не обеспечены кодом.

1. doctor «не изменяет диагностируемую систему».

   Принудительный skipServiceStart закрывал ровно одну ИЗВЕСТНУЮ мутацию —
   рестарт сервисов. Всё остальное в smoke держалось на том, что автор правки
   выбрал правильный раннер: `test -s`, `grep -q`, `stat`, `sudo -u ... test`
   и `nft -c` шли через мутирующий namespace, хотя ничего не меняют. Ожидание
   между попытками выполнялось подпроцессом `sleep` через runMutatingHidden,
   то есть пауза между двумя чтениями объявлялась изменением системы.

   Следствие: настоящая мутация, случайно добавленная в smoke, ничем бы от них
   не отличалась и была бы разрешена в doctor молча — а включить guard было
   нельзя, он отказал бы на первой же читающей команде.

   Команды классифицированы честно, `sleep` заменён таймером, и doctor целиком
   выполняется под тем же read-only guard, что и PHASE 0 установки. Guard
   снимается в finally. Диагностика при этом не сузилась: слушатели, healthz,
   права, machine auth, trafficStats, версия бинаря, семантика конфига и
   синтаксис nft проверяются полностью.

2. reset-admin различает «администратора нет» и «база не ответила».

   Слой данных специально возвращает разные sentinel'ы, но команда склеивала их
   обычным `if err != nil { создать } else { обновить }`. Опасен здесь не
   только нарушенный смысл: при транзиентном отказе чтения («database is
   locked») ветка создания отрабатывала успешно, и в таблице оказывались ДВЕ
   учётные записи администратора. GetAdminUser берёт First() и о второй строке
   не сообщает — на сервере оставалась вторая рабочая учётка с паролем, уже
   напечатанным на экран, и ни один запрос об этом не говорил.

   Заодно исправлено проглатывание ошибки хеширования: в ветке обновления
   стояло `hash, _ := util.HashPassword(password)` внутри литерала map. При
   отказе bcrypt в password_hash уезжала пустая строка, а на экран печатался
   пароль, которым войти уже невозможно — VerifyPassword отклоняет всё, что не
   bcrypt. Команда восстановления доступа умела молча его отобрать.

Тесты: doctor-readonly.test.ts дополнен поведенческой проверкой guard и
контролем набора раннеров в smoke; apps/cmd/reset_test.go проверяет обе ветки
на настоящей SQLite и отказ чтения при полностью работоспособной базе — ровно
тот случай, который прежний код превращал во второго администратора. Добавлена
dao.CountAdminUsers: до неё появление дубликата было ненаблюдаемым.
2026-08-30 18:28:39 +05:00

368 lines
17 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.
# Operations and troubleshooting
## Цель документа
Зафиксировать минимальный operational контур после установки.
## Что должен помнить оператор
### 0. Target prerequisites обязательны
На target-хосте install-flow сам обеспечивает системные зависимости (deps stage):
```bash
apt-get update
apt-get install -y sudo ca-certificates curl iproute2 tar openssl nftables systemd
```
`sudo` обязателен для smoke-проверок прав от имени runtime-пользователей (`hysteria`, `hy2xs-admin`), но его не нужно ставить вручную заранее: на clean-host он устанавливается на deps-стадии до smoke.
Важно: packaged baseline использует `HY2XS_SSH_PORT=2323` по умолчанию. На target-хосте это значение обязательно нужно привести к фактическому рабочему SSH-порту оператора в `/etc/hy2xs/hy2xs.env` и применить через `reconfigure --apply`.
### 1. Builder и target — разные миры
Если нужно изменить состав install package, это делается в локальном builder layer, а не на target server.
### 2. UI приезжает из нашего пакета
Если проблема в UI, сначала смотреть:
- какой `HY2XS_ADMIN_SOURCE`
- какой `HY2XS_ADMIN_BUILD_ID`
- тот ли пакет вообще стоит на сервере
### 3. Hysteria приходит из upstream
Если проблема в ядре Hysteria, сначала смотреть:
- какую фактическую версию оркестратор установил
- что записано в `HY2_VERSION`
- не связано ли поведение со свежим upstream release
- какая версия Hysteria зафиксирована в metadata установленного пакета
Для обновления бинарника Hysteria2 используйте новый release install package.
Изменение runtime env не обновляет бинарник Hysteria2.
### 4. Оркестратор — Bun/TypeScript, но target не билдит его
Если проблема в install flow, сначала смотреть:
- какой `ORCH_BUILD_ID`
- какой `ORCH_ENTRYPOINT`
- не подменён ли install package вручную
## Базовые команды проверки
Проверка сервисов:
```bash
systemctl status hysteria-server
systemctl status hy2xs-admin
```
Проверка порта:
```bash
ss -uln
```
Проверка firewall:
```bash
nft list ruleset
```
Проверка `post-install.env`:
```bash
cat /etc/hysteria/post-install.env
```
Проверка логов через journald:
```bash
journalctl -u hysteria-server.service -n 100 --no-pager
journalctl -u hy2xs-admin.service -n 100 --no-pager
```
Источник логов в UI:
- Страница «Логи Hysteria» читает записи из journald unit `hysteria-server.service`.
- Экспорт «Логи Hysteria» также формируется из journald (`journalctl`), а не из отдельного файла `hysteria2.log`.
Проверка install-state marker:
```bash
cat /var/lib/hy2xs/install-state.json
```
Если `reconfigure` сообщает об отсутствии marker, нужно повторно выполнить чистый install и только потом применять runtime-изменения.
## Auth endpoint fail checklist
```bash
systemctl status hysteria-server
systemctl status hy2xs-admin
sudo -u hy2xs-admin test -r /etc/hysteria/config.yaml
curl -sS -X POST \
-H 'Content-Type: application/json' \
--data '{"addr":"127.0.0.1:12345","auth":"invalid","tx":0}' \
http://127.0.0.1:8080/internal/hysteria/auth
curl -sS \
-H "Authorization: <trafficStatsSecret>" \
http://127.0.0.1:36712/online
```
## Типовые проблемы
### Установка отказывается: обнаружена предыдущая установка
Отказ происходит в **PHASE 0**, до любой мутации. Сервер остался в том
состоянии, в котором был: ни `/usr/local/lib/hy2xs`, ни
`/var/lib/hy2xs/install-state.json`, ни работающие службы не тронуты.
В тексте отказа перечислены конкретные найденные маркеры. Порядок действий —
[14-legacy-cleanup.md](14-legacy-cleanup.md): сохранить данные, посмотреть план
`tools/legacy/purge-v0.sh`, выполнить очистку, установить заново.
Проверить хост, ничего не устанавливая:
```bash
./orchestrator/hy2xs-orchestrator preflight-install --package-dir "$(pwd)"
```
### `reconfigure`/`repair` отказываются: маркер чужого поколения
```text
Маркер установки /var/lib/hy2xs/install-state.json не относится к текущему
поколению HY2XS.
```
`installed: true` сам по себе ничего не доказывает: такой же маркер мог
остаться от `0.x`. Обе команды проверяют `product`, `release_line` и
`config_schema_version`.
Посмотреть, что видит оркестратор:
```bash
hy2xs-orchestrator status --package-dir /usr/local/lib/hy2xs/package \
| grep -o '"install_state_generation":"[^"]*"'
```
`"current"` — маркер текущего поколения; `"foreign"` — требуется чистая
переустановка; `"absent"` — установки нет.
### Незавершённая установка текущего поколения
Если установка упала **после** начала применения изменений, полная очистка не
нужна:
```bash
hy2xs-orchestrator repair \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env \
--allow-partial-state
```
Флаг обязателен и осознан: без него `repair` работает только поверх полностью
успешной установки.
### Сервер установился, но UI не работает
Проверить:
- разложился ли bundled UI
- корректен ли unit `hy2xs-admin`
- совпадает ли `HY2XS_ADMIN_INSTALL_DIR` с реальностью
- не сломан ли bind host / port
### Admin UI access via SSH tunnel
Production-модель для UI: `HY2XS_UI_BIND_HOST=127.0.0.1`, внешний доступ к `8080/tcp` не открывается.
Доступ оператора выполняется через SSH local forwarding.
Windows-команда туннеля:
```bash
ssh -p 2323 \
-i C:\Users\kirap\.ssh\id_ed25519_uk1 \
-N \
-L 127.0.0.1:8080:127.0.0.1:8080 \
root@185.156.108.141
```
После запуска открыть `http://127.0.0.1:8080/#/login`.
Если SSH-туннель не поднимается (`administratively prohibited`), проверить effective SSH policy:
```bash
sshd -T | grep -E '^(port|allowtcpforwarding|permitopen|gatewayports|passwordauthentication|permitrootlogin) '
```
Рекомендуемый фрагмент hardening `sshd_config`:
```sshconfig
Port 2323
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
AllowTcpForwarding local
PermitOpen 127.0.0.1:8080 localhost:8080
GatewayPorts no
X11Forwarding no
AllowAgentForwarding no
MaxAuthTries 3
LoginGraceTime 20
ClientAliveInterval 300
ClientAliveCountMax 2
```
### Hysteria скачалась, но не стартует
Проверить:
- валиден ли config
- совпадают ли listen port и firewall rule
- домен / SNI / TLS policy
- реальную установленную версию Hysteria
### Тестовый клиент не подключается
Проверить:
- `server_name`
- порт
- `obfs.password`
- auth material
- что используется совместимый клиентский конфиг
### Install/reconfigure падает на DNS AAAA
Проверить значение `HY2XS_DNS_AAAA_POLICY` в `/etc/hy2xs/hy2xs.env`:
- `strict` (default): AAAA приводит к fail в IPv4-only профиле;
- `warn`: warning + продолжение;
- `off`: AAAA-check отключён.
Для production baseline рекомендуется `strict`.
### `DNS IPv4 mismatch`: DNS ведёт не на этот сервер
Сообщение выглядит так:
```text
DNS IPv4 mismatch for HY2XS_PUBLIC_HOST fi.api.withen.pro:
DNS A records: 185.xxx.xxx.10
server public IPv4: 185.xxx.xxx.27
Update the DNS A record before using this server.
```
Это не ложное срабатывание, а именно то, ради чего проверка сделана: сервисы на
машине живы, но публичный endpoint ведёт куда-то ещё. Чаще всего — после
принудительной смены IPv4 провайдером.
Что делать:
1. сверить фактический адрес сервера: `ip -4 addr show scope global`;
2. обновить A-запись у DNS-провайдера;
3. дождаться истечения TTL;
4. повторить `hy2xs-orchestrator doctor`.
`doctor` безопасно запускать на работающем сервере: он не перезапускает
сервисы и живые соединения не рвёт. Раньше это было не так — команда звала
общий smoke, который начинается с `systemctl restart hysteria-server
hy2xs-admin`, и диагностика подозрения на проблему сама создавала обрыв у всех
подключённых клиентов.
Безопасность здесь — инвариант рантайма, а не свойство текущего кода. `doctor`
целиком выполняется под тем же read-only guard, что и PHASE 0 установки: любая
запись в файл и любой мутирующий вызов под ним отказывают. Раньше от рестарта
защищал один принудительный флаг, а остальные проверки smoke — чтение прав,
владельцев и синтаксиса `nftables` — выполнялись мутирующими раннерами, поэтому
настоящая мутация, случайно добавленная в smoke, была бы разрешена молча.
При этом диагностика не сужается: слушатели, `healthz`, права на файлы, machine
auth, `trafficStats`, версия бинаря, семантика `/etc/hysteria/config.yaml` и
синтаксис `nft` проверяются полностью.
Вариант `server public IPv4:` пустой означает, что на интерфейсах нет ни одного
публичного маршрутизируемого IPv4 — сервер за NAT. Это топология вне baseline;
осознанное решение оформляется через `HY2XS_PUBLIC_ENDPOINT_POLICY=warn`.
Если в A-записях присутствует правильный адрес **и** посторонний, проверка тоже
отказывает. HY2XS — single-host профиль: второй backend за тем же именем
означает, что часть клиентов попадёт не на этот сервер.
### Скорость не соответствует ожиданиям
Проверить:
- `bandwidth.*` на сервере
- клиентские `up_mbps/down_mbps`
- нет ли ложного ожидания, что один только host BBR решает speed policy
### Изменили `post-install.env`, но runtime не изменился
Это ожидаемо.
`post-install.env` — reference file, а не autoreconcile engine.
Редактировать нужно `/etc/hy2xs/hy2xs.env` и затем запускать `reconfigure --dry-run/--apply`.
### Изменили bootstrap-поля, но пароль admin не сменился
Это ожидаемо.
`HY2XS_ADMIN_INITIAL_PASSWORD` и `HY2XS_ADMIN_CON_PASS` используются только как bootstrap-данные при первичной установке.
Для ротации существующих credentials нужен отдельный flow на уровне account-management.
### `hy2xs-admin` не стартует: «HY2XS_ADMIN_INITIAL_PASSWORD не задан»
Означает, что учётной записи администратора в базе нет, а переменной, из которой
её положено создать, — тоже.
Придумывать пароль самостоятельно админка не будет: такой пароль не знал бы
никто, кроме журнала, а раньше именно он туда и попадал открытым текстом.
Сообщение говорит о повреждённом контракте запуска.
Что проверять:
```bash
systemctl cat hy2xs-admin | grep EnvironmentFile
grep -c '^HY2XS_ADMIN_INITIAL_PASSWORD=' /etc/hy2xs/hy2xs.env
grep -c '^ADMIN_INITIAL_PASSWORD=' /etc/hy2xs/bootstrap-admin.secret
```
Починка — `hy2xs-orchestrator repair --allow-partial-state`: значения принадлежат
оркестратору, он же приводит `hy2xs.env` и `bootstrap-admin.secret` в
согласованное состояние.
Аналогичное сообщение про `HY2XS_ADMIN_CON_PASS` относится к пиру установщика.
Его секрет продублирован в `bootstrap-admin.secret`, откуда его читает проверка
machine-auth, поэтому придуманный секрет разошёлся бы с файлом и первая же
проверка подключения провалилась бы.
### Забыт пароль администратора
```bash
systemctl stop hy2xs-admin
set -a; . /etc/hy2xs/hy2xs.env; set +a
"$HY2XS_INSTALL_DIR/hy2xs-admin" reset-admin
systemctl start hy2xs-admin
```
Runtime env подключается намеренно: из него берутся пути к базе и журналу
(`HY2XS_DATA_DIR`, `HY2XS_LOG_DIR`) — те же, с которыми работает юнит.
Команда печатает новые логин и пароль в консоль и требует смены пароля при
первом входе. Работает поверх существующей установки; на машине без базы она
осмысленно откажет — это инструмент восстановления, а не установки.
### Автоматический сброс трафика не срабатывает
Проверьте сохранённое расписание:
```bash
journalctl -u hy2xs-admin | grep RESET_TRAFFIC_CRON
```
Невалидное выражение теперь отклоняется API до записи в базу, поэтому попасть в
это состояние можно только правкой базы в обход продукта. Планировщик в таком
случае поднимается без джобы сброса и пишет ERROR — старт сервиса при этом не
прерывается намеренно: на панели висит `/internal/hysteria/auth`, и её отказ
положил бы подключения пользователей.
Починка — сохранить корректное значение в разделе настроек панели; оно
применяется сразу, без перезапуска сервиса. Пустое значение — легальное и
означает «автоматический сброс выключен».
## Правила эксплуатации
1. Не править сервер как будто на нём есть builder.
2. Не считать bundled UI источником install-policy.
3. Не считать `post-install.env` автоматическим механизмом применения изменений.
4. Не расширять install-only baseline до lifecycle-manager без отдельного проектного решения.
5. Не смешивать install baseline и access/bot platform в одной документации.
6. Не включать IPv6 в runtime-политике HY2XS (проект IPv4-only).