# 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: " \ 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` работает только поверх полностью успешной установки. ### Операция отказывает: уже выполняется другая ```text 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` — общие, и две одновременные операции записывают их поверх друг друга, после чего откат одной «восстанавливает» состояние поверх изменений другой. Отказ происходит **до** первой мутации, поэтому сервер не тронут. Что делать: ```bash # кто держит замок 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, а мёртвого держателя следующая операция обнаруживает и переиспользует замок самостоятельно. Удалять файл руками нужно ровно в одном случае — если оркестратор сообщил, что содержимое замка не является корректной записью: ```text operation lock … exists but is not a valid HY2XS lock record ``` Такой замок сознательно не снимается автоматически: непонятый файл не доказывает, что операции нет. `status` и `diagnostics collect` замок не берут и работают во время операции. В отчёте `status` при этом появляется поле `operation_in_progress` — читайте состояние как снимок незавершённой транзакции, а не как итог. ### Операция отказывает: guard предыдущей операции ещё вооружён ```text previous HY2XS operation is no longer running, but its firewall rollback guard is still armed: hy2xs-fw-rollback-.timer (active) ``` Предыдущая операция умерла аварийно **после** применения firewall. Её процесса уже нет — замка может не быть тоже, — но rollback guard это отдельный объект systemd, и он переживает свой процесс. Если начать новую операцию сейчас, guard сработает посреди неё и вернёт firewall, существовавший до **предыдущей** операции. Ничего делать не нужно, кроме как подождать: окно guard — 45 секунд с момента применения firewall. ```bash # сколько ещё ждать и что именно висит systemctl list-units --all 'hy2xs-fw-rollback-*' hy2xs-orchestrator status --package-dir /usr/local/lib/hy2xs/package ``` Когда guard сработает, юнит перестанет быть `active`, и операция пройдёт. Состояние `failed` у него покою не мешает: оно означает, что откат отработал не полностью, и это как раз повод запустить `repair`, а не ждать дальше — подробности в `journalctl -u 'hy2xs-fw-rollback-*'`. ### Установка отказала с `firewall_guard_fired` ```text automatic firewall rollback has already fired phase: firewall_guard_fired ``` Это означает: автоматический откат firewall сработал раньше, чем операция успела снять guard. Сервер жив и доступен, но работает на **прежнем** firewall, а не на том, который сгенерировала операция. Именно поэтому фиксация успеха запрещена, даже если smoke успел сойтись, — иначе сервер считался бы настроенным с чужими правилами, что особенно дорого при смене порта Hysteria, SSH или ACME. Окно guard — 45 секунд, и оно не обязано покрывать smoke. Причину ищите в том, почему проход в него не уложился: ```bash 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-команда туннеля: ```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` проверяются полностью. ### Где проходит граница 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 не задан» Означает, что учётной записи администратора в базе нет, а переменной, из которой её положено создать, — тоже. Придумывать пароль самостоятельно админка не будет: такой пароль не знал бы никто, кроме журнала, а раньше именно он туда и попадал открытым текстом. Сообщение говорит о повреждённом контракте запуска. Что проверять: ```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).