Files
3X-UI_autoVPS/FORK.md
T

24 KiB
Raw Blame History

Форк 3X-UI_autoVPS (hacker07) — своя сборка и свой канал обновлений

Коротко: это 3X-UI_autoVPS со своими доработками. Наш код живёт рядом с кодом автора и не ломается, когда автор выпускает новую версию, а серверы ставятся и обновляются из этого репозитория — релизы подписываются нашим ключом.

С чего начать: новый VPS

  1. Если нужен свой домен для маскировки — заранее направьте его A-запись на IP сервера и освободите порт 80 (иначе Let's Encrypt не выпустит сертификат). Без домена этот шаг пропускается.
  2. Одна команда на сервере (от root) — дальше скрипт спросит сам: какие протоколы ставить (Enter — REALITY + AmneziaWG-3.1, «все» — набор автора), какую маскировку и домен, закрывать ли ответ на ping, включать ли автообновления, менять ли порт SSH (Enter — оставить текущий, число — перевести на него), менять ли пароль root:
bash <(curl -fsSL https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main/scripts/3x-ui-local.sh)

Те же ответы можно задать флагами — тогда вопросов не будет:

bash <(curl -fsSL …/scripts/3x-ui-local.sh) --protocols reality,hy2,awg,mtproto --domain vpn.example.com
  1. Скрипт делает всё сам: проверяет сервер → скачивает наш релиз и проверяет подпись SHA256SUMS и SHA256 установщика → запускает установщик автора (панель 3X-UI, только выбранные протоколы, сайт-заглушка, маскировка, сертификат, подписка, ufw) → наши вопросы и правки (ping, автообновления, второй порт SSH, пароль root) → kit fix → в конце показывает kit check и сводку kit local hardening.
  2. Забрать ссылки и подписку: kit user link admin (или адрес подписки из вывода установки).

Сомневаетесь в канале — сначала ничего не меняющие режимы:

bash <(curl -fsSL …/scripts/3x-ui-local.sh) --verify-only     # только проверка подписи релиза
bash <(curl -fsSL …/scripts/3x-ui-local.sh) --dry-run --domain vpn.example.com   # план установки

Флаги установки

Флаг Что делает
--protocols список какие протоколы ставить: all, minimal или через запятую — reality,reality2,hy2,xhttp,ws,trojan,vmess,ss,tuic,wg,awg,awg3,mtproto. Невыбранные не создаются вообще. Без флага — вопрос, по умолчанию предлагаются REALITY + AmneziaWG-3.1 (ответ «все» — набор автора)
--site N|random|off сайт-заглушка: конкретный из нашего набора (10 ИТ-сайтов), случайный (по умолчанию) или не трогать авторскую страницу (то же — --no-site)
--yes не задавать вопросы (ping закрывается, автообновления включаются, порт SSH и пароль не трогаются)
--harden закрыть ответ на ping и включить автообновления без вопросов
--domain домен свой домен для маскировки: сайт и REALITY маскируются под него (A-запись на сервер, свободный 80)
--sni сайт.com маскировка под чужой сайт вместо домена
--panel-on domain|ip где открывать панель и подписку
--ssh-port N перевести SSH на порт N (число 1024–65535). Прежний порт сам не закрывается: сначала проверьте вход во втором окне, потом kit local ssh --close-old N
--no-harden не закрывать ответ на ping и не включать автообновления безопасности
--no-time / --no-check не запускать kit fix / kit check
--verify-only / --dry-run проверить релиз / показать план и ничего не менять
остальные передаются установщику автора как есть: --host, --user, --port, --protocols, --restore, --multi-port, --cert/--key, --panel-ssl

Чего скрипт не делает (сознательно): apt full-upgrade (тянет систему вперёд и может сломать закреплённые панель 3X-UI 3.9.0 и ядро Xray 26.6.27 — вместо него обновления безопасности), автоматическую перезагрузку и смену пароля root (в однострочник пароль не подставляем, он попал бы в список процессов — для этого есть kit local passwd).

Как проверить, что установка удалась

kit check                    # без ❌: службы, порт 443 (nginx), подписка, маскировка, сертификат
kit local hardening          # сводка: порты SSH, ответ на ping: deny, ufw активен, автообновления: включены
kit local channel            # источник — этот репозиторий, ключей в KIT_SIGNERS: 1 (наш)
kit local version            # версия сборки: 1.2.5 или новее
ping <IP сервера>            # с домашней машины — таймаут: ответ на ping закрыт, это ожидаемо
kit user link admin          # ссылка подписки; откройте её в браузере

Если ставили с доменом, в kit check должны быть строки «свой домен … указывает на этот сервер» и «сайт маскировки … отвечает по TLS 1.3 и HTTP/2»; предупреждения про известный сайт на чужом IP быть не должно (это касалось бы вспомогательного XHTTP, у REALITY должен быть ваш домен).

Команды форка (kit local)

kit local help                    # справка по всем командам форка
kit local version                 # версия, источник обновлений, дата сборки нашего файла
kit local channel                 # откуда берутся обновления и чем проверяется подпись
kit local seams                   # какие швы форка сейчас в установленном /usr/local/bin/kit
kit local masking                 # состояние маскировки (у REALITY должен быть свой домен)
kit local hardening               # сводка по защите сервера

Защита сервера (поведение взято из проекта StressOzz/vps-setup, реализация своя и безопаснее):

Команда Что делает
kit local site поставить случайный сайт-заглушку из нашего набора (10 ИТ-сайтов) и синхронизировать ответ Hysteria2
kit local site --list / kit local site 7 / --show список сайтов с номерами / поставить конкретный / какой стоит сейчас
kit local ssh 2222 добавить порт SSH (прежние продолжают работать; порт сначала открывается в ufw, конфиг проверяется через sshd -t)
kit local ssh --status какие порты SSH слушаются сейчас
kit local ssh --close-old 2222 закрыть прежние порты после того, как новый проверен из второго окна
kit local ping off|on|status ответ на ICMP echo: закрыть/вернуть/показать (правилом ufw, видно в ufw status)
kit local passwd [юзер] сменить пароль (спросит дважды, в историю команд не попадёт)
kit local passwd --generate сгенерировать пароль и показать один раз
kit local upgrades on|off|status автообновления безопасности (только security, без перезагрузки)

К любой правке можно добавить --dry-run — покажет план и ничего не изменит. Копии изменённых файлов складываются в /root/kit-local-backup-<дата>/ (не в /tmp).

Что ping off затрагивает: только ICMP echo. Подписка (HTTPS), панель, REALITY/XHTTP (TCP 443), Hysteria2 (UDP 443) работают как работали; исходящий ping с самого сервера тоже остаётся. Если сервер есть в вашем мониторинге — пробу до него делайте по HTTPS/TCP, а не по ICMP.

Что где лежит

Что Где Правит
Наши доработки (команды, хуки) scripts/kit-local.sh только мы
Установка одной командой (обёртка) scripts/3x-ui-local.sh только мы
Описание «швов» в коде автора tools/fork_apply.py только мы
Синхронизация с автором и проверки tools/fork-sync.sh только мы
Записи о наших версиях FORK-CHANGELOG.md только мы
README.md целиком, включая картинки в manuals/assets/ только мы
Файлы автора (scripts/kit.sh, scripts/3x-ui.sh, …) как есть только автор

Ключевая идея: в файлах автора мы правим только «швы» — несколько констант, одну строку подключения и нормализацию сумм в релизном скрипте. Всё, что мы дописываем по существу, лежит в наших файлах. Швы описаны в одном месте (tools/fork_apply.py) и накатываются только на чистое дерево автора; если автор изменил нужную строку — инструмент останавливается и говорит об этом, а не делает полу-патч.

Ветки

tools/release.sh — наш файл (как README): авторский скрипт подписи мы переписали под себя (проверка LF, гейт справки, шапка SHA256SUMS в двух строках, свой список файлов). Швов к нему нет; при обновлении из авторского проекта файл конфликтует и решается в нашу пользу (как README).

Важно при правках швов: авторский скрипт работает с set -u, поэтому если оборачивать его присваивание в if, переменную нужно объявить пустой (local extra="", а не local extra) — иначе при невыполненном условии любое чтение $extra валит установку на середине с «unbound variable».

Проверка корректности швов (делать после каждой правки и после синхронизации с автором): накатать tools/fork_apply.py на чистую копию авторского кода и сверить с нашим деревом — расхождений быть не должно, кроме наших собственных файлов. Это единственный способ увидеть правки, которых нет в швах.

main двигается только вместе с подписанным релизом. Серверы читают версию из main (KIT_RAW), поэтому в main не должно попадать то, что ещё не подписано: иначе kit update и однострочник установки упираются в отсутствующий тег или в неподписанные суммы. Порядок такой:

  1. поднять версию и собрать изменения в dev;
  2. пользователь подписывает (tools/release.sh);
  3. коммит с SHA256SUMS и SHA256SUMS.sig → тег v<версия> → и только теперь dev вливается в main.

Проверка перед публикацией в main: curl -fsSL .../raw/tag/v<версия>/VERSION должен отдавать ту же версию, а bash <(curl ... raw/branch/main/scripts/3x-ui-local.sh) --verify-only — писать, что подпись сошлась. и релизы

  • upstream — точная копия ветки автора (main), только вперёд.
  • dev — наша сборка: upstream + швы + наши файлы; из неё делаются релизы.
  • main — то же состояние плюс документация после релиза (по нему считается «последняя версия»).
  • релизы — теги v<номера>; номер = <версия автора>.<наш уровень> (автор 1.2 → наш 1.2.1; после релиза автора 1.3 → 1.3.1). Номер обязан строго расти: kit update не принимает откат назад.

Обновление сервера и канал

kit update --force && kit check      # тянет наш релиз из этого репозитория, проверяет нашу подпись

Источник файлов — KIT_RAW / kit_ref_raw() в scripts/kit.sh (наш Gitea и наш тег), подпись проверяется ключом из KIT_SIGNERS (там только ключ форка). Ночной kit-update.timer (03:00) делает то же автоматически — поэтому сервер и должен смотреть в этот репозиторий: иначе наши доработки исчезнут в первую же ночь.

Вернуться на канал автора: скачать scripts/kit.sh из релиза автора с GitHub в /usr/local/bin/kit, затем kit update --force.

Обновление от автора (с рабочей станции)

Автоматически: робот на раннере (CT 114)

Раз в сутки (03:30) Gitea сама запускает .gitea/workflows/fork-sync.yml на раннере в CT 114: робот tools/fork-sync-bot.sh забирает ветку автора, переносит на неё наши правки, прогоняет те же проверки, что и tools/fork-sync.sh, дописывает раздел в FORK-CHANGELOG.md и публикует dev (--force-with-lease). Не сошлось (конфликт в местах наших правок) — ничего не публикуется, причина видна в Gitea → Actions и в /opt/kit-fork-sync/last-run.log на контейнере.

Робот не выпускает и не подписывает релиз: закрытый ключ подписи остаётся на рабочей станции. После автоматической синхронизации — как обычно: git fetch origin && git checkout dev && git reset --hard origin/dev (робот переписывает dev), описать изменения в FORK-CHANGELOG.md, затем tools/release.sh и тег.

cd H:/My_Proj/Fork_3X-UI_KIT
bash tools/fork-sync.sh            # подтянуть автора, перенести наши правки, прогнать проверки
# при конфликте — разобрать вручную: инструмент сам ничего не публикует

bash tools/release.sh ~/.ssh/3x-ui-kit-fork/release_key   # SHA256SUMS + подпись нашим ключом

**Перед подписью — обязательный прогон.** `bash scripts/3x-ui-local.sh --verify-only` проверяет ровно тот
путь установки, которым пойдут серверы: скачивание всех файлов релиза и сверка каждого с подписанным
списком. На Windows запускать с `TMPDIR=C:/…` (нативные утилиты не понимают MSYS-пути).

**Нумерация версий.** Основа — версия автора, наша надстройка добавляется через точку с единицы:
у автора 1.2.1 → наша сборка 1.2.1.1; вышла его 1.2.2 → наша 1.2.2.1. Так на сервере всегда видно,
на чьей базе собрана сборка.

**Обновление базы автора.** Когда автор выпускает новую версию: перенести его файлы
(`git checkout upstream -- scripts/kit.sh scripts/3x-ui.sh scripts/hysteria2.sh scripts/kit-sub.py`),
прогнать `python tools/fork_apply.py` и посмотреть, какие якоря не нашли себя. Те, чью работу автор уже
делает сам, — вычеркнуть; остальные перенастроить на его новый код и пересобрать таблицу швов
`git branch -f upstream <коммит автора>`.

Скрипт сам приводит переводы строк к LF, проверяет справку и то, что установщик не ругается на пустые
строки, сверяет панель и ядро Xray с официальными хешами GitHub (если GitHub недоступен — предупреждает и
пропускает эти проверки за секунды, а не висит; расхождение хешей при живом GitHub по-прежнему останавливает
выпуск). Далее скрипт ждёт **парольную фразу ключа** — её вводит человек, поэтому запускать надо в окне, где
можно набрать пароль (в PowerShell — да, в фоне — нет).
git add -A && git commit -m "Релиз v$(cat VERSION): …" && git tag v$(cat VERSION)
git push origin dev v$(cat VERSION) upstream

Подпись релизов

  • Открытая часть ключа — tools/fork-signer.pub и в KIT_SIGNERS (в scripts/kit.sh); она же вшита в scripts/3x-ui-local.sh, чтобы проверять подпись до запуска установщика.
  • Закрытая часть — только на рабочей станции владельца (~/.ssh/3x-ui-kit-fork/release_key). В репозиторий, CI и на серверы не попадает: кто получит ключ — получит root на всех серверах, которые обновляются из этого репозитория. Пароль на ключ пока не установлен (ssh-keygen -p -f ~/.ssh/3x-ui-kit-fork/release_key).
  • Смена ключа: новый релиз подписывается старым ключом, иначе серверы его не примут.

Грабли, о которых стоит помнить

  1. Ночное автообновление (kit-update.timer, 03:00) перезаписывает /usr/local/bin/kit тем, что лежит в настроенном репозитории. Канал и наши правки — один шаг, а не два.
  2. Переводы строк. Релиз подписывается по суммам рабочей копии, а сервер сверяет их с файлами из Gitea: при core.autocrlf=true суммы не сойдутся (CRLF против LF). В клоне должно быть git config core.autocrlf false (и .gitattributes), проверка есть в tools/fork-sync.sh.
  3. Наш файл обязан быть в двух списках — FILES=(…) в tools/release.sh и список загрузки fetch_release() в scripts/kit.sh, иначе после обновления kit-local.sh останется старым.
  4. Панель 3X-UI ставится с GitHub по закреплённой версии и SHA256 — это шаг автора; для установки с нуля серверу нужен доступ к GitHub.
  5. Формат контрольных сумм. kit сверяет имя файла в SHA256SUMS строго ($2 == "путь"). На Windows sha256sum пишет hash *путь, и такие релизы kit update отклоняет («в подписанном списке нет такого файла»), хотя sha256sum -c их принимает. Поэтому tools/release.sh приводит список к POSIX-виду до подписи, а релизы v1.2.1–v1.2.4 ставить нельзя — только v1.2.5+. Проверять релиз надо логикой самого kit, а не одной sha256sum -c.
  6. Сборка релиза обращается к GitHub (сверка закреплённых версий панели и ядра). Из домашней сети TLS до GitHub иногда рвётся (schannel: failed to receive handshake) — просто повторить запуск.
  7. README.md — наш файл целиком. Авторское описание убрано: иначе человек, открывший репозиторий, читает команду установки авторской сборки, ставит по ней и оказывается на авторском канале обновлений — ночное автообновление вернёт его сборку. При обновлении из авторского проекта README конфликтует: берём нашу версию (git checkout --ours README.md), авторские правки в нём нас не интересуют.

Что проверено на сегодня

  • Релиз v1.2.5: подпись сходится с ключом из опубликованного kit.sh, строгая сверка сумм $2 == путь для kit.sh, kit-sub.py, kit-local.sh — как это делает kit на сервере.
  • Публичный однострочник в режиме --verify-only отвечает «подпись сошлась, SHA256 установщика совпал» — проверено снаружи, из домашней сети.
  • Установка «в бою» на живом сервере пока не прогонялась: сначала её стоит выполнить на новом VPS, посмотрев --verify-only и --dry-run.