Признак на странице конфигурации отвечал только на вопрос «достучится ли админка», поэтому 0.0.0.0 показывался как норма — хотя внутренний control plane при нём опубликован на всех интерфейсах, а оркестратор такой конфигурации не создаёт. Состояний теперь четыре: канон профиля, wildcard, не-канонический loopback и недостижимый адрес. Backend не тронут: он по-прежнему отвечает только на вопрос достижимости — превращать лишнюю публикацию в отказ обслуживания значило бы отключить всех пиров. Исправлено ложное утверждение в его комментарии: пустой хост `:36712` в Go означает все интерфейсы, а не loopback. Удалены мёртвые фразы common.wait/enableSuccess/disableSuccess — остатки операций запуска, остановки и смены версии Hysteria, которых у панели нет.
30 KiB
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 работает только поверх полностью
успешной установки.
Операция отказывает: уже выполняется другая
another HY2XS operation is already in progress: reconfigure (pid 4242, started at …)
install, reconfigure, repair и doctor сериализованы замком
/run/lock/hy2xs-orchestrator.lock. Это не перестраховка: production paths —
/etc/hysteria/config.yaml, unit-файлы, /etc/nftables.conf,
/var/lib/hy2xs/install-state.json — общие, и две одновременные операции
записывают их поверх друг друга, после чего откат одной «восстанавливает»
состояние поверх изменений другой.
Отказ происходит до первой мутации, поэтому сервер не тронут. Что делать:
# кто держит замок
cat /run/lock/hy2xs-orchestrator.lock
# что делает держатель
ps -o pid,etime,cmd -p "$(sed -n 's/.*"pid": *\([0-9]*\).*/\1/p' /run/lock/hy2xs-orchestrator.lock)"
Дождитесь завершения. Замок снимается сам при любом завершении держателя,
включая Ctrl+C, SIGTERM и обрыв SSH, а мёртвого держателя следующая операция
обнаруживает и переиспользует замок самостоятельно.
Удалять файл руками нужно ровно в одном случае — если оркестратор сообщил, что содержимое замка не является корректной записью:
operation lock … exists but is not a valid HY2XS lock record
Такой замок сознательно не снимается автоматически: непонятый файл не доказывает, что операции нет.
status и diagnostics collect замок не берут и работают во время операции.
В отчёте status при этом появляется поле operation_in_progress — читайте
состояние как снимок незавершённой транзакции, а не как итог.
Операция отказывает: guard предыдущей операции ещё вооружён
previous HY2XS operation is no longer running, but its firewall rollback guard
is still armed: hy2xs-fw-rollback-<op-id>.timer (active/waiting)
Предыдущая операция умерла аварийно после применения firewall. Её процесса уже нет — замка может не быть тоже, — но rollback guard это отдельный объект systemd, и он переживает свой процесс. Если начать новую операцию сейчас, guard сработает посреди неё и вернёт firewall, существовавший до предыдущей операции.
Ничего делать не нужно, кроме как подождать: окно guard — 45 секунд с момента
применения firewall плюс точность таймера (AccuracySec=1s), то есть не больше
46 секунд.
# сколько ещё ждать и что именно висит
systemctl list-units --all --plain 'hy2xs-fw-rollback-*'
hy2xs-orchestrator status --package-dir /usr/local/lib/hy2xs/package
--plain здесь не для красоты: у юнита в состоянии failed systemctl печатает
первой колонкой маркер ●, и без флага его легко не заметить в списке.
Когда guard сработает, отработавший таймер выгрузится (RemainAfterElapse=no),
и операция пройдёт. Состояние failed у сервиса покою не мешает: оно означает,
что откат отработал не полностью, и это как раз повод запустить repair, а не
ждать дальше — подробности в journalctl -u 'hy2xs-fw-rollback-*'. Такой
failed-юнит остаётся загруженным до systemctl reset-failed, но операцию не
блокирует.
Операция отказывает: состояние guard'а не удалось выяснить
unable to verify firewall rollback guard state; systemd query failed,
refusing to start a lifecycle operation: systemctl list-units failed: …
Это не «guard вооружён», и ждать здесь нечего. Барьер обязан доказать, что у
предыдущей операции не осталось исполнителей, способных изменить firewall;
systemctl не ответил, доказательства нет, и операция отказывает до первой
мутации.
Отсутствие ответа не равно отсутствию guard'а: systemd мог быть жив, а взведённый таймер — существовать. Разрешить операцию в этой ситуации означало бы допустить срабатывание старого таймера поверх новой операции.
systemctl status
systemctl list-units --all --plain 'hy2xs-fw-rollback-*'
journalctl -u 'hy2xs-fw-rollback-*' --no-pager
Разбирайтесь с systemd/D-Bus и повторяйте операцию. Отдельно ускорять ничего не
нужно: systemd-run требуется в preflight, поэтому без работающего systemd
операция всё равно не прошла бы — барьер лишь сообщает об этом раньше и точнее.
Установка отказала с firewall_guard_fired
automatic firewall rollback has already fired
phase: firewall_guard_fired
Это означает: автоматический откат firewall сработал раньше, чем операция успела снять guard. Сервер жив и доступен, но работает на прежнем firewall, а не на том, который сгенерировала операция. Именно поэтому фиксация успеха запрещена, даже если smoke успел сойтись, — иначе сервер считался бы настроенным с чужими правилами, что особенно дорого при смене порта Hysteria, SSH или ACME.
Окно guard — 45 секунд (плюс точность таймера, AccuracySec=1s), и оно не
обязано покрывать smoke. Причину ищите в том, почему проход в него не уложился:
journalctl -u 'hy2xs-fw-rollback-*' --no-pager
journalctl -u hysteria-server -u hy2xs-admin --since '-10 min' --no-pager
Обычная причина — медленный старт одного из сервисов. После устранения root-cause операция запускается заново; откат уже вернул сервер в исходное состояние.
Сервер установился, но 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
- что используется совместимый клиентский конфиг
Ни один пир не проходит авторизацию
Симптом резкий: подключения перестают устанавливаться у всех сразу, в журнале
админки — device limit unavailable.
Лимит устройств проверяется fail-closed: без ответа /online админка не
знает, сколько устройств уже на связи, и пускать подключения не имеет права.
Значит вопрос ровно один — почему недоступен Traffic Stats API.
# 1. что записано в конфиге
grep -A2 '^trafficStats:' /etc/hysteria/config.yaml
# 2. отвечает ли API по этому адресу
curl -sS -H "Authorization: <trafficStatsSecret>" http://127.0.0.1:36712/online
# 3. что говорит сама админка
journalctl -u hy2xs-admin -n 100 --no-pager | grep -i 'traffic stats'
Частая причина — правка trafficStats.listen руками. Админка обращается к
Traffic Stats API только по loopback, поэтому адрес вроде 192.168.1.10
разводит Hysteria и панель по разным адресам: сама Hysteria работает, туннели
существующих клиентов живут, но лимит устройств, учёт трафика и принудительное
отключение выключаются разом. С таким конфигом админка отказывает явно:
trafficStats.listen слушает 192.168.1.10, а админка обращается к Traffic Stats
API только по loopback. ...Верните 127.0.0.1 через `hy2xs-orchestrator reconfigure`
Страница конфигурации показывает тот же адрес и называет его состояние. Ответов три, и они означают разное:
| адрес | что показано | что это значит |
|---|---|---|
127.0.0.1:36712 |
без пометки | канон production-профиля |
0.0.0.0:36712, :36712 |
предупреждение | API достижим, но опубликован на всех интерфейсах; при HY2XS_FIREWALL_MODE=external|off его не прикрывает ничто |
127.0.0.5:36712 |
предупреждение | достижим, но оркестратор такого не создаёт — конфиг правили руками |
192.168.1.10:36712 |
ошибка | панель до него не достучится, доступ пиров уже не работает |
Пустой хост в listen — это не loopback: в Go :36712 означает все интерфейсы,
ровно как 0.0.0.0.
Дашборд показывает «состояние службы неизвестно»
Это не «Hysteria остановлена». Значение означает, что не удалось получить
ответ systemctl is-active hysteria-server: сломанный или недоступный systemctl
при живой Hysteria выглядит именно так.
Проверяется отдельно от туннеля:
systemctl is-active hysteria-server # active | inactive | failed | ...
Доступность Traffic Stats API на дашборде — независимый факт, полученный фактическим обращением к API. Сочетание «состояние неизвестно» + «API доступен» означает исправно работающий туннель и сломанную диагностику службы; сочетание «служба активна» + «API недоступен» — предыдущий раздел.
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 провайдером.
Что делать:
- сверить фактический адрес сервера:
ip -4 addr show scope global; - обновить A-запись у DNS-провайдера;
- дождаться истечения TTL;
- повторить
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 проверяются полностью.
Где проходит граница read-only
Guard действует внутри процесса оркестратора. Он не способен запретить побочный эффект, который вызвал бы HTTP-запрос в другом процессе, поэтому эта половина границы держится не им, а составом проб.
Существенный случай — machine-auth. Успешная авторизация пира заставляет админку
выполнить UPDATE peer.last_connection_at, то есть диагностика изменила бы
отображаемое «последнее подключение» у bootstrap-admin-peer. Поэтому проба с
действующим паролем выполняется только в режиме install; doctor работает
в режиме reconfigure и до неё не доходит. Полный happy-path авторизации
проверяют установка и E2E, а не диагностика.
doctor отправляет только пробы, которые заведомо не проходят авторизацию
(отсутствующий machine token, неверные учётные данные, некорректный тип поля) и
читающие запросы (/healthz, trafficStats /online). Ни одна из них не
изменяет данные.
Честная формулировка гарантии:
doctorне изменяет конфигурацию, состояние сервисов, firewall и данные.
Единственный след, который он оставляет, — записи в журнале админки: пробы проходят через обычный обработчик логирования, как любой запрос. Это не состояние системы, но и не «совсем ничего», поэтому сказано прямо.
Вариант 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, и её отказ
положил бы подключения пользователей.
Починка — сохранить корректное значение в разделе настроек панели; оно применяется сразу, без перезапуска сервиса. Пустое значение — легальное и означает «автоматический сброс выключен».
Правила эксплуатации
- Не править сервер как будто на нём есть builder.
- Не считать bundled UI источником install-policy.
- Не считать
post-install.envавтоматическим механизмом применения изменений. - Не расширять install-only baseline до lifecycle-manager без отдельного проектного решения.
- Не смешивать install baseline и access/bot platform в одной документации.
- Не включать IPv6 в runtime-политике HY2XS (проект IPv4-only).