Установщик: «рекомендуем» у своего домена, без «mihomo» в строке о проекте, без ссылок «открыть в приложении» и без деления протоколов на группы; обновление панели проверяет подключения клиентом и откатывает при сбое; README: отдельная Hysteria2 наравне с 11 протоколами

This commit is contained in:
maxdmitrov
2026-10-05 18:01:28 +03:00
parent 97718bd3e6
commit 31bc2d5480
4 changed files with 46 additions and 40 deletions
+16 -4
View File
@@ -38,6 +38,7 @@
## Возможности
- ⚡ **Или только Hysteria2:** отдельный лёгкий сервер одной командой, без панели ([вариант 2](#вариант-2-только-hysteria2)).
- 🧩 **11 протоколов сразу** – VLESS REALITY, XHTTP, WebSocket, Trojan gRPC, VMess,
Shadowsocks 2022, Hysteria2, TUIC, AmneziaWG (классика и 3.1) и MTProto для Telegram.
- 🚪 **Всё TCP – через порт 443.** Панель, подписка и протоколы спрятаны за одним портом,
@@ -67,17 +68,28 @@
## Установка
Подключитесь к серверу по SSH и выполните:
Подключитесь к серверу по SSH и выберите, что нужно. Оба варианта ставятся одной командой на чистый VPS.
### Вариант 1. Сервер с 11 протоколами
Панель 3X-UI, все протоколы на порту 443 и одна подписка на всё:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main/scripts/3x-ui.sh)
```
Через пару минут скрипт покажет адрес панели, логин, пароль и подписку с QR-кодом.
Подробно – подключение приложений, дополнительные пользователи и параметры – в **[инструкции](manuals/3x-ui.md)**.
Подробно – подключение приложений, дополнительные пользователи и параметры –
в **[инструкции](manuals/3x-ui.md)**. Нужен только Hysteria2 – есть
[отдельный скрипт](manuals/hysteria2.md).
### Вариант 2. Только Hysteria2
Лёгкий отдельный сервер: один протокол (UDP), свой сертификат или Let's Encrypt с доменом, сайт-заглушка, готовая ссылка и команда `hy2`:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main/scripts/hysteria2.sh)
```
Подробно – в **[инструкции по Hysteria2](manuals/hysteria2.md)**.
> [!WARNING]
> Проект создан в образовательных целях. Убедитесь, что ваши действия
+1 -2
View File
@@ -187,8 +187,7 @@ kit net panel domain # панель и подписка открываю
Лучший сайт маскировки – ваш собственный домен (`--domain` при установке): сервер изображает ваш сайт, а сертификат
и имя совпадают. Без домена установщик подбирает сайт среди соседей по подсети сервера: около 250 коротких обращений к порту 443
соседних адресов (не нужно – укажите `--sni сайт` или `--domain`). Основные протоколы – REALITY, XHTTP и Hysteria2; остальные запасные, их можно выключить командой
`kit net off`.
соседних адресов (не нужно – укажите `--sni сайт` или `--domain`). Любой протокол можно выключить командой `kit net off имя` и включить обратно `kit net on имя`.
## Резервная копия
+3 -12
View File
@@ -231,7 +231,7 @@ choose_masking() {
echo "${B}Под какой сайт маскировать сервер?${N}"
echo "Со стороны он будет выглядеть как обычный сайт."
echo
echo " ${B}1${N} Свой домен самый надёжный: нужен домен и свободный порт 80"
echo " ${B}1${N} Свой домен самый надёжный: нужен домен и свободный порт 80 ${G}[рекомендуем]${N}"
echo " ${B}2${N} Сосед по подсети подберу сам, домен не нужен ${G}[рекомендуем]${N}"
echo " ${B}3${N} Свой сайт вы вводите адрес сами"
echo " ${D}Сертификат своего домена виден в публичных журналах сертификатов: связь «домен – сервер» не скрыта.${N}"
@@ -382,7 +382,7 @@ ART
printf '%s' "$N"
echo
echo "${B}3X-UI KIT $KIT_VERSION${N} · it's not Kubrick. it's just a VPN."
echo "${D}на основе панели 3X-UI (MHSanaei/3x-ui), ядра Xray и mihomo · github.com/itsnotkubrick/3X-UI_KIT${N}"
echo "${D}на основе панели 3X-UI (MHSanaei/3x-ui) и ядра Xray · github.com/itsnotkubrick/3X-UI_KIT${N}"
}
main() {
@@ -790,21 +790,12 @@ main() {
echo " Вставьте в Happ, Hiddify, Karing, v2rayN, Clash Verge или FlClash."
qrencode -t ANSIUTF8 -m 1 "$SUB_URL" || true
echo
/usr/local/bin/kit __applinks "$SUB_URL" || true
echo
KIT_NO_QR=1 /usr/local/bin/kit __links "$NAME" "$SUBID" || true
else
/usr/local/bin/kit __links "$NAME" "$SUBID" || true
fi
echo
local main_list="" spare_list="" c
for c in "${CREATED[@]}"; do
case $c in REALITY | XHTTP | Hysteria2) main_list+="$c, " ;; *) spare_list+="$c, " ;; esac
done
echo "${B}ПРОТОКОЛЫ${N}"
echo " Основные: ${main_list%, }"
echo " Запасные: ${spare_list%, }"
echo " Порты и выключение лишнего: ${B}kit net${N}"
echo "${D}Порты, выключение и включение протоколов: ${N}${B}kit net${N}"
if ((${#WARNINGS[@]})); then
echo
echo "${B}ВНИМАНИЕ${N}"
+26 -22
View File
@@ -934,6 +934,14 @@ print(json.dumps({"log": {"loglevel": "none"},
PY
}
# Подключения клиентом, только итог (для проверки до и после обновления панели): 0 – всё подключается.
deep_probe() {
CHECK_BAD=0; CHECK_WARN=0; CHECK_QUIET=yes
deep_clients >/dev/null 2>&1 || true
CHECK_QUIET=no
((CHECK_BAD == 0))
}
deep_clients() {
local xray sid link name cfg port=18080 pid ok tmp hypin
hypin=$(deep_hy_pin)
@@ -1144,6 +1152,9 @@ panel_update() {
tar xzf "$tmp/x-ui.tar.gz" -C "$tmp"
[[ -s $tmp/x-ui/x-ui ]] || die "В архиве панели нет x-ui. Сервер не тронут."
# Подключения клиентом до обновления: если они работали, после обновления должны работать так же, иначе вернём прежнюю панель.
local pre=no
deep_probe && pre=ok
# 2. Копия на случай отката (база снимается средствами SQLite).
ts=$(date +%Y%m%d-%H%M); bak=/root/x-ui-before-update-$ts
install -d -m 700 "$bak/etc-x-ui"
@@ -1186,14 +1197,15 @@ PY
systemctl start x-ui
# 4. Проверка и откат при провале.
if panel_healthy && [[ $(/usr/local/x-ui/x-ui -v 2>/dev/null | head -1) == "$target" ]]; then
if panel_healthy && [[ $(/usr/local/x-ui/x-ui -v 2>/dev/null | head -1) == "$target" ]] && { [[ $pre != ok ]] || { sleep 5; deep_probe; }; }; then
systemctl is-active -q x-ui || true
say "Панель обновлена до $target, ядро Xray ${XRAY_PIN#v} осталось."
[[ $pre == ok ]] && say "Подключения клиентом после обновления работают (REALITY, XHTTP, Hysteria2)."
echo "Копию прежней панели можно удалить, когда убедитесь, что всё работает: rm -rf $bak"
echo "Проверить сервер: ${B}kit check${N}"
return 0
fi
warn "Панель после обновления не отвечает – возвращаю прежнюю."
warn "Панель после обновления не отвечает или подключения клиентом не работают – возвращаю прежнюю."
systemctl stop x-ui || true
rm -rf /usr/local/x-ui && cp -a "$bak/usr-local-x-ui" /usr/local/x-ui
cp -a "$bak/etc-x-ui/." /etc/x-ui/
@@ -1640,22 +1652,16 @@ def sch_net_ok(label, net):
return label not in ("Shadowsocks",)
print("ПОДКЛЮЧЕНИЕ · %s" % name)
print()
main = [i for m in MAIN for i in items if i[0] == m]
ORDER = ["REALITY-2", "VLESS-WS", "Trojan-gRPC", "VMess-WS", "Shadowsocks", "TUIC", "AmneziaWG", "AmneziaWG 3.1", "MTProto"]
spare = [i for i in items if i[0] not in MAIN]
spare.sort(key=lambda i: ORDER.index(i[0]) if i[0] in ORDER else len(ORDER))
ORDER = ["REALITY", "XHTTP", "Hysteria2", "REALITY-2", "VLESS-WS", "Trojan-gRPC", "VMess-WS", "Shadowsocks", "TUIC", "AmneziaWG", "AmneziaWG 3.1", "MTProto"]
items.sort(key=lambda i: ORDER.index(i[0]) if i[0] in ORDER else len(ORDER))
qr_done = False
for title, group in (("Основные", main), ("Запасные", spare)):
if not group:
continue
print(title)
for it in group:
show(*it)
if not qr_done and it[0] == "REALITY" and shutil.which("qrencode") and not os.environ.get("KIT_NO_QR"):
sys.stdout.flush()
subprocess.run(["qrencode", "-t", "ANSIUTF8", "-m", "1", it[4]])
print()
qr_done = True
for it in items:
show(*it)
if not qr_done and it[0] == "REALITY" and shutil.which("qrencode") and not os.environ.get("KIT_NO_QR"):
sys.stdout.flush()
subprocess.run(["qrencode", "-t", "ANSIUTF8", "-m", "1", it[4]])
print()
qr_done = True
PY
python3 -c "$script" "$name" <<<"$raw"
}
@@ -1967,14 +1973,12 @@ inbound_patch() { # id фильтр-jq [аргументы jq...]: меняет
}
net_show() {
local list name port proto listen en sni fp shown_reality2=no mark note group fpl=""
local list name port proto listen en sni fp shown_reality2=no mark note fpl=""
list=$(api GET inbounds/list)
printf '%s %s %s %s\n' "$(padr Протоколы 22)" "$(padr Порт 12)" "$(padr Статус 8)" "Заметка"
for group in main spare; do
if [[ $group == main ]]; then echo "Основные"; else echo "Запасные"; fi
{
while IFS='|' read -r name port proto listen en sni fp; do
[[ -n $name ]] || continue
if net_main_name "$name"; then [[ $group == main ]] || continue; else [[ $group == spare ]] || continue; fi
[[ $name == REALITY-2 ]] && shown_reality2=yes
if [[ $listen == 127.0.0.1 ]]; then mark="443/tcp"; else mark="$port/$(port_net "$proto")"; fi
[[ $(port_net "$proto") == both ]] && mark=$port
@@ -1985,7 +1989,7 @@ net_show() {
done < <(jq -r '.[] | (.streamSettings | if type == "string" then fromjson else . end) as $s
| [.remark, .port, .protocol, .listen, .enable, ($s.realitySettings.serverNames[0] // ""),
($s.realitySettings.settings.fingerprint // $s.tlsSettings.settings.fingerprint // "")] | map(tostring) | join("|")' <<<"$list")
done
}
[[ $shown_reality2 == yes ]] || printf ' %s %s %s %s\n' "$(padr REALITY-2 20)" "$(padr – 12)" "$(padr выкл 8)" "включить: kit net on reality2"
echo
echo "Отпечаток клиента: ${fpl:-?} (сменить: kit net fp firefox)"