Compare commits

...
3 Commits
Author SHA1 Message Date
hacker07 d034e207da docs: README под новые возможности; журнал версий в порядке убывания
Синхронизация с автором / sync (push) Successful in 1s
2026-10-13 22:59:43 +03:00
hacker07 22b9925b0c Релиз v1.2.14: подпись
Синхронизация с автором / sync (push) Successful in 2s
2026-10-13 22:57:13 +03:00
hacker07 9aa93fad37 v1.2.14: меню выбора сайта маскировки по скану + снос не обрывается на set -e 2026-10-13 22:52:33 +03:00
11 changed files with 191 additions and 52 deletions
+18 -17
View File
@@ -1,3 +1,15 @@
## v1.2.14: выбор сайта маскировки по результатам скана + снос, который не обрывается
* **Появилось меню маскировки.** Сначала один скан подсети (~250 обращений к 443 соседних адресов),
потом выбор: номер из найденного, `0` — ввести адрес вручную, Enter — автоподбор. Отдельно для
**REALITY** (найденное или свой) и отдельно для **XHTTP/MTProto** (найденное, свой или известный
сайт — список известных расширен Microsoft, AMD, Intel, NVIDIA). Если XHTTP/MTProto не выбраны,
второго меню не будет. Отключить меню: `--auto-sni`.
* **Исправлено: `kit local uninstall` обрывался на середине.** `kit` запускает наши команды
с `set -e`, и подсчёт чужих сайтов nginx (`find | grep | wc -l`, где grep не находит ничего и
возвращает 1) убивал скрипт сразу после остановки служб — файлы и `/usr/local/bin/kit` оставались,
поэтому `kit help` продолжал работать. Теперь снос выполняется с отключённым `set -e` и все
подобные конвейеры защищены; проверка после сноса это подтверждает списком, если что-то осталось.
## v1.2.13: вопросов после показа доступов нет, и никакой работы для невыбранных протоколов ## v1.2.13: вопросов после показа доступов нет, и никакой работы для невыбранных протоколов
* **Все вопросы — до установщика.** Раньше после установки (когда на экране уже были данные входа * **Все вопросы — до установщика.** Раньше после установки (когда на экране уже были данные входа
@@ -9,7 +21,6 @@
* Наши команды помнят выбор: установщик пишет список протоколов в `/etc/kit/kl-protos`, и * Наши команды помнят выбор: установщик пишет список протоколов в `/etc/kit/kl-protos`, и
`kit local site` не синхронизирует ответ Hysteria2, если Hysteria2 не ставился (раньше он это делал `kit local site` не синхронизирует ответ Hysteria2, если Hysteria2 не ставился (раньше он это делал
и выдавал предупреждение про отсутствующий Hysteria2). и выдавал предупреждение про отсутствующий Hysteria2).
## v1.2.12: не сканируем соседей, когда они не нужны ## v1.2.12: не сканируем соседей, когда они не нужны
* Автор подбирал сайты-соседи для XHTTP и MTProto **всегда**, когда маскировка доверенная («панель по IP») * Автор подбирал сайты-соседи для XHTTP и MTProto **всегда**, когда маскировка доверенная («панель по IP»)
@@ -21,14 +32,6 @@
Здесь записи о наших версиях. Файл автора (`CHANGELOG.md`) мы не трогаем — так обновления Здесь записи о наших версиях. Файл автора (`CHANGELOG.md`) мы не трогаем — так обновления
автора не конфликтуют с нашими записями. автора не конфликтуют с нашими записями.
## v1.2.6: вопросы при установке
* **README.md переписан целиком:** убрано авторское описание с его командой установки, добавлены
актуальная команда, флаги, таблица протоколов, список сайтов-заглушек, команды `kit` и `kit local`,
канал обновлений и оговорки. Две картинки — наши (`manuals/assets/fork-banner.svg` и
`manuals/assets/script-3x-ui-local.svg`, терминал собран из настоящего вывода скрипта).
* Шов в README больше не нужен (`tools/fork_apply.py`): файл наш целиком, как `kit-local.sh`.
## v1.2.11: снос знает про kit-limit и находит свои службы сам ## v1.2.11: снос знает про kit-limit и находит свои службы сам
* В списке сноса не было `kit-limit.service` и `kit-limit.timer` (юнит «общий лимит трафика» из kit) — * В списке сноса не было `kit-limit.service` и `kit-limit.timer` (юнит «общий лимит трафика» из kit) —
@@ -38,7 +41,6 @@
новая служба, добавленная автором позже, не останется незамеченной. Проверка после сноса ищет новая служба, добавленная автором позже, не останется незамеченной. Проверка после сноса ищет
по тем же шаблонам и показывает всё найденное. по тем же шаблонам и показывает всё найденное.
* Флаг `--clean` у установщика использует тот же список. * Флаг `--clean` у установщика использует тот же список.
## v1.2.10: убран мусор в выводе установщика ## v1.2.10: убран мусор в выводе установщика
* В шапке `scripts/3x-ui-local.sh` один из флагов потерял `#` и **при каждом запуске печаталось * В шапке `scripts/3x-ui-local.sh` один из флагов потерял `#` и **при каждом запуске печаталось
@@ -46,7 +48,6 @@
Исправлено, заодно выровнены строки справки. Исправлено, заодно выровнены строки справки.
* В релизный гейт добавлена проверка, которая такое ловит: холостой прогон установщика, чей вывод в stderr * В релизный гейт добавлена проверка, которая такое ловит: холостой прогон установщика, чей вывод в stderr
содержит «command not found / syntax error / bad substitution», релиз не собирается. содержит «command not found / syntax error / bad substitution», релиз не собирается.
## v1.2.9: снос действительно убирает всё, повторная установка после сноса ## v1.2.9: снос действительно убирает всё, повторная установка после сноса
* Список удаляемого в `kit local uninstall` собран заново **по коду установщика** и дополнен тем, чего * Список удаляемого в `kit local uninstall` собран заново **по коду установщика** и дополнен тем, чего
@@ -59,7 +60,6 @@
* Флаг `--reset-ssh`: снести и наш ssh-дроп-ин (порт возвращается к системному). По умолчанию SSH не трогаем. * Флаг `--reset-ssh`: снести и наш ssh-дроп-ин (порт возвращается к системному). По умолчанию SSH не трогаем.
* У установщика появился флаг **`--clean`**: убрать прошлую установку и поставить заново. Если установщик * У установщика появился флаг **`--clean`**: убрать прошлую установку и поставить заново. Если установщик
всё-таки упал на «уже установлена», в подсказке прямо сказано запустить с `--clean`. всё-таки упал на «уже установлена», в подсказке прямо сказано запустить с `--clean`.
## v1.2.8: доступ к панели в конце установки и снос сервера из меню ## v1.2.8: доступ к панели в конце установки и снос сервера из меню
* В конце установки (перед подпиской) печатается **доступ к панели 3X-UI**: адрес, логин и пароль из * В конце установки (перед подпиской) печатается **доступ к панели 3X-UI**: адрес, логин и пароль из
@@ -71,7 +71,6 @@
«удалить», есть `--dry-run`. «удалить», есть `--dry-run`.
**SSH не трогается** — ни порт, ни конфиги, ни правила для него: иначе легко потерять доступ к серверу. **SSH не трогается** — ни порт, ни конфиги, ни правила для него: иначе легко потерять доступ к серверу.
В главном меню `kit` появился пункт **10. Удалить всё установленное**. В главном меню `kit` появился пункт **10. Удалить всё установленное**.
## v1.2.7: имя 3X-UI_autoVPS, справка kit help, подписка с QR, живые кнопки на заглушках ## v1.2.7: имя 3X-UI_autoVPS, справка kit help, подписка с QR, живые кнопки на заглушках
* **Сборка называется `3X-UI_autoVPS`** (было `3X-UI KIT`): баннер сервера — `Hacker 07` в том же * **Сборка называется `3X-UI_autoVPS`** (было `3X-UI KIT`): баннер сервера — `Hacker 07` в том же
@@ -119,7 +118,13 @@
* Флаг `--harden` теперь действительно существует (раньше он уходил установщику автора неизвестным * Флаг `--harden` теперь действительно существует (раньше он уходил установщику автора неизвестным
параметром и ронял установку): закрыть ping и включить автообновления без вопросов. параметром и ронял установку): закрыть ping и включить автообновления без вопросов.
* `--dry-run` показывает, что именно будет спрошено или сделано по умолчанию. * `--dry-run` показывает, что именно будет спрошено или сделано по умолчанию.
## v1.2.6: вопросы при установке
* **README.md переписан целиком:** убрано авторское описание с его командой установки, добавлены
актуальная команда, флаги, таблица протоколов, список сайтов-заглушек, команды `kit` и `kit local`,
канал обновлений и оговорки. Две картинки — наши (`manuals/assets/fork-banner.svg` и
`manuals/assets/script-3x-ui-local.svg`, терминал собран из настоящего вывода скрипта).
* Шов в README больше не нужен (`tools/fork_apply.py`): файл наш целиком, как `kit-local.sh`.
## v1.2.5: суммы релиза в формате, который понимает kit ## v1.2.5: суммы релиза в формате, который понимает kit
* Исправлена ошибка нашего релизного конвейера: на Windows `sha256sum` пишет суммы как * Исправлена ошибка нашего релизного конвейера: на Windows `sha256sum` пишет суммы как
@@ -130,14 +135,12 @@
другой машине. другой машине.
* Проверка теперь повторяет логику самого `kit`: подпись `SHA256SUMS` + строгая сверка имени * Проверка теперь повторяет логику самого `kit`: подпись `SHA256SUMS` + строгая сверка имени
каждого файла из подписанного списка (а не только `sha256sum -c`, который звёздочку прощает). каждого файла из подписанного списка (а не только `sha256sum -c`, который звёздочку прощает).
## v1.2.4: проверка релиза отдельной командой ## v1.2.4: проверка релиза отдельной командой
* Флаг `--verify-only`: скачать релиз, проверить подпись `SHA256SUMS` и SHA256 установщика и выйти — * Флаг `--verify-only`: скачать релиз, проверить подпись `SHA256SUMS` и SHA256 установщика и выйти —
можно проверить канал на сервере, ничего не устанавливая. можно проверить канал на сервере, ничего не устанавливая.
* Порядок шагов изменён: проверка подписи теперь идёт до предполётных проверок и до любых * Порядок шагов изменён: проверка подписи теперь идёт до предполётных проверок и до любых
изменений на сервере, чтобы чужой код не запускался раньше проверки. изменений на сервере, чтобы чужой код не запускался раньше проверки.
## v1.2.3: установка одной командой (`scripts/3x-ui-local.sh`) ## v1.2.3: установка одной командой (`scripts/3x-ui-local.sh`)
* Один скрипт на чистом VPS: проверка сервера → скачивание нашего релиза → проверка подписи * Один скрипт на чистом VPS: проверка сервера → скачивание нашего релиза → проверка подписи
@@ -152,7 +155,6 @@
передаются как есть; есть `--dry-run` (план без изменений) и `--help`. передаются как есть; есть `--dry-run` (план без изменений) и `--help`.
* Публичный ключ форка вшит в обёртку — подпись проверяется до запуска чужого кода; при смене * Публичный ключ форка вшит в обёртку — подпись проверяется до запуска чужого кода; при смене
ключа обновляется и здесь, и в `kit`. ключа обновляется и здесь, и в `kit`.
## v1.2.2: команды защиты сервера (поведение из StressOzz/vps-setup) ## v1.2.2: команды защиты сервера (поведение из StressOzz/vps-setup)
* Новые команды `kit local`: `ssh <порт>` (добавить порт SSH), `ssh --close-old` (закрыть прежние * Новые команды `kit local`: `ssh <порт>` (добавить порт SSH), `ssh --close-old` (закрыть прежние
@@ -169,7 +171,6 @@
а закрытие старых портов не делается автоматически — там ошибка запирает доступ к серверу. а закрытие старых портов не делается автоматически — там ошибка запирает доступ к серверу.
* Ping отключается правилом ufw (`before.rules`), а не общесистемным `icmp_echo_ignore_all`: * Ping отключается правилом ufw (`before.rules`), а не общесистемным `icmp_echo_ignore_all`:
видно в `ufw status`, отменяется одной командой и не глушит ICMP для внутренних интерфейсов. видно в `ufw status`, отменяется одной командой и не глушит ICMP для внутренних интерфейсов.
## v1.2.1: сборка форка поверх 3X-UI_autoVPS 1.2 ## v1.2.1: сборка форка поверх 3X-UI_autoVPS 1.2
* Источник файлов и обновлений — свой репозиторий Gitea: установка и `kit update` берут релиз * Источник файлов и обновлений — свой репозиторий Gitea: установка и `kit update` берут релиз
+33 -12
View File
@@ -28,9 +28,15 @@
bash <(curl -fsSL https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main/scripts/3x-ui-local.sh) bash <(curl -fsSL https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main/scripts/3x-ui-local.sh)
``` ```
Без флагов скрипт **спрашивает** всё по ходу: какие протоколы ставить (Enter — REALITY + AmneziaWG-3.1), Без флагов скрипт **спрашивает всё заранее, до установки**: какие протоколы ставить (Enter — REALITY +
какую маскировку и домен, закрывать ли ответ на ping, включать ли автообновления безопасности, AmneziaWG-3.1), менять ли порт SSH (Enter — оставить 22), менять ли пароль root, закрывать ли ответ на
менять ли порт SSH (Enter — оставить 22), менять ли пароль root. ping, включать ли автообновления безопасности. Затем сканирует подсеть и **предлагает выбрать сайт
маскировки** — отдельно для REALITY и, если выбраны XHTTP/MTProto, отдельно для них: номер из найденных
соседей, `0` — ввести адрес вручную, для XHTTP/MTProto ещё и известный сайт (Google, Microsoft, AMD…).
После установщика скрипт **ничего не спрашивает** — только выполняет выбранное, проверяет сервер и в
самом конце показывает то, что нужно унести: **доступ к панели 3X-UI** (адрес, логин, пароль) и
**подписку с QR-кодом** и ссылками по протоколам.
<img alt="Установка одной командой" src="manuals/assets/script-3x-ui-local.svg" width="860"> <img alt="Установка одной командой" src="manuals/assets/script-3x-ui-local.svg" width="860">
@@ -67,6 +73,9 @@ bash <(curl -fsSL …/scripts/3x-ui-local.sh) --dry-run # что имен
| `--harden` / `--no-harden` | закрыть ping и включить автообновления безопасности / не трогать это | | `--harden` / `--no-harden` | закрыть ping и включить автообновления безопасности / не трогать это |
| `--yes` | не задавать вопросы (для автоматизации) | | `--yes` | не задавать вопросы (для автоматизации) |
| `--host IP`, `--user имя`, `--port N`, `--restore файл` | адрес в ссылках, имя первого клиента, порт REALITY, восстановление из копии `kit backup` | | `--host IP`, `--user имя`, `--port N`, `--restore файл` | адрес в ссылках, имя первого клиента, порт REALITY, восстановление из копии `kit backup` |
| `--clean` | убрать прошлую (оборвавшуюся) установку и поставить заново — SSH не трогает |
| `--auto-sni` | не показывать меню выбора сайта маскировки, подобрать самим (автоподбор автора) |
| `--no-sub` | не показывать в конце доступ к панели, подписку и QR-код |
| `--no-time`, `--no-check`, `--verify-only`, `--dry-run`, `-h` | служебные | | `--no-time`, `--no-check`, `--verify-only`, `--dry-run`, `-h` | служебные |
## Что происходит по шагам ## Что происходит по шагам
@@ -74,15 +83,17 @@ bash <(curl -fsSL …/scripts/3x-ui-local.sh) --dry-run # что имен
1. **Проверка сервера**: root, Debian/Ubuntu, память, место на диске, занятые порты 80/443. 1. **Проверка сервера**: root, Debian/Ubuntu, память, место на диске, занятые порты 80/443.
2. **Проверка релиза**: файлы берутся из этого репозитория по тегу, проверяется **подпись `SHA256SUMS`** 2. **Проверка релиза**: файлы берутся из этого репозитория по тегу, проверяется **подпись `SHA256SUMS`**
нашим ключом и SHA256 самого установщика. Не сошлось — ничего не запускается. нашим ключом и SHA256 самого установщика. Не сошлось — ничего не запускается.
3. **Выбор протоколов** и запуск установщика: панель 3X-UI, выбранные подключения, nginx на 443, 3. **Наши вопросы** (до установки): протоколы, порт SSH, пароль root, ping, автообновления.
сертификат, сайт-заглушка, служба подписки, правила ufw. 4. **Установщик**: панель 3X-UI, только выбранные подключения, nginx на 443, сертификат, сайт-заглушка,
4. **Сайт-заглушка**: ставится случайный правдоподобный ИТ-сайт из нашего набора (10 штук) и служба подписки, правила ufw. Маскировку спрашивает он: свой домен, сосед по подсети или свой сайт.
синхронизируется в ответ Hysteria2. Если выбран сосед — сначала идёт **скан подсети**, затем меню выбора сайта (см. выше).
5. **Наши вопросы и правки**: закрыть ответ на ping, включить обновления безопасности, сменить порт 5. **Наши действия** (без вопросов): правдоподобный ИТ-сайт-заглушка вместо авторской страницы,
SSH, сменить пароль root. `kit fix` (время и безопасные починки), закрыть ответ на ping, включить обновления безопасности,
6. **`kit fix`** — синхронизация времени и безопасные починки. перевести SSH на новый порт, сменить пароль root.
7. **Проверка**: `kit check` (порты, службы, подписка, сертификат, маскировка) и сводка 6. **Проверка**: `kit check` (порты, службы, подписка, сертификат, маскировка) и сводка
`kit local hardening`. В конце — как забрать ссылки и подписку. `kit local hardening`.
7. **В самом конце** — **доступ к панели 3X-UI** (адрес, логин, пароль из `/root/3x-ui.txt`) и
**подписка с QR-кодом** и ссылками по протоколам. После них скрипт уже ничего не делает и не спрашивает.
## Протоколы ## Протоколы
@@ -110,6 +121,15 @@ bash <(curl -fsSL …/scripts/3x-ui-local.sh) --dry-run # что имен
дополнительных объяснений на клиенте. Маскировку и сайт можно менять потом: `kit net site`, дополнительных объяснений на клиенте. Маскировку и сайт можно менять потом: `kit net site`,
`kit local site`. `kit local site`.
**Сайт маскировки выбираете вы.** После скана подсети установщик спрашивает отдельно для REALITY
(найденный сосед или свой адрес) и отдельно для XHTTP/MTProto (найденный, свой или известный сайт —
список известных расширен Google, Amazon, Samsung, Yahoo, Microsoft, AMD, Intel, NVIDIA). Выбранный
адрес проверяется на TLS 1.3 + HTTP/2. Если XHTTP и MTProto не выбраны, второго вопроса нет.
Для протоколов, которые **не** выбраны, не делается ничего: ни подключений, ни записей в nginx, ни
ссылок в подписке, ни лишних обращений к сети (например, обход соседей запускается только когда он
действительно нужен).
## Сайт-заглушка ## Сайт-заглушка
На 443 при неизвестном имени отдаётся страница нашего набора: небольшие правдоподобные ИТ-сайты На 443 при неизвестном имени отдаётся страница нашего набора: небольшие правдоподобные ИТ-сайты
@@ -163,6 +183,7 @@ kit local site --show # какой стоит сейчас
| `kit local ping off\|on\|status` | ответ на ICMP echo правилом ufw (видно в `ufw status`, отменяется одной командой) | | `kit local ping off\|on\|status` | ответ на ICMP echo правилом ufw (видно в `ufw status`, отменяется одной командой) |
| `kit local passwd [юзер]`, `--generate` | смена пароля (спрашивает дважды, в логи не попадает) | | `kit local passwd [юзер]`, `--generate` | смена пароля (спрашивает дважды, в логи не попадает) |
| `kit local upgrades on\|off\|status` | обновления безопасности | | `kit local upgrades on\|off\|status` | обновления безопасности |
| `kit local uninstall` | **снести установку целиком**: панель, службы и файлы kit, nginx и сайт, сертификаты, наши правки системы и правила ufw для 80/443. Спрашивает слово «удалить», есть `--dry-run`. **SSH не трогает** |
| `kit local hardening`, `masking`, `channel`, `seams`, `version` | сводка защиты, состояние маскировки, канал обновлений, какие места форк правит у автора | | `kit local hardening`, `masking`, `channel`, `seams`, `version` | сводка защиты, состояние маскировки, канал обновлений, какие места форк правит у автора |
У любой правки есть `--dry-run` — показывает план и ничего не меняет. Копии изменённых файлов У любой правки есть `--dry-run` — показывает план и ничего не меняет. Копии изменённых файлов
+8 -8
View File
@@ -1,9 +1,9 @@
# 3X-UI KIT 1.2.13 # 3X-UI KIT 1.2.14
# 3X-UI_autoVPS 1.2.13 # 3X-UI_autoVPS 1.2.14
cd74360690438bbdd29c49ee91fdeb9f52c90ec1e909784000553fbf79aa54bc scripts/3x-ui.sh 666af017a32dd8b5b4c82488af7d47c17ebd4d7b700ddf056ee3e50c089b13d1 scripts/3x-ui.sh
231546d9a0385d6269273f51232b19a61abb48e41380badd7e0e374182dfbd86 scripts/hysteria2.sh d224550fc777ac15c9bccafb17a82a36b42bcdc6376572c7c0d1bb9781a200dc scripts/hysteria2.sh
04984a970f6e9505f62068ed71e7d5596d351cdd56647c4e612200ad58d66914 scripts/kit.sh d362a890e8e70a835021dadd8996409069e644db000f9685415bf857112fcf05 scripts/kit.sh
4027ea2c6dbc9fd176356acac3f676445419c2f021146ed25dfe5a304c3ac5fb scripts/kit-sub.py 4027ea2c6dbc9fd176356acac3f676445419c2f021146ed25dfe5a304c3ac5fb scripts/kit-sub.py
d2946f95d7206c8f70aa7d28c36e5ffa781a7e4dbbb83230b774ba284fd3eaf1 scripts/kit-local.sh d248182063d75028f20f83c2669b020515dd80ea91bafba100ac75a7c3480941 scripts/kit-local.sh
129ca6a643b62289a7933f702c0eb4c8a04075e24a6fa51c0df8dd65dda388bc scripts/3x-ui-local.sh a48508498b4fcd88d55eb011e1c60a9503fdfa28ceae22053d4c05a10c4cf6a7 scripts/3x-ui-local.sh
c21698334b1e2308b8556ac1d10402342b8d3e837f65fa1c431eb23392824b1d VERSION 22e988bfb2127d8584cc829f76868c2c85e138af32fe0989b29f7580b88a00a5 VERSION
+2 -2
View File
@@ -1,6 +1,6 @@
-----BEGIN SSH SIGNATURE----- -----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgozRJ+f9RB6qO9S9j3BWCDtC0bV U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgozRJ+f9RB6qO9S9j3BWCDtC0bV
FHakRSSqtQPljLoQUAAAARM3gtdWkta2l0LXJlbGVhc2UAAAAAAAAABnNoYTUxMgAAAFMA FHakRSSqtQPljLoQUAAAARM3gtdWkta2l0LXJlbGVhc2UAAAAAAAAABnNoYTUxMgAAAFMA
AAALc3NoLWVkMjU1MTkAAABAabjVMlDEIV+naJm6DJrU/5noz1yaF/N3dqAVZdskWwCB9V AAALc3NoLWVkMjU1MTkAAABAgzLMUmGdBD24AF1SiVATfnfXNJK0/mVfusR3Wm+pEBIunn
sSUf+ylo45wF9B4d04On900xu6B3ve01WMvOabDg== 18+cyVZfVPHaFlB7yXmYhZ1/xPtRKMVnVQoSJFAQ==
-----END SSH SIGNATURE----- -----END SSH SIGNATURE-----
+1 -1
View File
@@ -1 +1 @@
1.2.13 1.2.14
+11 -1
View File
@@ -35,6 +35,7 @@
# --no-time не запускать kit fix # --no-time не запускать kit fix
# --no-check не запускать в конце kit check # --no-check не запускать в конце kit check
# --clean убрать прошлую установку и поставить заново (SSH не трогает) # --clean убрать прошлую установку и поставить заново (SSH не трогает)
# --auto-sni не предлагать выбор сайта маскировки (иначе покажем меню по скану подсети)
# --verify-only только проверить релиз (подпись и SHA256) и выйти # --verify-only только проверить релиз (подпись и SHA256) и выйти
# --dry-run показать план и ничего не делать # --dry-run показать план и ничего не делать
# -h | --help эта справка # -h | --help эта справка
@@ -43,7 +44,7 @@
set -Eeuo pipefail set -Eeuo pipefail
KIT_VERSION="1.2.13" # версия сборки форка; должна совпадать с VERSION релиза KIT_VERSION="1.2.14" # версия сборки форка; должна совпадать с VERSION релиза
GITEA="${KIT_FORK_BASE:-https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS}" GITEA="${KIT_FORK_BASE:-https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS}"
NS="3x-ui-kit-release" NS="3x-ui-kit-release"
SIG_ID="releases@3x-ui-kit" SIG_ID="releases@3x-ui-kit"
@@ -81,6 +82,7 @@ usage() {
не трогать авторскую страницу (то же: --no-site) не трогать авторскую страницу (то же: --no-site)
--no-sub не показывать в конце подписку, доступ к панели и QR-код --no-sub не показывать в конце подписку, доступ к панели и QR-код
--clean снести прошлую установку и поставить заново (если прошлый запуск оборвался) --clean снести прошлую установку и поставить заново (если прошлый запуск оборвался)
--auto-sni не предлагать выбор сайта маскировки (автоподбор автора)
--no-time не запускать kit fix (время и безопасные починки) --no-time не запускать kit fix (время и безопасные починки)
--no-check не запускать в конце kit check --no-check не запускать в конце kit check
--verify-only только проверить релиз (подпись и SHA256) и выйти --verify-only только проверить релиз (подпись и SHA256) и выйти
@@ -95,6 +97,7 @@ INSTALLER_ARGS=(); harden=auto; ssh_port=""; fix_time=yes; do_check=yes; dry=no;
site=random # заглушка: случайный сайт из нашего набора (off — не трогать) site=random # заглушка: случайный сайт из нашего набора (off — не трогать)
show_sub=yes # в самом конце показать подписку, QR-код и ссылки show_sub=yes # в самом конце показать подписку, QR-код и ссылки
clean=no # --clean: убрать прошлую установку перед новой clean=no # --clean: убрать прошлую установку перед новой
pick_sni=yes # показывать меню выбора сайта маскировки по результатам скана
while (($#)); do while (($#)); do
case $1 in case $1 in
--harden) harden=yes ;; --harden) harden=yes ;;
@@ -107,6 +110,7 @@ while (($#)); do
--no-site) site=off ;; --no-site) site=off ;;
--no-sub) show_sub=no ;; --no-sub) show_sub=no ;;
--clean) clean=yes ;; --clean) clean=yes ;;
--auto-sni) pick_sni=no ;;
--no-time) fix_time=no ;; --no-time) fix_time=no ;;
--no-check) do_check=no ;; --no-check) do_check=no ;;
--verify-only) verify_only=yes ;; --verify-only) verify_only=yes ;;
@@ -139,6 +143,8 @@ if [[ $dry == yes ]]; then
pre "версия сборки: $KIT_VERSION" pre "версия сборки: $KIT_VERSION"
pre "откуда: $GITEA (тег v$KIT_VERSION), подпись проверяется ключом форка" pre "откуда: $GITEA (тег v$KIT_VERSION), подпись проверяется ключом форка"
[[ $clean == yes ]] && pre "прошлая установка: уберу её перед установкой (--clean), SSH не трону" [[ $clean == yes ]] && pre "прошлая установка: уберу её перед установкой (--clean), SSH не трону"
if [[ $pick_sni == yes ]]; then pre "маскировка: просканирую подсеть и предложу выбрать сайт (--auto-sni — автоподбор)"
else pre "маскировка: подберу сайт сам, без вопросов (--auto-sni)"; fi
if ((${#INSTALLER_ARGS[@]})); then pre "установщик автора: bash scripts/3x-ui.sh ${INSTALLER_ARGS[*]}"; else pre "установщик автора: bash scripts/3x-ui.sh — спросит про маскировку и домен"; fi if ((${#INSTALLER_ARGS[@]})); then pre "установщик автора: bash scripts/3x-ui.sh ${INSTALLER_ARGS[*]}"; else pre "установщик автора: bash scripts/3x-ui.sh — спросит про маскировку и домен"; fi
if [[ $harden == auto ]]; then if [[ $harden == auto ]]; then
if interactive; then pre "защита: спрошу про ответ на ping и автообновления" if interactive; then pre "защита: спрошу про ответ на ping и автообновления"
@@ -397,6 +403,10 @@ fi
[[ $clean == yes ]] && clean_previous [[ $clean == yes ]] && clean_previous
# Меню выбора сайта маскировки: сканируем подсеть один раз и предлагаем выбрать сайт отдельно для
# REALITY и (если выбраны) для XHTTP/MTProto. Отключается флагом --auto-sni (тогда автоподбор автора).
if [[ $pick_sni == yes ]]; then export KL_PICK_SNI=1; else export KL_PICK_SNI=0; fi
say "запускаю установщик автора (панель 3X-UI, протоколы, сайт-заглушка, подписка)" say "запускаю установщик автора (панель 3X-UI, протоколы, сайт-заглушка, подписка)"
if ((${#INSTALLER_ARGS[@]})); then printf ' флаги: %s\n' "${INSTALLER_ARGS[*]}"; else printf ' флагов нет: установщик задаст вопросы (маскировка, домен)\n'; fi if ((${#INSTALLER_ARGS[@]})); then printf ' флаги: %s\n' "${INSTALLER_ARGS[*]}"; else printf ' флагов нет: установщик задаст вопросы (маскировка, домен)\n'; fi
# stdin: если есть терминал — отдаём его установщику (там есть вопросы), иначе /dev/null, # stdin: если есть терминал — отдаём его установщику (там есть вопросы), иначе /dev/null,
+80 -5
View File
@@ -30,7 +30,7 @@ wait_apt_idle() {
XUI_VERSION="v3.9.0" XUI_VERSION="v3.9.0"
# SHA256 установщика 3X-UI этой версии: тег могут передвинуть, а хеш – нет (проверено 2026-09-30). # SHA256 установщика 3X-UI этой версии: тег могут передвинуть, а хеш – нет (проверено 2026-09-30).
XUI_INSTALL_SHA256="18616fe26c8f6c92db6daa2dcd7cd53c5143ee69ecfd26d8ea6dc9b2c78607a6" XUI_INSTALL_SHA256="18616fe26c8f6c92db6daa2dcd7cd53c5143ee69ecfd26d8ea6dc9b2c78607a6"
KIT_VERSION="1.2.13" KIT_VERSION="1.2.14"
# kit и kit-sub берём из того же релиза, что и этот скрипт, а не из меняющейся ветки main. # kit и kit-sub берём из того же релиза, что и этот скрипт, а не из меняющейся ветки main.
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$KIT_VERSION" KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$KIT_VERSION"
# Ядро Xray для панели. С 26.7.x клиенты на Mihomo и sing-box (Hiddify, FlClash, # Ядро Xray для панели. С 26.7.x клиенты на Mihomo и sing-box (Hiddify, FlClash,
@@ -50,7 +50,8 @@ RESULT=/root/3x-ui.txt
XUI_ENV=/etc/x-ui/install-result.env XUI_ENV=/etc/x-ui/install-result.env
# Сайты для маскировки REALITY: нужны TLS 1.3 и HTTP/2. Берём первый доступный. # Сайты для маскировки REALITY: нужны TLS 1.3 и HTTP/2. Берём первый доступный.
# Apple, iCloud, Microsoft и домены .ru сам Xray не советует – их тут нет. # Apple, iCloud, Microsoft и домены .ru сам Xray не советует – их тут нет.
SNI_CANDIDATES=(dl.google.com www.amazon.com www.samsung.com www.yahoo.com) # Наш шов: добавлены известные сайты (Microsoft, AMD, Intel, NVIDIA) — их можно выбрать вручную.
SNI_CANDIDATES=(dl.google.com www.amazon.com www.samsung.com www.yahoo.com www.microsoft.com www.amd.com www.intel.com www.nvidia.com)
ALL_PROTOS=(reality reality2 hy2 xhttp ws trojan vmess ss tuic wg awg awg3 mtproto) ALL_PROTOS=(reality reality2 hy2 xhttp ws trojan vmess ss tuic wg awg awg3 mtproto)
# Обычный WireGuard легко распознаётся сетевым оборудованием и работает нестабильно # Обычный WireGuard легко распознаётся сетевым оборудованием и работает нестабильно
@@ -537,6 +538,57 @@ main() {
later "Адрес $HOST не найден на сетевых интерфейсах сервера (бывает за NAT). Если ссылки не работают – запустите с --host и верным IP." later "Адрес $HOST не найден на сетевых интерфейсах сервера (бывает за NAT). Если ссылки не работают – запустите с --host и верным IP."
fi fi
# ---------------------------------------------------------------------------
# Наш шов: выбор сайта маскировки по результатам скана подсети.
# Печатает выбранное имя в stdout (пусто — оставляем автоподбор автора).
# Роль: reality (только найденные и ручной ввод) или xhttp (плюс известные сайты).
kl_sni_menu() {
local role=$1; shift
local -a found=("$@")
local -a known=("${SNI_CANDIDATES[@]}")
local src=${KL_ASK_SRC:-/dev/tty} ans i n try
[[ $role == xhttp ]] || known=() # REALITY маскируем только под соседа или введённый вручную
printf '\n%s%s%s\n' "$B" "$role" "$N" >&2
if ((${#found[@]})); then
printf ' найдено в вашей подсети (TLS 1.3, h2, сертификат верный):\n' >&2
for i in "${!found[@]}"; do printf ' %2d) %s\n' "$((i + 1))" "${found[i]}" >&2; done
else
printf ' в подсети подходящих сайтов не нашлось\n' >&2
fi
if [[ $role == xhttp ]]; then
n=${#found[@]}
printf ' известные сайты (заметнее, чем соседи по подсети):\n' >&2
for i in "${!known[@]}"; do printf ' %2d) %s\n' "$((i + 1 + n))" "${known[i]}" >&2; done
fi
printf ' 0) ввести адрес сайта вручную\n' >&2
printf ' Enter — оставить автоподбор. Выбор: ' >&2
for try in 1 2 3; do
ans=""
read -r -t 300 ans <"$src" 2>/dev/null || true
ans=${ans//[[:space:]]/}
[[ -z $ans ]] && return 0
if [[ $ans == 0 ]]; then
printf ' адрес сайта (например vpn.example.com): ' >&2
ans=""
read -r -t 300 ans <"$src" 2>/dev/null || true
ans=${ans//[[:space:]]/}
if [[ $ans =~ ^[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then printf '%s' "$ans"; return 0; fi
printf ' не похоже на адрес сайта\n' >&2
elif [[ $ans =~ ^[0-9]+$ ]]; then
local total=${#found[@]}
((total)) && known=() # для reality известные не предлагаются
local idx=$((10#$ans))
if ((idx >= 1 && idx <= total)); then printf '%s' "${found[idx - 1]}"; return 0; fi
if ((idx > total && idx <= total + ${#known[@]})); then printf '%s' "${known[idx - total - 1]}"; return 0; fi
printf ' такого номера нет\n' >&2
else
printf ' не понял «%s» — введите номер или 0\n' "$ans" >&2
fi
printf ' ещё раз (Enter — автоподбор): ' >&2
done
return 0
}
# Маскировка: из флагов, по вопросу пользователю или автоматически (сосед по подсети). # Маскировка: из флагов, по вопросу пользователю или автоматически (сосед по подсети).
if [[ -n $DOMAIN ]]; then if [[ -n $DOMAIN ]]; then
[[ $PANEL_SSL == ip ]] || die "Свой домен работает с сертификатом панели Let's Encrypt на IP: освободите порт 80 и не указывайте --cert, --key и --panel-ssl none (сертификат для домена установщик получит сам)." [[ $PANEL_SSL == ip ]] || die "Свой домен работает с сертификатом панели Let's Encrypt на IP: освободите порт 80 и не указывайте --cert, --key и --panel-ssl none (сертификат для домена установщик получит сам)."
@@ -548,12 +600,35 @@ main() {
SNI=$DOMAIN SNI=$DOMAIN
elif [[ -z $SNI ]]; then elif [[ -z $SNI ]]; then
echo echo
echo "Подбираю сайт для маскировки среди соседей по подсети… (около 250 коротких обращений к порту 443 соседних адресов, около минуты; не нужно – запустите с --sni или --domain)" echo "Сканирую подсеть: около 250 коротких обращений к порту 443 соседних адресов (около минуты)."
echo "По результатам предложу сайты для маскировки. Не нужно – запустите с --sni или --domain."
local -a nb=() local -a nb=()
mapfile -t nb < <(nearby_sites | shuf) mapfile -t nb < <(nearby_sites | shuf)
NB_ALL=("${nb[@]}") NB_ALL=("${nb[@]}")
if ((${#nb[@]})); then # Наш шов: показываем найденное и даём выбрать — отдельно для REALITY и для XHTTP/MTProto.
SNI=${nb[0]}; SNI2=${nb[1]:-}; SNI3=${nb[2]:-} if [[ ${KL_PICK_SNI:-} == 1 && $yes == no ]] && { : </dev/tty; } 2>/dev/null; then
local pick pick2
pick=$(kl_sni_menu "REALITY (TCP 443) — под какой сайт маскировать" ${nb[@]+"${nb[@]}"})
if ((${#nb[@]})); then
[[ -n $pick ]] && SNI=$pick || SNI=${nb[0]}
elif [[ -n $pick ]]; then
SNI=$pick
fi
if [[ -n $SNI ]]; then
if ! sni_ok "$SNI"; then
warn "$SNI не отвечает по TLS 1.3 + HTTP/2 — беру найденное в подсети"
SNI="${nb[0]:-$SNI}"
fi
ok "маскировка REALITY: ${B}$SNI${N}"
fi
if [[ " ${PROTOS[*]} " == *" xhttp "* || " ${PROTOS[*]} " == *" mtproto "* ]]; then
pick2=$(kl_sni_menu "XHTTP и MTProto (различаются по имени сайта) — какой сайт взять" ${nb[@]+"${nb[@]}"})
[[ -n $pick2 ]] && SNI2=$pick2
[[ -n $SNI2 ]] && ok "маскировка XHTTP/MTProto: ${B}$SNI2${N}"
fi
fi
if [[ -z $SNI ]] && ((${#nb[@]})); then
SNI=${nb[0]}; SNI2=${SNI2:-${nb[1]:-}}; SNI3=${nb[2]:-}
ok "найден: ${B}$SNI${N} · та же подсеть · TLS 1.3, h2, сертификат верный" ok "найден: ${B}$SNI${N} · та же подсеть · TLS 1.3, h2, сертификат верный"
else else
warn "В подсети подходящего сайта не нашёл." warn "В подсети подходящего сайта не нашёл."
+1 -1
View File
@@ -8,7 +8,7 @@
# Код выхода 1 при любом запуске (и при `__update_binary`) – старый `hy2 update` на нём # Код выхода 1 при любом запуске (и при `__update_binary`) – старый `hy2 update` на нём
# останавливается и не заменяет команду hy2. # останавливается и не заменяет команду hy2.
KIT_VERSION="1.2.13" KIT_VERSION="1.2.14"
cat >&2 <<'MSG' cat >&2 <<'MSG'
Отдельный установщик Hysteria2 больше не поддерживается. Отдельный установщик Hysteria2 больше не поддерживается.
+8 -4
View File
@@ -932,6 +932,10 @@ kit_local_uninstall() {
esac esac
# Службы: и явный список, и всё, что найдётся по нашим шаблонам (чтобы новые юниты kit не оставались). # Службы: и явный список, и всё, что найдётся по нашим шаблонам (чтобы новые юниты kit не оставались).
# kit запускает наши команды с `set -e`: при уборке это недопустимо — любая «не найденная» служба
# или пустой grep обрывали бы снос на середине (так и было: файлы оставались). Убираем errexit.
set +e
local -a svc=(x-ui.service kit-sub.service kit-update.timer kit-update.service kit-limit.timer kit-limit.service) local -a svc=(x-ui.service kit-sub.service kit-update.timer kit-update.service kit-limit.timer kit-limit.service)
local u local u
for u in $(systemctl list-unit-files --no-legend 2>/dev/null | awk '{print $1}' \ for u in $(systemctl list-unit-files --no-legend 2>/dev/null | awk '{print $1}' \
@@ -988,8 +992,8 @@ kit_local_uninstall() {
done done
if kl_have nginx; then if kl_have nginx; then
local others local others
others=$(find /etc/nginx/conf.d /etc/nginx/sites-enabled /etc/nginx/streams-enabled -mindepth 1 2>/dev/null \ others=$( { find /etc/nginx/conf.d /etc/nginx/sites-enabled /etc/nginx/streams-enabled -mindepth 1 2>/dev/null || true; } \
| grep -vE 'kit\.conf$|(^|/)default$' | wc -l) | { grep -vE 'kit\.conf$|(^|/)default$' || true; } | wc -l)
if [[ $others -eq 0 ]]; then if [[ $others -eq 0 ]]; then
systemctl disable --now nginx >/dev/null 2>&1 && kl_ok "nginx остановлен (чужих сайтов в нём не было)" systemctl disable --now nginx >/dev/null 2>&1 && kl_ok "nginx остановлен (чужих сайтов в нём не было)"
KL_NGINX_PURGE=yes KL_NGINX_PURGE=yes
@@ -1056,8 +1060,8 @@ kit_local_uninstall() {
| grep -E '^(x-ui|kit-|kit\.)' | grep -E '\.(service|timer)$'); do | grep -E '^(x-ui|kit-|kit\.)' | grep -E '\.(service|timer)$'); do
kl_warn "осталась наша служба: $s"; left=1 kl_warn "осталась наша служба: $s"; left=1
done done
found=$(ls -d /etc/kit* /usr/local/lib/kit* /usr/local/bin/kit /etc/cron.d/kit-* /var/www/kit \ found=$( { ls -d /etc/kit* /usr/local/lib/kit* /usr/local/bin/kit /etc/cron.d/kit-* /var/www/kit \
/root/3x-ui.txt /root/cert /etc/x-ui /usr/local/x-ui 2>/dev/null | head -20) /root/3x-ui.txt /root/cert /etc/x-ui /usr/local/x-ui 2>/dev/null || true; } | head -20)
[[ -z $found ]] || { kl_warn "осталось на диске:"; printf ' %s\n' $found; left=1; } [[ -z $found ]] || { kl_warn "осталось на диске:"; printf ' %s\n' $found; left=1; }
ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -qE '[:.]443$' && { kl_warn "порт 443 всё ещё кем-то слушается"; left=1; } ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -qE '[:.]443$' && { kl_warn "порт 443 всё ещё кем-то слушается"; left=1; }
if ((left)); then if ((left)); then
+1 -1
View File
@@ -12,7 +12,7 @@
set -Eeuo pipefail set -Eeuo pipefail
export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам
KIT_VERSION="1.2.13" KIT_VERSION="1.2.14"
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main" KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main"
# Файлы новой версии берём из её тега, а не из меняющейся ветки main. # Файлы новой версии берём из её тега, а не из меняющейся ветки main.
kit_ref_raw() { echo "https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$1"; } kit_ref_raw() { echo "https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$1"; }
+28
View File
@@ -261,6 +261,34 @@ def main():
' # Наш шов: подписки «-awg»/«-tg» есть только у установок с AmneziaWG и MTProto — не тянем их,\n # если эти протоколы не выбраны (никаких лишних обращений к панели).\n if [[ " ${PROTOS[*]} " == *" awg "* || " ${PROTOS[*]} " == *" awg3 "* || " ${PROTOS[*]} " == *" mtproto "* ]]; then\n extra=$(sub_links "$SUBID-awg" 1; sub_links "$SUBID-tg" 1)\n fi', ' # Наш шов: подписки «-awg»/«-tg» есть только у установок с AmneziaWG и MTProto — не тянем их,\n # если эти протоколы не выбраны (никаких лишних обращений к панели).\n if [[ " ${PROTOS[*]} " == *" awg "* || " ${PROTOS[*]} " == *" awg3 "* || " ${PROTOS[*]} " == *" mtproto "* ]]; then\n extra=$(sub_links "$SUBID-awg" 1; sub_links "$SUBID-tg" 1)\n fi',
'не тянуть legacy-подписки awg/tg, если протоколы не выбраны', 'не тянуть legacy-подписки awg/tg, если протоколы не выбраны',
), ),
(
'scripts/3x-ui.sh',
'SNI_CANDIDATES=(dl.google.com www.amazon.com www.samsung.com www.yahoo.com)',
False,
'# Наш шов: добавлены известные сайты (Microsoft, AMD, Intel, NVIDIA) — их можно выбрать вручную.\nSNI_CANDIDATES=(dl.google.com www.amazon.com www.samsung.com www.yahoo.com www.microsoft.com www.amd.com www.intel.com www.nvidia.com)',
'список известных сайтов расширен',
),
(
'scripts/3x-ui.sh',
' # Маскировка: из флагов, по вопросу пользователю или автоматически (сосед по подсети).',
False,
'# ---------------------------------------------------------------------------\n# Наш шов: выбор сайта маскировки по результатам скана подсети.\n# Печатает выбранное имя в stdout (пусто — оставляем автоподбор автора).\n# Роль: reality (только найденные и ручной ввод) или xhttp (плюс известные сайты).\nkl_sni_menu() {\n local role=$1; shift\n local -a found=("$@")\n local -a known=("${SNI_CANDIDATES[@]}")\n local src=${KL_ASK_SRC:-/dev/tty} ans i n try\n [[ $role == xhttp ]] || known=() # REALITY маскируем только под соседа или введённый вручную\n printf \'\\n%s%s%s\\n\' "$B" "$role" "$N" >&2\n if ((${#found[@]})); then\n printf \' найдено в вашей подсети (TLS 1.3, h2, сертификат верный):\\n\' >&2\n for i in "${!found[@]}"; do printf \' %2d) %s\\n\' "$((i + 1))" "${found[i]}" >&2; done\n else\n printf \' в подсети подходящих сайтов не нашлось\\n\' >&2\n fi\n if [[ $role == xhttp ]]; then\n n=${#found[@]}\n printf \' известные сайты (заметнее, чем соседи по подсети):\\n\' >&2\n for i in "${!known[@]}"; do printf \' %2d) %s\\n\' "$((i + 1 + n))" "${known[i]}" >&2; done\n fi\n printf \' 0) ввести адрес сайта вручную\\n\' >&2\n printf \' Enter — оставить автоподбор. Выбор: \' >&2\n for try in 1 2 3; do\n ans=""\n read -r -t 300 ans <"$src" 2>/dev/null || true\n ans=${ans//[[:space:]]/}\n [[ -z $ans ]] && return 0\n if [[ $ans == 0 ]]; then\n printf \' адрес сайта (например vpn.example.com): \' >&2\n ans=""\n read -r -t 300 ans <"$src" 2>/dev/null || true\n ans=${ans//[[:space:]]/}\n if [[ $ans =~ ^[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$ ]]; then printf \'%s\' "$ans"; return 0; fi\n printf \' не похоже на адрес сайта\\n\' >&2\n elif [[ $ans =~ ^[0-9]+$ ]]; then\n local total=${#found[@]}\n ((total)) && known=() # для reality известные не предлагаются\n local idx=$((10#$ans))\n if ((idx >= 1 && idx <= total)); then printf \'%s\' "${found[idx - 1]}"; return 0; fi\n if ((idx > total && idx <= total + ${#known[@]})); then printf \'%s\' "${known[idx - total - 1]}"; return 0; fi\n printf \' такого номера нет\\n\' >&2\n else\n printf \' не понял «%s» — введите номер или 0\\n\' "$ans" >&2\n fi\n printf \' ещё раз (Enter — автоподбор): \' >&2\n done\n return 0\n}\n\n # Маскировка: из флагов, по вопросу пользователю или автоматически (сосед по подсети).',
'меню выбора сайта маскировки (наш код)',
),
(
'scripts/3x-ui.sh',
' echo\n echo "Подбираю сайт для маскировки среди соседей по подсети… (около 250 коротких обращений к порту 443 соседних адресов, около минуты; не нужно – запустите с --sni или --domain)"',
False,
' echo\n echo "Сканирую подсеть: около 250 коротких обращений к порту 443 соседних адресов (около минуты)."\n echo "По результатам предложу сайты для маскировки. Не нужно – запустите с --sni или --domain."\n local -a nb=()\n mapfile -t nb < <(nearby_sites | shuf)\n NB_ALL=("${nb[@]}")\n',
'скан + выбор сайта вместо автоподбора',
),
(
'scripts/3x-ui.sh',
' SNI=${nb[0]}; SNI2=${nb[1]:-}; SNI3=${nb[2]:-}\n ok "найден: ${B}$SNI${N} · та же подсеть · TLS 1.3, h2, сертификат верный"',
False,
' SNI=${nb[0]}; SNI2=${SNI2:-${nb[1]:-}}; SNI3=${nb[2]:-}\n ok "найден: ${B}$SNI${N} · та же подсеть · TLS 1.3, h2, сертификат верный"',
'не перетирать выбранный SNI2',
),
] ]
bad = 0 bad = 0