# HY2XS Core
HY2XS — production‑установщик Hysteria2‑сервера с локальной HY2XS admin‑панелью, systemd‑юнитами, nftables‑firewall и воспроизводимой моделью release‑пакета для чистого Debian 13.
Что это ·
Возможности ·
Быстрый старт ·
Конфигурация ·
Версии ·
Сборка ·
Changelog ·
Лицензия
---
## Что это
HY2XS — это install‑only пакет для развёртывания Hysteria2‑узла на чистом Debian 13 amd64. Проект разделяет сборку и установку: разработчик один раз собирает release‑архив на Debian 13 build‑хосте, а целевой сервер получает уже готовый пакет и запускает `install.sh` без `git clone`, `go build`, `bun install`, `pnpm install` и сборки frontend‑ассетов.
HY2XS подходит для сценария, где нужен один production‑сервер с Hysteria2, локальной админ‑панелью, управляемым firewall и предсказуемой конфигурацией. Проект не является универсальным server manager, update manager или панелью для любых Linux‑дистрибутивов.
Ключевая идея: на target‑сервере выполняется только установка готового пакета. Вся тяжёлая сборочная часть остаётся на build‑машине.
## Возможности
HY2XS release‑пакет разворачивает и настраивает:
- официальный upstream‑бинарник Hysteria2: последняя стабильная версия выбирается при сборке пакета, закрепляется в его metadata и проверяется по SHA256;
- HY2XS admin — встроенную админ‑панель для управления users/peers, трафиком, конфигурацией, логами и состоянием сервера;
- systemd‑юнит `hysteria-server` для Hysteria2;
- systemd‑юнит `hy2xs-admin` для админ‑панели;
- runtime‑конфиг `/etc/hy2xs/hy2xs.env`;
- Hysteria2‑конфиг `/etc/hysteria/config.yaml`;
- post‑install snapshot `/etc/hysteria/post-install.env`;
- bootstrap‑секреты администратора в `/etc/hy2xs/bootstrap-admin.secret`;
- nftables‑правила с staged apply и rollback guard;
- smoke‑проверки после установки;
- команды диагностики, статуса, реконфигурации и сбора support‑bundle.
HY2XS admin запускается локально по умолчанию на `127.0.0.1:8080`. Наружу публикуется Hysteria2‑транспорт, а доступ к панели выполняется через SSH local port forwarding.
## Архитектура
```mermaid
graph LR
operator[Оператор на Windows/Linux/macOS] -->|SSH 2323/tcp| ssh[sshd на target]
operator -->|SSH tunnel 127.0.0.1:8080| ui[HY2XS admin 127.0.0.1:8080]
internet[Интернет] -->|443/udp| hy2[Hysteria2 server]
letsencrypt[Let's Encrypt] -->|80/tcp при ACME http| acme[ACME challenge]
hy2 -->|HTTP auth localhost + machine token| ui
ui -->|trafficStats localhost| hy2
hy2 --> cfg["/etc/hysteria/config.yaml"]
ui --> env["/etc/hy2xs/hy2xs.env"]
fw[nftables] --> ssh
fw --> hy2
fw --> acme
```
В production‑профиле проект работает как IPv4‑only stack. IPv6 намеренно не входит в текущий baseline. Если у домена есть AAAA‑запись и включён строгий режим `HY2XS_DNS_AAAA_POLICY=strict`, установка останавливается до применения конфигурации.
## Поддерживаемые платформы
### Target‑сервер
| Компонент | Поддерживается |
| --- | --- |
| ОС | Debian 13 |
| Архитектура | amd64 / x86_64 |
| Init system | systemd |
| Firewall | nftables |
| Сетевой профиль | IPv4‑only |
| Hysteria2 target | linux‑amd64 |
| Admin UI | локальный bind по умолчанию: `127.0.0.1:8080` |
### Build‑машина
| Компонент | Поддерживается |
| --- | --- |
| ОС | Debian 13 |
| Архитектура | amd64 / x86_64 |
| Git tree | обязателен |
| Dirty tree | запрещён по умолчанию |
| Интернет | нужен для apt, Go, Bun, Node.js, pnpm/npm registry, GitHub и upstream Hysteria2 |
Windows и macOS можно использовать для разработки, редактирования кода и работы с GitHub, но production‑архив должен собираться на Debian 13 amd64.
## Что HY2XS не делает
Это важные границы проекта:
- не собирает проект на target‑сервере;
- не поддерживает Debian 12, Ubuntu, CentOS, Alpine и другие ОС;
- не поддерживает arm64 в текущем release‑профиле;
- не включает IPv6‑production baseline;
- не настраивает `sshd` автоматически;
- не предоставляет полноценный uninstall/update framework;
- не обновляет Hysteria2 на уже работающем сервере: `reconfigure` намеренно не является Hysteria updater;
- не мигрирует установки `0.x` на `1.0.0` — переход выполняется чистой установкой, см. [CHANGELOG](CHANGELOG.md);
- не выполняет сложную миграцию старых неизвестных состояний сервера;
- не реализует Telegram‑бота, port hopping и универсальный access‑delivery workflow;
- не предназначен для установки поверх давно используемого сервера с неизвестными firewall/systemd‑правками.
## Release‑пакет
Production‑сборка создаёт один архив:
```text
hy2xs-install-.tar.gz
```
Внутри архива находятся:
```text
hy2xs-install/
├── install.sh
├── orchestrator/hy2xs-orchestrator
├── ui/hy2xs-admin/hy2xs-admin
├── config/hy2xs.env
├── templates/
├── systemd/
├── docs/
└── metadata/
```
При запуске `install.sh` пакет сначала проверяет `metadata/checksums.txt` и
выполняет read‑only clean‑host preflight **из распакованного архива**. Только
после этого он устанавливает orchestrator в
`/usr/local/lib/hy2xs/hy2xs-orchestrator`, создаёт symlink
`/usr/local/bin/hy2xs-orchestrator`, копирует package assets в
`/usr/local/lib/hy2xs/package` и передаёт управление install‑only orchestrator.
## Сетевая модель по умолчанию
| Назначение | Значение по умолчанию |
| --- | --- |
| SSH оператора | `2323/tcp` |
| Hysteria2 | `443/udp` |
| ACME HTTP challenge | `80/tcp`, если `HY2XS_TLS_MODE=acme` и `HY2XS_ACME_TYPE=http` |
| HY2XS admin | `127.0.0.1:8080` |
| TrafficStats Hysteria2 | `127.0.0.1:36712` |
| Firewall mode | `takeover` в packaged baseline |
| Hysteria2 auth | `http` через локальный HY2XS admin |
| Hysteria2 obfs | `gecko` (512/1200); `salamander` доступен как режим совместимости |
| Congestion fallback | `bbr`, профиль `standard` |
| QUIC stateless reset | включён |
Важно: `HY2XS_SSH_PORT` нужен HY2XS для nftables‑правил и проверки доступности SSH‑порта. Сам `sshd` проект не перенастраивает. SSH на `2323` и вход только по ключу нужно настроить до запуска `./install.sh`.
## Версионная политика Hysteria2
HY2XS **не привязан к конкретному номеру версии Hysteria**.
> Источник по умолчанию берёт последний стабильный релиз Hysteria, доступный на момент сборки пакета. Разрешённая версия, URL артефакта и контрольная сумма замораживаются в получившемся install‑пакете.
Как это работает:
```text
build machine target server
───────────── ─────────────
определить последнюю стабильную ─┐
взять ожидаемый SHA-256 из │
upstream hashes.txt │
скачать артефакт и сверить его ├─► release‑пакет ──► скачать ровно
проверить, что бинарник принимает │ version + url этот артефакт,
канонический конфиг HY2XS │ + sha256 сверить SHA-256
заморозить version/url/sha256 ─┘ и `hysteria version`
```
Контрольная сумма берётся из upstream‑ассета `hashes.txt`, а не считается
только локально: локальный пересчёт подтверждает, что файл не изменился после
скачивания, но не доказывает, что скачан именно ожидаемый upstream artifact.
Что это даёт:
- новая установка получает актуальную Hysteria без ручного обновления version lock;
- если между сборкой пакета и его установкой выйдет новая версия, **содержимое установки не изменится**;
- повторная установка старого пакета поставит ту же версию, что и в день сборки;
- несовместимый upstream ломает сборку, а не сервер оператора.
Переопределения при сборке:
```bash
# по умолчанию: последняя стабильная
./tools/build/build.sh
# закрепить конкретную версию
HYSTERIA_VERSION_OVERRIDE=v2.12.2 ./tools/build/build.sh
# офлайн-сборка по закоммиченному tools/build/hysteria-lock.env
HYSTERIA_CHANNEL=pinned ./tools/build/build.sh
```
Фактически установленная версия видна в `/etc/hysteria/post-install.env` (`HY2_VERSION`), а способ её выбора — в `HY2_RESOLUTION`.
Обновление Hysteria на уже работающем сервере в текущем релизе не поддерживается: `reconfigure` намеренно не является Hysteria updater. Это сохраняет immutable‑контракт развёртывания.
## Обфускация
Новые установки HY2XS используют **Gecko**.
Gecko помечен upstream как **experimental**. Он достраивается поверх Salamander: помимо scramble он дополнительно фрагментирует QUIC handshake на пакеты случайного размера. HY2XS использует upstream‑defaults размеров пакетов `512/1200` как проверенный production‑профиль.
**Salamander остаётся поддержанным режимом совместимости.** Смена типа обфускации требует соответствующих изменений на клиенте: это изменение wire‑совместимости, а не косметическая настройка.
| | Gecko | Salamander |
| --- | --- | --- |
| Статус upstream | experimental | stable |
| Роль в HY2XS | default для новых установок | режим совместимости |
| Параметр | `HY2XS_HYSTERIA_OBFS_TYPE=gecko` | `HY2XS_HYSTERIA_OBFS_TYPE=salamander` |
| В клиентской ссылке | `obfs=gecko` | `obfs=salamander` |
Экспериментальность upstream остаётся контролируемым риском, потому что одновременно выполняются три условия: Salamander доступен как fallback, каждая разрешённая версия проходит compatibility gate до выпуска пакета, и существующие серверы никогда не переводятся на Gecko молча.
Размеры пакетов Gecko не выносятся в конфигурацию: официальная схема `hysteria2://` не умеет их передавать, поэтому нестандартные значения сделали бы клиентскую ссылку неполной.
## Быстрый старт для нового сервера
Ниже приведён полный путь для оператора, который работает с Windows и ставит HY2XS на чистый Debian 13 сервер.
В примерах используются условные значения:
| Параметр | Пример |
| --- | --- |
| IP сервера | `SERVER_IP` |
| Домен сервера | `vpn.example.com` |
| Email для ACME | `admin@example.com` |
| Release‑архив | `hy2xs-install-0.2.2.tar.gz` |
| SSH‑ключ | `id_ed25519_hy2xs` |
Замените эти значения на свои.
### 1. Проверьте DNS и внешние порты
До установки домен должен указывать A‑записью на IPv4‑адрес target‑сервера:
```text
vpn.example.com -> SERVER_IP
```
Для стандартной установки с `HY2XS_TLS_MODE=acme` и `HY2XS_ACME_TYPE=http` снаружи должны быть доступны:
| Порт | Протокол | Назначение |
| --- | --- | --- |
| `2323` | TCP | SSH оператора |
| `80` | TCP | ACME HTTP challenge |
| `443` | UDP | Hysteria2 |
Если у домена есть AAAA‑запись, при строгой политике `HY2XS_DNS_AAAA_POLICY=strict` установка будет остановлена, потому что текущий production‑профиль HY2XS является IPv4‑only.
A‑запись должна указывать именно на этот сервер, а не просто существовать. Preflight сверяет её с публичными IPv4, назначенными интерфейсам машины, и останавливает установку при расхождении:
```text
DNS IPv4 mismatch for HY2XS_PUBLIC_HOST vpn.example.com:
DNS A records: 185.xxx.xxx.10
server public IPv4: 185.xxx.xxx.27
Update the DNS A record before using this server.
```
Та же проверка выполняется в `reconfigure` и `doctor`, поэтому принудительная смена IPv4 провайдером не остаётся незамеченной. Адрес сервера определяется локально, без обращения к внешним сервисам определения IP.
Если сервер работает за NAT или на floating IP — это топология вне текущего baseline; осознанное решение оформляется значением `HY2XS_PUBLIC_ENDPOINT_POLICY=warn`.
### 2. Создайте SSH‑ключ на Windows
Откройте PowerShell:
```powershell
ssh-keygen -t ed25519 -a 100 -f "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" -C "hy2xs-target-1"
```
Выведите публичный ключ:
```powershell
type "$env:USERPROFILE\.ssh\id_ed25519_hy2xs.pub"
```
Скопируйте всю строку вида:
```text
ssh-ed25519 AAAA... hy2xs-target-1
```
Именно её нужно добавить на сервер в `/root/.ssh/authorized_keys`.
### 3. Войдите на сервер по паролю провайдера
Первый вход обычно выполняется по временному root‑паролю от провайдера:
```powershell
ssh root@SERVER_IP
```
На сервере установите базовые пакеты:
```bash
apt-get update
apt-get install -y openssh-server sudo ca-certificates curl tar
```
### 4. Добавьте публичный ключ на сервер
На сервере замените пример ключа на свой реальный публичный ключ:
```bash
install -d -m 700 -o root -g root /root/.ssh
cat > /root/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAA_REPLACE_WITH_YOUR_PUBLIC_KEY hy2xs-target-1
EOF
chmod 600 /root/.ssh/authorized_keys
chown -R root:root /root/.ssh
```
### 5. Переведите SSH на порт 2323 и отключите парольный вход
Не закрывайте текущую SSH‑сессию, пока не проверите новый вход по ключу.
Сначала посмотрите, какие SSH‑директивы уже заданы провайдером:
```bash
grep -RniE '^[[:space:]]*(Include|Port|PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|ChallengeResponseAuthentication|PubkeyAuthentication|AllowTcpForwarding|PermitOpen|GatewayPorts|Match)\b' \
/etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true
```
OpenSSH для многих параметров использует первое найденное значение. Поэтому поздний файл вида `99-*.conf` может не перекрыть ранний provider‑конфиг. Безопаснее сначала закомментировать конфликтующие директивы, затем создать ранний `00-hy2xs-operator.conf`.
```bash
for f in /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf; do
[ -f "$f" ] || continue
cp -a "$f" "$f.bak.$(date +%Y%m%d-%H%M%S)"
sed -i -E 's/^[[:space:]]*(Port|PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|ChallengeResponseAuthentication|PubkeyAuthentication|AllowTcpForwarding|PermitOpen|GatewayPorts|X11Forwarding|AllowAgentForwarding|MaxAuthTries|LoginGraceTime|ClientAliveInterval|ClientAliveCountMax)[[:space:]]+/# disabled-by-hy2xs-hardening: &/' "$f"
done
cat > /etc/ssh/sshd_config.d/00-hy2xs-operator.conf <<'EOF'
Port 2323
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication 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
EOF
sshd -t
systemctl reload ssh || systemctl restart ssh
```
Проверьте effective‑конфиг:
```bash
sshd -T | grep -E '^(port|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin|allowtcpforwarding|permitopen|gatewayports) '
ss -H -ltn | grep -E ':(22|2323) '
```
Ожидаемо:
```text
port 2323
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
gatewayports no
allowtcpforwarding local
permitopen 127.0.0.1:8080 localhost:8080
```
В списке listen‑портов должен остаться `2323`, а `22` не должен слушаться.
### 6. Проверьте вход по ключу
В новом PowerShell‑окне:
```powershell
ssh -p 2323 -i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" root@SERVER_IP
```
Проверьте, что парольный вход запрещён:
```powershell
ssh -p 2323 -o PreferredAuthentications=password -o PubkeyAuthentication=no root@SERVER_IP
```
Правильный результат:
```text
Permission denied (publickey).
```
После этого можно дополнительно заблокировать пароль root на уровне Linux‑аккаунта:
```bash
passwd -l root
passwd -S root
```
Это не ломает вход по SSH‑ключу, но защищает от случайного возврата парольной SSH‑аутентификации в будущем.
### 7. Скопируйте release‑архив на сервер
С Windows:
```powershell
scp -P 2323 -i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" .\hy2xs-install-0.2.2.tar.gz root@SERVER_IP:/root/
```
На сервере:
```bash
cd /root
tar -xzf hy2xs-install-0.2.2.tar.gz
cd /root/hy2xs-install
```
### 8. Создайте конфиг для своего сервера
Не редактируйте исходный шаблон внутри пакета без необходимости. Создайте отдельный source‑config:
```bash
cp config/hy2xs.env /root/hy2xs-target.env
nano /root/hy2xs-target.env
```
Минимально проверьте и измените:
```env
HY2XS_DOMAIN=vpn.example.com
HY2XS_PUBLIC_HOST=vpn.example.com
HY2XS_ACME_EMAIL=admin@example.com
HY2XS_SSH_PORT=2323
HY2XS_UI_BIND_HOST=127.0.0.1
HY2XS_UI_PORT=8080
HY2XS_HYSTERIA_PORT=443
HY2XS_FIREWALL_MODE=takeover
HY2XS_FIREWALL_STAGED_APPLY=true
```
Для стандартной production‑установки оставьте:
```env
HY2XS_IPV6_ENABLED=false
HY2XS_TLS_MODE=acme
HY2XS_ACME_TYPE=http
HY2XS_HYSTERIA_AUTH_MODE=http
HY2XS_HYSTERIA_OBFS_TYPE=gecko
HY2XS_UI_PUBLIC_ACCESS=false
```
Если нужен режим совместимости со старыми клиентами, укажите `HY2XS_HYSTERIA_OBFS_TYPE=salamander`. Подробнее — в разделе [Обфускация](#обфускация).
### 9. Запустите установку
```bash
./install.sh --config /root/hy2xs-target.env --non-interactive
```
Установка идёт в две фазы с жёсткой границей между ними.
**PHASE 0 — только чтение.** До её успешного завершения на сервере не
изменяется ни один файл, включая `/usr/local/lib/hy2xs`:
1. проверит, что запущено от root;
2. проверит checksums release‑пакета;
3. запустит clean‑host preflight **из распакованного архива**: платформа
Debian 13 amd64, отсутствие предыдущей установки, валидность конфигурации.
**PHASE 1 — применение изменений:**
4. установит orchestrator в `/usr/local/lib/hy2xs` и разложит runtime‑пакет;
5. создаст runtime‑каталоги и service users;
6. запишет `/etc/hy2xs/hy2xs.env`;
7. разложит bundled HY2XS admin;
8. скачает закреплённый в пакете Hysteria2 binary из upstream, проверит SHA256 и фактическую версию;
9. создаст `/etc/hysteria/config.yaml`;
10. установит systemd‑юниты;
11. применит nftables‑правила;
12. выполнит smoke‑checks;
13. зафиксирует успешное состояние в `/var/lib/hy2xs/install-state.json`.
Если PHASE 0 не прошла, установщик завершается с ошибкой и **сервер остаётся в
том же состоянии, в котором был**. HY2XS v1 не устанавливается поверх
предыдущего поколения и не мигрирует его состояние: очистка старой установки —
отдельная явная операция, см. [docs/14-legacy-cleanup.md](docs/14-legacy-cleanup.md).
### 10. Получите bootstrap‑пароль админки
```bash
cat /etc/hy2xs/bootstrap-admin.secret
```
Файл содержит:
```env
ADMIN_USER=...
ADMIN_INITIAL_PASSWORD=...
ADMIN_CON_PASS=...
```
`ADMIN_INITIAL_PASSWORD` нужен для первого входа в HY2XS admin. После первого входа смените пароль через интерфейс.
`ADMIN_CON_PASS` — bootstrap‑секрет Hysteria2 peer/auth слоя. Не публикуйте этот файл и не отправляйте его в issue/log без редактирования.
### 11. Откройте HY2XS admin через SSH‑туннель
На локальной машине:
```powershell
ssh -p 2323 `
-i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" `
-N `
-L 127.0.0.1:8080:127.0.0.1:8080 `
root@SERVER_IP
```
Пример для Linux/macOS (одной строкой):
```bash
ssh -p 2323 -i ~/.ssh/id_ed25519_hy2xs -N -L 8080:127.0.0.1:8080 root@SERVER_IP
```
После этого откройте в браузере:
```text
http://127.0.0.1:8080/
```
Если порт `8080` на локальной машине занят, можно пробросить другой локальный порт:
```powershell
ssh -p 2323 `
-i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" `
-N `
-L 127.0.0.1:18080:127.0.0.1:8080 `
root@SERVER_IP
```
И открыть:
```text
http://127.0.0.1:18080/
```
### 12. Проверьте установку
На сервере:
```bash
systemctl status hysteria-server --no-pager
systemctl status hy2xs-admin --no-pager
ss -H -lun | grep ':443'
ss -H -ltn | grep ':8080'
ss -H -ltn | grep ':2323'
nft list ruleset
cat /var/lib/hy2xs/install-state.json
```
Выполните doctor‑проверку:
```bash
hy2xs-orchestrator doctor \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
```
Проверьте сводный статус:
```bash
hy2xs-orchestrator status \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
```
## Конфигурация `hy2xs.env`
После установки основным редактируемым файлом является:
```text
/etc/hy2xs/hy2xs.env
```
Менять runtime‑параметры нужно через этот файл и затем применять `reconfigure`. Редактирование `post-install.env` не меняет runtime‑состояние.
### Основные параметры
| Переменная | Назначение | Значение по умолчанию в packaged baseline |
| --- | --- | --- |
| `HY2XS_CONFIG_SCHEMA_VERSION` | Версия схемы конфигурации HY2XS. Конфигурация другой схемы отклоняется fail‑fast | `2` |
| `HY2XS_IPV6_ENABLED` | IPv6‑режим. В production baseline должен быть `false` | `false` |
| `HY2XS_DOMAIN` | Домен для ACME и deploy‑профиля | `fi.api.withen.pro` |
| `HY2XS_DNS_AAAA_POLICY` | Поведение при наличии AAAA‑записи: `strict`, `warn`, `off` | `strict` |
| `HY2XS_PUBLIC_ENDPOINT_POLICY` | Строгость проверки того, что A‑записи публичного endpoint ведут на IPv4 этого сервера: `strict`, `warn`, `off` | `strict` |
| `HY2XS_PUBLIC_HOST` | Публичный host без схемы, порта и path | `fi.api.withen.pro` |
| `HY2XS_PUBLIC_PORT` | Публичный порт Hysteria2 endpoint | `443` |
| `HY2XS_SSH_PORT` | SSH‑порт, который будет разрешён firewall‑правилами | `2323` |
| `HY2XS_FIREWALL_MODE` | Режим управления nftables: `managed`, `takeover`, `external`, `off` | `takeover` |
| `HY2XS_FIREWALL_STAGED_APPLY` | Включает staged apply и rollback guard firewall | `true` |
| `HY2XS_UI_BIND_HOST` | IPv4 bind HY2XS admin | `127.0.0.1` |
| `HY2XS_UI_PUBLIC_ACCESS` | Флаг публичного UI‑доступа. В baseline оставляйте `false` | `false` |
| `HY2XS_UI_PORT` | Порт HY2XS admin | `8080` |
| `HY2XS_ADMIN_USER` | Bootstrap‑логин администратора | `hy2xsadmin` |
| `HY2XS_ADMIN_INITIAL_PASSWORD` | Bootstrap‑пароль администратора; `__GENERATE__` генерируется при install | `__GENERATE__` |
| `HY2XS_ADMIN_CON_PASS` | Bootstrap‑секрет peer/auth слоя; `__GENERATE__` генерируется при install | `__GENERATE__` |
| `HY2XS_FORCE_PASSWORD_CHANGE` | Принудительная смена пароля. UX‑flow пока не включён в production baseline | `false` |
| `HY2XS_ALLOW_SELF_SIGNED_DEV` | Разрешает `self_signed_dev` TLS‑режим | `false` |
| `HY2XS_TLS_MODE` | TLS‑режим: `acme`, `file`, `self_signed_dev` | `acme` |
| `HY2XS_ACME_TYPE` | ACME challenge: `http`, `tls`, `dns`; `dns` пока не поддержан production‑профилем | `http` |
| `HY2XS_ACME_EMAIL` | Email для ACME | `admin@withen.pro` |
| `HY2XS_TLS_CERT_PATH` | Путь к cert при `file`/`self_signed_dev` | `/etc/hysteria/server.crt` |
| `HY2XS_TLS_KEY_PATH` | Путь к key при `file`/`self_signed_dev` | `/etc/hysteria/server.key` |
| `HY2XS_HYSTERIA_BIND_HOST` | Bind Hysteria2. В production profile фиксируется на `0.0.0.0` | `0.0.0.0` |
| `HY2XS_HYSTERIA_PORT` | UDP‑порт Hysteria2 | `443` |
| `HY2XS_HYSTERIA_AUTH_MODE` | Auth mode Hysteria2. Фиксированное значение production‑профиля | `http` |
| `HY2XS_HYSTERIA_TRAFFIC_STATS_HOST` | Host trafficStats API | `127.0.0.1` |
| `HY2XS_HYSTERIA_TRAFFIC_STATS_PORT` | Порт trafficStats API | `36712` |
| `HY2XS_HYSTERIA_TRAFFIC_STATS_SECRET` | Secret для trafficStats и machine auth | `__GENERATE__` |
| `HY2XS_HYSTERIA_OBFS_TYPE` | Тип обфускации: `gecko` или `salamander`. Смена меняет wire‑совместимость | `gecko` |
| `HY2XS_HYSTERIA_OBFS_PASSWORD` | Пароль обфускации; `__GENERATE__` генерируется при install | `__GENERATE__` |
| `HY2XS_HYSTERIA_BANDWIDTH_UP` | Hysteria2 upstream bandwidth | `50 mbps` |
| `HY2XS_HYSTERIA_BANDWIDTH_DOWN` | Hysteria2 downstream bandwidth | `50 mbps` |
| `HY2XS_HYSTERIA_IGNORE_CLIENT_BANDWIDTH` | Игнорировать bandwidth клиента | `false` |
| `HY2XS_HYSTERIA_CONFIG_PATH` | Путь Hysteria2 config. В production baseline фиксирован | `/etc/hysteria/config.yaml` |
| `HY2XS_INSTALL_DIR` | Каталог установки HY2XS admin | `/opt/hy2xs-admin` |
| `HY2XS_DATA_DIR` | Data‑каталог HY2XS admin | `/var/lib/hy2xs-admin` |
| `HY2XS_LOG_DIR` | Log‑каталог HY2XS | `/var/log/hy2xs` |
### Режимы firewall
| Режим | Поведение |
| --- | --- |
| `managed` | HY2XS управляет nftables, но останавливается при обнаружении чужого `/etc/nftables.conf` |
| `takeover` | HY2XS явно берёт управление nftables на себя |
| `external` | HY2XS не меняет nftables; оператор сам отвечает за firewall |
| `off` | Firewall‑слой HY2XS отключён |
При `managed` и `takeover` генерируется nftables‑конфигурация с default drop policy, разрешением loopback, established/related, SSH‑порта, ACME challenge‑порта, Hysteria2 UDP‑порта и ICMP echo‑request.
## Реконфигурация
После изменения `/etc/hy2xs/hy2xs.env` сначала выполните dry‑run:
```bash
hy2xs-orchestrator reconfigure \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env \
--dry-run
```
Если ошибок нет, примените изменения:
```bash
hy2xs-orchestrator reconfigure \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env \
--apply
```
`reconfigure --apply` выполняет backup, генерирует конфиги, обновляет systemd‑юниты, применяет firewall, выполняет smoke‑checks и при ошибке пытается откатить изменённое состояние.
Важно: `HY2XS_ADMIN_INITIAL_PASSWORD` и `HY2XS_ADMIN_CON_PASS` являются install‑only bootstrap‑полями. Их изменение в `/etc/hy2xs/hy2xs.env` после первичной установки не ротирует уже созданные credentials в SQLite автоматически.
## Команды управления
| Команда | Назначение |
| --- | --- |
| `hy2xs-orchestrator preflight-install` | Read‑only проверка чистоты хоста; ничего не меняет |
| `hy2xs-orchestrator status` | Показать состояние платформы, сервисов, firewall и install marker |
| `hy2xs-orchestrator doctor` | Выполнить preflight и smoke‑checks текущей установки |
| `hy2xs-orchestrator reconfigure --dry-run` | Проверить конфиг без применения |
| `hy2xs-orchestrator reconfigure --apply` | Применить runtime‑конфигурацию |
| `hy2xs-orchestrator repair --allow-partial-state` | Довести до конца незавершённую установку **текущего поколения** |
| `hy2xs-orchestrator diagnostics collect` | Собрать diagnostic bundle в `/var/log/hy2xs/diagnostics` |
| `hy2xs-orchestrator redact-config` | Отредактировать секреты в env/yaml перед публикацией логов |
`repair` без `--allow-partial-state` работает только поверх полностью успешной
установки. В обоих режимах он сначала проверяет, что
`/var/lib/hy2xs/install-state.json` принадлежит текущему поколению продукта
(`product`, `release_line`, `config_schema_version`), и отказывается работать
поверх чужого состояния.
Пример сбора диагностики:
```bash
hy2xs-orchestrator diagnostics collect \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
```
## Проверка безопасности после установки
Минимальный набор проверок:
```bash
# SSH слушает только ожидаемый порт
ss -H -ltn | grep -E ':(22|2323) '
# password auth отключён
sshd -T | grep -E '^(port|permitrootlogin|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|allowtcpforwarding|permitopen) '
# UI не слушает публичный 0.0.0.0
ss -H -ltn | grep ':8080'
# Hysteria2 слушает UDP-порт
ss -H -lun | grep ':443'
# nftables-конфиг валиден
nft -c -f /etc/nftables.conf
# сервисы активны
systemctl is-active hysteria-server hy2xs-admin
```
Ожидаемые SSH‑значения:
```text
port 2323
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
allowtcpforwarding local
permitopen 127.0.0.1:8080 localhost:8080
```
## Troubleshooting
### Установка отказывается: обнаружена предыдущая установка
```text
[hy2xs] ERROR: На сервере обнаружена предыдущая или посторонняя установка.
HY2XS v1 не поддерживает установку поверх и не мигрирует состояние 0.x.
Ни один файл на сервере не изменён.
```
Это ожидаемое поведение, а не сбой. Отказ происходит в PHASE 0, до любой
мутации: сервер остался в том состоянии, в котором был.
Что делать:
1. сохраните нужные данные (база пиров, конфиг) — см.
[docs/14-legacy-cleanup.md](docs/14-legacy-cleanup.md);
2. посмотрите план очистки: `sudo ./purge-v0.sh`;
3. выполните очистку: `sudo ./purge-v0.sh --apply --yes-i-know`;
4. повторите установку.
Отдельный случай — отказ вида
`HY2XS_CONFIG_SCHEMA_VERSION отсутствует в конфигурации`. Он означает, что
переданный `--config` относится к предыдущему поколению: до v1 этого поля не
существовало. Создайте конфиг заново по разделу «Создайте конфиг для своего
сервера».
Если установка HY2XS v1 упала **после** начала применения изменений, полная
очистка не нужна — используйте
`hy2xs-orchestrator repair --allow-partial-state`.
### Установка падает на DNS AAAA
Причина: домен имеет IPv6 AAAA‑запись, а HY2XS production profile является IPv4‑only.
Решения:
1. удалить AAAA‑запись у домена;
2. либо временно установить `HY2XS_DNS_AAAA_POLICY=warn`, если оператор осознанно принимает риск клиентских IPv6‑маршрутов вне текущего baseline.
### `DNS IPv4 mismatch`: DNS ведёт не на этот сервер
Причина: A‑запись публичного endpoint указывает на адрес, которого нет среди публичных IPv4 этого сервера. Типичный случай — провайдер принудительно сменил IP, а DNS остался старым: сервисы на машине живы, но клиентская ссылка отправляет людей на другой адрес.
Решения:
1. сверить фактический адрес сервера и обновить A‑запись:
```bash
ip -4 addr show scope global
```
2. дождаться истечения TTL и повторить `hy2xs-orchestrator doctor`;
3. если в строке `server public IPv4:` пусто — на интерфейсах нет публичного IPv4 (сервер за NAT). Это вне baseline; при осознанном решении установите `HY2XS_PUBLIC_ENDPOINT_POLICY=warn`.
Если A‑записей несколько и среди них есть посторонняя, проверка тоже отказывает: HY2XS — single‑host профиль, и второй backend за тем же именем означает, что часть клиентов попадёт не на этот сервер.
### Установка падает на проверке SSH‑порта
HY2XS firewall‑слой проверяет, что порт из `HY2XS_SSH_PORT` уже слушается. Если указано `2323`, но `sshd` продолжает слушать только `22`, установка остановится.
Проверьте:
```bash
ss -H -ltn | grep -E ':(22|2323) '
sshd -T | grep '^port '
```
### Вход по паролю всё ещё работает
Проверьте ранние provider‑конфиги:
```bash
grep -RniE '^[[:space:]]*(PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|PubkeyAuthentication)\b' \
/etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf
```
Если есть ранний файл с `PasswordAuthentication yes`, он может применяться раньше вашего hardening‑файла. Закомментируйте конфликтующие директивы и создайте `00-hy2xs-operator.conf`, как показано в quick start.
### UI не открывается в браузере
Проверьте, что SSH‑туннель запущен и сервис слушает localhost:
```bash
systemctl status hy2xs-admin --no-pager
ss -H -ltn | grep ':8080'
curl -sS http://127.0.0.1:8080/healthz
```
На локальной машине проверьте, что порт не занят другим приложением. При необходимости используйте локальный порт `18080` вместо `8080`.
### Нужно отправить логи без секретов
Соберите диагностику:
```bash
hy2xs-orchestrator diagnostics collect \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
```
Перед публикацией отдельных env/yaml файлов используйте redaction:
```bash
hy2xs-orchestrator redact-config \
--config /etc/hy2xs/hy2xs.env \
--out /root/hy2xs.env.redacted \
--format env
```
## Сборка release‑пакета
Обычному пользователю не нужно собирать проект из исходников. Этот раздел нужен maintainer’у, который готовит release‑архив.
Сборка поддерживается на Debian 13 amd64 из чистого git work tree.
```bash
apt-get update
apt-get install -y git ca-certificates curl unzip tar xz-utils build-essential pkg-config bash coreutils findutils grep sed gawk openssl
git clone
cd
git status --short
```
Подготовьте build env:
Версии и контрольные суммы toolchain **не задаются переменными окружения**: они
объявлены в корневом [`versions.env`](versions.env), и сборка берёт их оттуда.
Раньше их приходилось передавать снаружи, из-за чего воспроизводимая сборка в
чистой Debian‑среде требовала предварительного знания четырёх SHA‑256.
```bash
export BUILD_ID=prod-$(date -u +%Y%m%dT%H%M%SZ)
# Для переносимости между x86_64-серверами без AVX2 предпочтителен baseline artifact.
# Ожидаемый digest выбирается автоматически: в versions.env зафиксированы обе суммы.
export BUN_FLAVOR=x64-baseline
# Опционально: снимает anonymous rate limit при разрешении upstream-релиза.
export GITHUB_TOKEN=
```
`PACKAGE_VERSION` тоже приходит из `versions.env` (`HY2XS_VERSION`). Шаг
`verify_versions_contract` роняет сборку, если версия продукта, схема
конфигурации, целевая платформа или `packageManager` в `package.json`
разошлись с контрактом.
Запустите сборку:
```bash
./tools/build/build.sh
```
Сборка последовательно:
1. проверяет контракт `versions.env` (`verify_versions_contract`);
2. прогоняет тесты и типы оркестратора (`bun test`, `tsc --noEmit`);
3. определяет последнюю стабильную версию Hysteria, берёт ожидаемый SHA‑256 из upstream `hashes.txt` и сверяет с ним скачанный артефакт;
4. проходит compatibility gate: реальный бинарник Hysteria должен принять канонический конфиг HY2XS для Gecko и для Salamander;
5. собирает orchestrator, frontend и backend, проставляя версию админки из контракта;
6. прогоняет `go vet` и `go test` для HY2XS admin;
7. формирует архив и прогоняет acceptance‑проверки.
Любой сбой на шагах 1–6 останавливает сборку до создания пакета.
Переменные, управляющие выбором версии Hysteria:
| Переменная | По умолчанию | Назначение |
| --- | --- | --- |
| `HYSTERIA_CHANNEL` | из `versions.env` (`stable`) | `stable` — разрешить последнюю стабильную; `pinned` — офлайн‑сборка по `tools/build/hysteria-lock.env` |
| `HYSTERIA_VERSION_OVERRIDE` | пусто | Закрепить конкретную версию `vX.Y.Z` |
| `HYSTERIA_COMPAT_GATE` | `true` | Compatibility gate; для release‑сборок обязателен |
| `HYSTERIA_VERIFY_UPSTREAM_HASHES` | `true` | Сверять артефакт с upstream `hashes.txt`; отключение — только break‑glass |
| `HYSTERIA_WRITE_LOCK` | `false` | Записать разрешённые значения обратно в lock‑файл |
Полный E2E с реальным клиентом Hysteria запускается отдельно (нужен Go: ссылка
берётся из production‑генератора, а не из отдельной реализации внутри теста):
```bash
HYSTERIA_BIN=/usr/local/bin/hysteria ./tools/test/e2e-hysteria.sh
```
Результат:
```text
dist/hy2xs-install-.tar.gz
```
Проверьте архив:
```bash
ls -lh dist/hy2xs-install-*.tar.gz
sha256sum dist/hy2xs-install-*.tar.gz
tar -tzf dist/hy2xs-install-1.0.0.tar.gz | grep -E \
'^(hy2xs-install/install.sh|hy2xs-install/orchestrator/hy2xs-orchestrator|hy2xs-install/ui/hy2xs-admin/hy2xs-admin|hy2xs-install/metadata/checksums.txt)$'
```
Подробная документация по сборочному слою находится в [`tools/build/README.md`](tools/build/README.md).
## Структура репозитория
```text
.
├── apps/ # HY2XS admin: Go backend + Vue/Vite frontend
├── orchestrator/ # install-only orchestrator на Bun + TypeScript
├── package/ # skeleton будущего install package
├── tools/build/ # production builder и packaging pipeline
├── tools/test/ # end-to-end проверки с реальным клиентом Hysteria
├── tools/legacy/ # purge-v0.sh: очистка сервера от предыдущего поколения
├── docs/ # спецификации baseline, тестов и эксплуатации
├── versions.env # контракт продукта, платформы и toolchain
├── CHANGELOG.md
├── README.md
└── LICENSE
```
Каталог `dist/` создаётся builder’ом и не должен храниться в git.
## Для кого этот проект
HY2XS рассчитан на операторов, которым нужен воспроизводимый способ поставить Hysteria2‑сервер с локальной панелью управления, не собирая проект на production‑сервере и не открывая admin UI наружу.
Проект особенно полезен, если важны:
- строгая target‑платформа;
- установка из одного release‑архива;
- локальный UI через SSH‑туннель;
- systemd/nftables baseline;
- проверяемая установка со smoke‑checks;
- понятная диагностика при сбоях.
## Лицензия
HY2XS распространяется на условиях **GNU Affero General Public License v3.0 only** (`AGPL-3.0-only`).
Полный текст лицензии находится в [`LICENSE`](LICENSE).
Сторонние библиотеки и зависимости сохраняют собственные лицензии.
---
Разработано во Flamy Studio.