Files
HY2XS_flamy/CHANGELOG.md
T
founder 3a4ce9c751 docs: clean-install-only, versions.env и очистка предыдущего поколения
Новый docs/14-legacy-cleanup.md: как выглядит отказ установщика, полный
список маркеров чужой установки, что сохранить перед очисткой, работа
purge-v0.sh, ручная процедура и отдельно - случай незавершённой
установки текущего поколения, где нужен repair, а не очистка.

Обновлено под фактическое поведение:

- README и package/docs: установка описана как две фазы, PHASE 0 ничего
  не меняет; добавлен troubleshooting по отказу clean-host; версии
  toolchain больше не передаются через окружение;
- 02-build-layer: раздел про versions.env (что в нём есть и чего нет и
  почему), verify_versions_contract, проверка происхождения артефакта
  по upstream hashes.txt;
- 08-orchestrator-spec: двухфазный контракт, read-only guard,
  идентификация поколения в install-state, ownership-aware rollback,
  расширенная семантическая проверка конфига, структурная редакция;
- 04-admin-panel: таблица удалённых маршрутов и почему они удалены, а
  не оставлены заглушками; сужена формулировка гарантии санитайза;
- 11-testing: новые unit-наборы, полный список инвариантов конфига,
  раздел про одну реализацию URI вместо двух, сценарий проверки
  границы установки на живом сервере;
- 12-operations и 13-runbook: диагностика отказов по поколению,
  поведение diagnostics-бандла;
- tools/build/README: контракт версий, обе суммы Bun, hashes.txt.

CHANGELOG: раздел Unreleased с разбором каждого исправленного дефекта.
2026-08-27 12:16:38 +05:00

256 lines
20 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.
# Изменения HY2XS
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/).
Проект использует [семантическое версионирование](https://semver.org/lang/ru/).
Версия относится к самому HY2XS, а не к Hysteria: версия Hysteria выбирается
на этапе сборки пакета и фиксируется в его metadata.
## [Unreleased]
Hardening-проход перед релизом `1.0.0`. Основная тема — сделать политику
«только чистая установка» настоящим системным инвариантом, а не строчкой в
документации.
### Исправлено
- **Установщик мог повредить работающий сервер до того, как откажется его
трогать.** `install.sh` переписывал `/usr/local/lib/hy2xs`, раскладывал
runtime-пакет и перезаписывал `/var/lib/hy2xs/install-state.json`, и лишь
потом запускал clean-host preflight. При ошибочном запуске поверх старого
сервера rollback дополнительно выполнял `stop` и `disable` для работающих
`hysteria-server` и `hy2xs-admin`.
Установка разделена на две фазы с жёсткой границей: **PHASE 0 — read only**,
**PHASE 1 — mutation**. Read-only проверка выполняется новой командой
`hy2xs-orchestrator preflight-install` из распакованного архива, а граница
держится runtime-guard'ом, а не соглашением.
- **Отсутствие `HY2XS_CONFIG_SCHEMA_VERSION` считалось текущей схемой.** До v1
этого поля не существовало, поэтому именно пустое значение — самый вероятный
признак конфигурации `0.x`. Теперь оно отклоняется как legacy с указанием на
чистую установку. Тест, закреплявший прежнее поведение, инвертирован.
- **`reconfigure` и `repair` работали поверх любого маркера установки.**
Проверялся только флаг `installed`, который мог остаться и от `0.x`.
Маркер получил идентификацию поколения (`product`, `release_line`,
`config_schema_version`), и обе команды проверяют её до всего остального.
- **Классификация отказа шла по тексту сообщения об ошибке.** Ошибка
preflight со словом `nftables` классифицировалась как отказ firewall и
приводила к откату чужого ruleset. Теперь классификация опирается на то, что
операция реально успела применить. `systemctl stop/disable` выполняется
только для юнитов, развёрнутых текущей операцией, а `fatal_pre_apply` по
определению не выполняет системный откат и не собирает diagnostics-бандл.
- **Diagnostics-бандл уносил machine token наружу.** Построчное правило
редакции `auth:` подставляло маркер в заголовок mapping'а и оставляло
нетронутым вложенный `auth.http.url` с `access_token=<секрет>` — тем самым,
что открывает и trafficStats API, и auth-endpoint. Редакция YAML переписана
структурно; в env-файлах секреты теперь вырезаются и из URL-значений
(`HY2_AUTH_URL` не подходил ни под один маркер имени).
- **`quic.maxIdleTimeout` не проверялся** семантической проверкой конфига, хотя
присутствовал в production-профиле. Заодно `auth.http.url` теперь сверяется
целиком (host/port/path/token), а не по наличию подстроки `access_token=`;
добавлены проверки `auth.http.insecure`, полей ACME и отсутствия посторонних
секций верхнего уровня.
- **Версия админки разъехалась с версией пакета**: пакет `1.0.0` сообщал
`HY2XS admin version v0.0.22`. Константа заменена переменной, которую
проставляет сборка через ldflags из `versions.env`.
- **Кнопки в панели, которые всегда возвращали ошибку.** «Перезапустить панель»
и загрузка сертификатов обращались к заглушкам. Маршруты и UI удалены.
### Добавлено
- **`versions.env`** — единственный источник истины для контракта
«продукт / платформа / toolchain»: версия продукта, линия релиза, схема
конфигурации, целевая платформа, версии и контрольные суммы Go/Bun/Node/pnpm,
политика выбора Hysteria. Прикладные зависимости и конкретная версия
Hysteria сюда намеренно не переносятся: у них есть собственные lock-механизмы.
- **Шаг сборки `verify_versions_contract`.** Роняет сборку до создания tarball,
если разошлись `PACKAGE_VERSION`, `packageManager` в двух `package.json`,
схема в `package/config/hy2xs.env`, константы, скомпилированные в
оркестратор, директива `go` в `apps/go.mod`, metadata пакета или версия,
которую сообщает собранный `hy2xs-admin`.
- **Контрольные суммы toolchain в контракте**, включая **обе** сборки Bun
(`bun-linux-x64` и `bun-linux-x64-baseline`): артефакт выбирается по наличию
AVX2, поэтому одной суммы архитектурно недостаточно. Передавать суммы через
окружение больше не нужно — production-сборка запускается одной командой.
- **Проверка происхождения артефакта Hysteria.** Ожидаемый SHA-256 берётся из
upstream-ассета `hashes.txt` и сверяется со скачанным бинарником до записи в
HY2XS lock. Раньше сумма считалась локально от уже скачанного файла, то есть
была trust-on-first-use.
- **Полный clean-host контракт.** Список маркеров чужой установки расширен с
двух до четырнадцати: состояние, runtime-пакет, конфиги, бинарник Hysteria,
фрагмент nftables, systemd-юниты, база админки и наследие `0.x`. Пути
установки и данных берутся из конфигурации, а не захардкожены.
- **`tools/legacy/purge-v0.sh`** и [docs/14-legacy-cleanup.md](docs/14-legacy-cleanup.md) —
явная очистка сервера от предыдущего поколения. По умолчанию скрипт
показывает план и ничего не делает; выполнение требует
`--apply --yes-i-know`. Из установщика он не вызывается никогда: это вернуло
бы destructive migration logic в путь свежей установки.
- **Явный флаг `--allow-partial-state` для `repair`.** Прежде согласие на
работу поверх незавершённой установки подразумевалось молча.
### Изменено
- **E2E подключается по ссылке из production-кода.** Внутри
`tools/test/e2e-hysteria.sh` жила вторая реализация `hysteria2://` URI на
bash: дрейф любой из двух реализаций оставлял обе группы тестов зелёными.
Теперь ссылку выдаёт `service.BuildHysteria2ShareURI` через
`apps/tools/share-uri`. Единственное расхождение — `insecure=1` для
самоподписанного сертификата, и оно ограничено тестами с двух сторон.
- **Формулировка гарантии санитайза экспорта.** Вместо «любой будущий секрет
будет удалён» — «известные секреты и неизвестные поля с секретоподобным
именем». Список маркеров расширен (`apiKey`, `privateKey`, `authorization`,
`cookie`, `bearer`, `passphrase`, `signature`, …) и синхронизирован между
Go-админкой и оркестратором.
### Удалено
- Маршруты, операциями которых продукт не владеет:
`POST /hysteria2ChangeVersion`, `GET /listRelease`,
`POST /config/updateHysteria2Config`, `POST /config/importHysteria2Config`,
`POST /config/restartServer`, `POST /config/uploadCertFile`,
`GET /config/hysteria2AcmePath`. Вместе с ними — соответствующие сервисы,
клиентские функции фронтенда, кнопки и строки i18n.
Маршруты удалены, а не оставлены отвечающими «feature disabled»: API-контракт
не должен обещать updater, которого у продукта нет, а неиспользуемый маршрут
остаётся attack surface.
## [1.0.0] — 2026-08-27
Первый релиз линейки `v1`.
> **Обновление с `0.x` не поддерживается.**
> Между `0.x` и `1.0.0` изменились схема конфигурации, тип обфускации
> по умолчанию и контракт выбора версии Hysteria. Сервер, установленный
> из пакета `0.x`, нужно поднимать заново: очистка и установка с нуля.
> Подробности — в разделе «Миграция с 0.x» ниже.
### Добавлено
- **Разрешение версии Hysteria на этапе сборки.** Builder по умолчанию сам
определяет последний стабильный upstream-релиз, скачивает артефакт,
вычисляет SHA-256 и замораживает `version` + `url` + `sha256` в metadata
пакета. Target-сервер по-прежнему скачивает конкретный неизменяемый
артефакт и никогда не обращается к moving `latest`.
- **Compatibility gate в сборке.** До создания release-пакета builder
рендерит канонический конфиг HY2XS тем же кодом, что и оркестратор, и
запускает с ним реальный бинарник Hysteria — для обоих профилей
обфускации. Несовместимый upstream ломает сборку, а не сервер оператора.
- **Поддержка Gecko-обфускации** (Hysteria 2.9.2+) со сквозной интеграцией:
оркестратор, шаблон конфига, модель админки, генерация `hysteria2://` URI,
типы и формы фронтенда.
- **Версия схемы конфигурации** `HY2XS_CONFIG_SCHEMA_VERSION=2`. Пакет
отказывается работать с конфигурацией неизвестной схемы вместо того,
чтобы молча применить чужие значения.
- **Современный серверный baseline** в генерируемом конфиге:
`congestion.type: bbr` + `bbrProfile: standard`,
`bandwidth.disableLossCompensation: false`,
`quic.disableStatelessReset: false`, а также
`maxIdleTimeout`, `maxIncomingStreams`, `disablePathMTUDiscovery`.
- **Модель современной схемы Hysteria в админке**: `obfs.gecko`, `ech`,
`congestion`, `mimic`, `realm`, `tls.clientCA`,
`quic.disableStatelessReset`, `bandwidth.disableLossCompensation`,
`masquerade.proxy.xForwarded`. Поля читаются и отображаются, даже если
HY2XS не включает их в default-профиль.
- **Тесты оркестратора** (`bun test`): разбор env, рендер конфига,
семантические инварианты профиля, резолвер upstream-релизов,
release rollover.
- **E2E-проверка с реальным клиентом Hysteria** — `tools/test/e2e-hysteria.sh`:
TLS и obfs handshake, HTTP auth (допуск и отказ), TCP и UDP forwarding,
trafficStats, per-peer accounting, переподключение после перезапуска
сервера и подключение клиента именно по сгенерированной ссылке.
- **`CHANGELOG.md`** в корне репозитория.
### Изменено
- **Обфускация по умолчанию для новых установок — Gecko.** Salamander
остаётся полностью поддержанным режимом совместимости и выбирается через
`HY2XS_HYSTERIA_OBFS_TYPE=salamander`.
- **Gecko использует upstream-defaults `512/1200`** и не выносит размеры
пакетов в env: официальная URI-схема не умеет их передавать, поэтому
нестандартные значения сделали бы клиентскую ссылку неполной.
- **Тип обфускации больше не собирается внутри статического YAML.**
Оркестратор формирует проверенный `obfs`-блок целиком, поэтому
комбинация вида `type: gecko` рядом с блоком `salamander` структурно
невозможна.
- **Экспорт конфига Hysteria работает от исходного YAML**, а не от
типизированной модели: поля, о которых HY2XS ещё не знает, переживают
выгрузку.
- **Smoke-проверки разбирают YAML** и сверяют его с production-профилем,
вместо поиска подстрок.
- **Канонический upstream-репозиторий** — `HyNetworks/hysteria`
(вместо устаревших ссылок на `apernet`).
- **Реестр ACME DNS-провайдеров во фронтенде** приведён к актуальному
upstream: добавлены `namecheap`, `njalla`, `porkbun`.
- **Дефолты формы Hysteria во фронтенде** отражают baseline HY2XS
(50/50 Mbps, Let's Encrypt, каталог ACME), а не пример из upstream-доки.
- Сборка запускает тесты оркестратора и админки до упаковки.
### Исправлено
- **`hysteria2://` для Gecko.** Генератор ссылок был завязан на
`Obfs.Salamander.Password`, поэтому при любой другой обфускации выдавал
формально корректную, но неработающую ссылку без параметров `obfs`.
- **SNI в клиентской ссылке при файловых сертификатах.** SNI брался только
из ACME-блока, поэтому при `HY2XS_TLS_MODE=file` уходил пустым. Теперь
источник — ACME-домен, затем `HY2XS_DOMAIN`, затем `HY2XS_PUBLIC_HOST`;
IP-адрес в качестве SNI не используется.
- **Утечка секретов в экспорте конфига.** Выгружаемый оператору YAML
содержал `trafficStats.secret`, `access_token` в auth-URL и пароль
обфускации. Секреты вырезаются, включая поля, о которых HY2XS ещё
не знает.
- **Расхождение runtime-конфига с разобранным.** `renderRuntimeEnv`
печатал тип обфускации и режим auth литералами, игнорируя фактическую
конфигурацию, — из-за чего запись `/etc/hy2xs/hy2xs.env` могла разойтись
с тем, что реально применено.
- **Смешение веток `obfs` в UI.** Форма склеивала дефолт с ответом API и
показывала блок обфускации, которого нет в конфиге сервера.
- **`namedotcom` в списке ACME DNS-провайдеров.** Провайдер удалён из
Hysteria в 2.11.0; конфигурация с ним больше не запускается.
- **Отсутствие подсказок в форме создания пира.** У полей «Пир»,
«Комментарий» и «Секрет» не было ни примеров, ни пояснений: оператор не
мог понять без документации, что секрет необязателен и генерируется
автоматически.
### Безопасность
- Переход на Hysteria 2.12.2 закрывает исправления, вышедшие после 2.8.2,
включая обход UDP ACL, возможный OOM через sniff и обход ACL через
домены с завершающей точкой (2.9.2).
- Экспорт конфига больше не выносит секреты за пределы сервера.
### Миграция с 0.x
Автоматическая миграция не предусмотрена и не планируется.
Порядок перехода:
1. Выпишите с работающего сервера список пиров и их секреты.
2. Очистите сервер: `tools/legacy/purge-v0.sh` или ручная процедура из
[docs/14-legacy-cleanup.md](docs/14-legacy-cleanup.md).
3. Разверните `1.0.0` на чистом Debian 13 из release-пакета.
4. Заведите пиров заново и раздайте новые клиентские ссылки.
Установщик `1.0.0` обнаружит остатки предыдущей установки на шаге PHASE 0,
откажется работать и **не изменит на сервере ничего**.
Клиентские ссылки `0.x` в любом случае перестанут работать: смена
обфускации — это изменение wire-совместимости.
[Unreleased]: https://git.flamy.studio/prod/HY2XS_flamy/compare/v1.0.0...HEAD
[1.0.0]: https://git.flamy.studio/prod/HY2XS_flamy/releases/tag/v1.0.0