Files
HY2XS_flamy/docs/12-operations-and-troubleshooting.md
T
founder 39139e95f7 docs: описать транзакционный guard и взаимное исключение операций
- docs/07: полный порядок staged apply, инвариант снятия guard, объяснение
  почему окно 45 секунд не обязано покрывать smoke и почему guard не трогает
  nftables.service, семантическая проверка эффективного firewall;
- docs/11: разделы A5e/A5f для новых unit-тестов и серверные сценарии D1e
  (guard доходит до дедлайна), D1f (конкурентные операции), D1g (успешная
  операция не оставляет следов транзакции); матрица и acceptance criteria
  дополнены;
- docs/12: разбор отказов "уже выполняется другая операция" и
  firewall_guard_fired;
- docs/13: строки журнала guard в таблице recovery, новый раздел 8a про замок
  операций;
- docs/14 и purge-v0.sh: очистка /run/hy2xs, замка операций и candidate-файлов
  firewall — /run это tmpfs, но очистка не имеет права требовать перезагрузки;
- README: защита от потери доступа при смене firewall и раздел "Одна операция
  за раз";
- CHANGELOG: шестой проход.
2026-08-31 01:31:15 +05:00

460 lines
23 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` работает только поверх полностью
успешной установки.
### Операция отказывает: уже выполняется другая
```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` — читайте
состояние как снимок незавершённой транзакции, а не как итог.
### Установка отказала с `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).