Files
HY2XS_flamy/docs/14-legacy-cleanup.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

246 lines
13 KiB
Markdown

# Очистка сервера от предыдущей установки
## Зачем этот документ
HY2XS v1 **не поддерживает установку поверх** и **не мигрирует состояние 0.x**.
Это осознанное решение продукта, а не временное ограничение: попытка угадать,
как устроен произвольный старый сервер, приводит к полурабочим установкам,
которые невозможно диагностировать.
Отсюда следует жёсткий системный инвариант:
```text
обнаружена старая установка
НОЛЬ изменений на сервере
понятный отказ
явная очистка (этот документ)
установка HY2XS v1 с нуля
```
Установщик **никогда** не выполняет очистку самостоятельно. Удаление чужого
состояния — операция оператора, а не побочный эффект запуска `install.sh`.
## Как выглядит отказ
Установщик проверяет чистоту хоста в **PHASE 0** — до того, как изменит хотя бы
один persistent path, включая `/usr/local/lib/hy2xs`:
```text
[hy2xs-install] PHASE 0: read-only checks (no persistent path is modified)
[hy2xs-install] verifying package checksums
[hy2xs-install] running clean-host preflight from the unpacked package
[hy2xs] ERROR: На сервере обнаружена предыдущая или посторонняя установка.
HY2XS v1 не поддерживает установку поверх и не мигрирует состояние 0.x.
Ни один файл на сервере не изменён.
Найденные маркеры:
- /etc/hysteria/post-install.env (post-install.env предыдущей установки HY2XS)
- hy2xs-admin.service (systemd-юнит админки HY2XS)
Очистите сервер и установите HY2XS заново: см. docs/14-legacy-cleanup.md
```
Если вы видите этот текст — сервер в том же состоянии, в котором был до запуска.
Отдельный случай — конфигурация без маркера схемы:
```text
HY2XS_CONFIG_SCHEMA_VERSION отсутствует в конфигурации.
Похоже на конфигурацию предыдущего поколения (0.x) или на неизвестный формат.
```
До v1 поля `HY2XS_CONFIG_SCHEMA_VERSION` не существовало, поэтому его отсутствие
трактуется как legacy, а не как «текущая схема по умолчанию».
## Что именно проверяется
Контракт чистого хоста объявлен в `orchestrator/src/steps/cleanHost.ts` и покрыт
тестами. Установка отказывается, если найден хотя бы один из объектов:
| Объект | Что это |
| --- | --- |
| `/etc/hysteria/post-install.env` | post-install.env предыдущей установки |
| `/etc/hy2xs/hy2xs.env` | runtime-конфигурация предыдущей установки |
| `/etc/hy2xs/bootstrap-admin.secret` | bootstrap-секрет администратора |
| `/var/lib/hy2xs/install-state.json` | маркер состояния установки |
| `/usr/local/lib/hy2xs/package` | runtime-пакет предыдущей установки |
| `/etc/hysteria/config.yaml` | сгенерированный серверный конфиг |
| `/usr/local/bin/hysteria` | уже установленный бинарник Hysteria |
| `/etc/nftables.d/hy2xs.nft` | nftables-фрагмент HY2XS |
| `hy2xs-admin.service` | systemd-юнит админки |
| `hysteria-server.service` | systemd-юнит сервера Hysteria |
| `h-ui.service`, `/usr/local/h-ui` | наследие панели поколения 0.x |
| `HY2XS_INSTALL_DIR` (по умолчанию `/opt/hy2xs-admin`) | каталог приложения |
| `HY2XS_DATA_DIR` (по умолчанию `/var/lib/hy2xs-admin`) | каталог данных и БД |
Последние два пути берутся из конфигурации, а не захардкожены: нестандартная
установка тоже должна быть обнаружена.
## Перед очисткой
Очистка **разрушительная**. Она удаляет базу админки вместе с учётными записями
пиров: выданные пользователям ссылки перестанут работать.
Сохраните то, что вам нужно:
```bash
# ссылки и учётные записи пиров (если старая панель ещё работает)
sudo sqlite3 /var/lib/hy2xs-admin/h_ui.db '.dump' > ~/hy2xs-peers-dump.sql
# серверный конфиг Hysteria
sudo cp -a /etc/hysteria/config.yaml ~/hysteria-config.yaml.bak
# post-install-справка предыдущей установки
sudo cp -a /etc/hysteria/post-install.env ~/post-install.env.bak
```
Файлы содержат секреты. Снимите с них лишние права и не пересылайте как есть:
```bash
chmod 600 ~/hy2xs-peers-dump.sql ~/hysteria-config.yaml.bak ~/post-install.env.bak
```
Для безопасной передачи конфига наружу используйте редактирование секретов:
```bash
hy2xs-orchestrator redact-config --config ~/hysteria-config.yaml.bak --out ~/hysteria-config.redacted.yaml
```
## Очистка скриптом
Скрипт `tools/legacy/purge-v0.sh` лежит в репозитории. Скопируйте его на сервер.
Сначала — план. Без флагов скрипт **ничего не меняет**:
```bash
sudo ./purge-v0.sh
```
Он покажет, какие службы будут остановлены, какие пути удалены и какие из них
существуют прямо сейчас.
Затем — выполнение. Требуются оба флага, `--apply` без подтверждения не работает:
```bash
sudo ./purge-v0.sh --apply --yes-i-know
```
Возможности «оставить бинарник Hysteria» у скрипта нет намеренно.
`/usr/local/bin/hysteria` входит в clean-host контракт установщика: сервер, где
он остался, установку HY2XS v1 не пройдёт. Скрипт, который сохранял бы бинарник
и при этом сообщал «хост чист», прямо противоречил бы следующему запуску
`install.sh`. Свежая установка всё равно кладёт собственную версию Hysteria,
проверенную по SHA-256 против upstream `hashes.txt`.
В конце скрипт сам проверяет, что хост стал чистым по тому же контракту, который
применяет установщик. Если что-то осталось, он назовёт конкретные объекты и
завершится с ошибкой.
## Что скрипт делает и чего не делает
Делает:
1. останавливает и выключает `hysteria-server`, `hy2xs-admin`, `h-ui`;
2. снимает таймеры отката firewall `hy2xs-fw-rollback-*` — они переживают
неудачную установку и иначе продолжили бы менять ruleset уже после очистки;
3. удаляет unit-файлы и выполняет `daemon-reload`;
4. удаляет каталоги приложения, конфигурации, данных и логов, включая
`/var/lib/hysteria` (там остаётся ACME-состояние и сертификаты Hysteria),
`/usr/local/lib/hy2xs` и symlink `/usr/local/bin/hy2xs-orchestrator`;
5. удаляет `/usr/local/bin/hysteria`;
6. удаляет артефакты незавершённой операции в `/run`: каталог отката
`/run/hy2xs` (прежние `nftables.conf`, `hy2xs.nft` и маркер срабатывания
guard) и замок операций `/run/lock/hy2xs-orchestrator.lock`, который может
пережить убитый `kill -9` процесс оркестратора и не дать запуститься
следующей установке. `/run` — tmpfs, и перезагрузка убрала бы оба, но
очистка не имеет права требовать перезагрузки;
7. удаляет `*.candidate` firewall — они остаются, если операция упала между
`nft -c` и подстановкой файла в production-путь;
8. удаляет фрагмент `/etc/nftables.d/hy2xs.nft` и строку `include` для него из
`/etc/nftables.conf`, после чего перезагружает ruleset;
9. проверяет чистоту хоста.
Список удаляемых путей и список legacy-маркеров clean-host контракта описывают
одну и ту же границу: расхождение между ними ловится приёмкой сборки. Иначе
возможен сервер, с которого «всё удалено», но который установщик всё равно
считает грязным — или, что хуже, наоборот.
Не делает:
- не трогает `sshd` и его конфигурацию;
- не удаляет `/etc/nftables.conf` целиком — остальной ruleset принадлежит
оператору;
- не удаляет системные пакеты, установленные ранее;
- не запускается автоматически из установщика.
## Ручная очистка
Если запускать скрипт нежелательно, те же шаги вручную:
```bash
sudo systemctl stop hysteria-server hy2xs-admin h-ui
sudo systemctl disable hysteria-server hy2xs-admin h-ui
sudo systemctl reset-failed hysteria-server hy2xs-admin h-ui
# таймеры отката firewall от незавершённой установки
sudo systemctl list-units --all 'hy2xs-fw-rollback-*'
# для каждого найденного юнита:
# sudo systemctl stop <unit> && sudo systemctl disable <unit>
# sudo rm -f /etc/systemd/system/<unit>
sudo rm -f /etc/systemd/system/hysteria-server.service \
/etc/systemd/system/hy2xs-admin.service \
/etc/systemd/system/h-ui.service
sudo systemctl daemon-reload
sudo rm -rf /etc/hy2xs /etc/hysteria /var/lib/hy2xs /var/lib/hy2xs-admin \
/var/lib/hysteria /var/log/hy2xs /opt/hy2xs-admin \
/usr/local/lib/hy2xs /usr/local/h-ui
sudo rm -f /usr/local/bin/hysteria /usr/local/bin/hy2xs-orchestrator
# артефакты незавершённой операции в /run (tmpfs)
sudo rm -rf /run/hy2xs
sudo rm -f /run/lock/hy2xs-orchestrator.lock
# candidate-файлы firewall от операции, упавшей до подстановки
sudo rm -f /etc/nftables.conf.candidate /etc/nftables.d/hy2xs.nft.candidate
sudo rm -f /etc/nftables.d/hy2xs.nft
sudo sed -i '/nftables.d\/hy2xs.nft/d' /etc/nftables.conf
sudo nft -c -f /etc/nftables.conf && sudo nft -f /etc/nftables.conf
```
## После очистки
Устанавливайте HY2XS v1 обычным путём. PHASE 0 установщика повторит проверку
чистоты хоста и подтвердит, что всё в порядке:
```bash
sudo ./install.sh
```
## Незавершённая установка v1 — это другой случай
Если установка HY2XS v1 упала **после** начала применения изменений, полная
очистка не нужна. У такой машины есть корректный маркер состояния текущего
поколения, и её чинит `repair`:
```bash
sudo hy2xs-orchestrator repair \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env \
--allow-partial-state
```
Флаг `--allow-partial-state` обязателен и осознан: без него `repair` работает
только поверх полностью успешной установки. При этом `repair` всё равно
проверяет, что маркер принадлежит текущему поколению (`product`,
`release_line`, `config_schema_version`), и откажется чинить чужое состояние.
Отказ вида «install state marker … не относится к текущему поколению HY2XS»
означает, что `repair` неприменим и нужна очистка по этому документу.