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

17 KiB
Raw Blame History

Operations and troubleshooting

Цель документа

Зафиксировать минимальный operational контур после установки.

Что должен помнить оператор

0. Target prerequisites обязательны

На target-хосте install-flow сам обеспечивает системные зависимости (deps stage):

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 вручную

Базовые команды проверки

Проверка сервисов:

systemctl status hysteria-server
systemctl status hy2xs-admin

Проверка порта:

ss -uln

Проверка firewall:

nft list ruleset

Проверка post-install.env:

cat /etc/hysteria/post-install.env

Проверка логов через journald:

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:

cat /var/lib/hy2xs/install-state.json

Если reconfigure сообщает об отсутствии marker, нужно повторно выполнить чистый install и только потом применять runtime-изменения.

Auth endpoint fail checklist

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: сохранить данные, посмотреть план tools/legacy/purge-v0.sh, выполнить очистку, установить заново.

Проверить хост, ничего не устанавливая:

./orchestrator/hy2xs-orchestrator preflight-install --package-dir "$(pwd)"

reconfigure/repair отказываются: маркер чужого поколения

Маркер установки /var/lib/hy2xs/install-state.json не относится к текущему
поколению HY2XS.

installed: true сам по себе ничего не доказывает: такой же маркер мог остаться от 0.x. Обе команды проверяют product, release_line и config_schema_version.

Посмотреть, что видит оркестратор:

hy2xs-orchestrator status --package-dir /usr/local/lib/hy2xs/package \
  | grep -o '"install_state_generation":"[^"]*"'

"current" — маркер текущего поколения; "foreign" — требуется чистая переустановка; "absent" — установки нет.

Незавершённая установка текущего поколения

Если установка упала после начала применения изменений, полная очистка не нужна:

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-команда туннеля:

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:

sshd -T | grep -E '^(port|allowtcpforwarding|permitopen|gatewayports|passwordauthentication|permitrootlogin) '

Рекомендуемый фрагмент hardening sshd_config:

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 ведёт не на этот сервер

Сообщение выглядит так:

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 не задан»

Означает, что учётной записи администратора в базе нет, а переменной, из которой её положено создать, — тоже.

Придумывать пароль самостоятельно админка не будет: такой пароль не знал бы никто, кроме журнала, а раньше именно он туда и попадал открытым текстом. Сообщение говорит о повреждённом контракте запуска.

Что проверять:

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, поэтому придуманный секрет разошёлся бы с файлом и первая же проверка подключения провалилась бы.

Забыт пароль администратора

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) — те же, с которыми работает юнит.

Команда печатает новые логин и пароль в консоль и требует смены пароля при первом входе. Работает поверх существующей установки; на машине без базы она осмысленно откажет — это инструмент восстановления, а не установки.

Автоматический сброс трафика не срабатывает

Проверьте сохранённое расписание:

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).