v1.2.15: меню 9/10/11, снос с добивкой, меню маскировки как просил, быстрый скан, швы пересобраны (63 шва, воспроизведение побайтово)
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
KIT_VERSION="1.2.14" # версия сборки форка; должна совпадать с VERSION релиза
|
||||
KIT_VERSION="1.2.15" # версия сборки форка; должна совпадать с VERSION релиза
|
||||
GITEA="${KIT_FORK_BASE:-https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS}"
|
||||
NS="3x-ui-kit-release"
|
||||
SIG_ID="releases@3x-ui-kit"
|
||||
|
||||
+59
-39
@@ -30,7 +30,7 @@ wait_apt_idle() {
|
||||
XUI_VERSION="v3.9.0"
|
||||
# SHA256 установщика 3X-UI этой версии: тег могут передвинуть, а хеш – нет (проверено 2026-09-30).
|
||||
XUI_INSTALL_SHA256="18616fe26c8f6c92db6daa2dcd7cd53c5143ee69ecfd26d8ea6dc9b2c78607a6"
|
||||
KIT_VERSION="1.2.14"
|
||||
KIT_VERSION="1.2.15"
|
||||
# kit и kit-sub берём из того же релиза, что и этот скрипт, а не из меняющейся ветки main.
|
||||
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$KIT_VERSION"
|
||||
# Ядро Xray для панели. С 26.7.x клиенты на Mihomo и sing-box (Hiddify, FlClash,
|
||||
@@ -164,26 +164,32 @@ same_net() { # имя
|
||||
# Имена из сертификатов соседних адресов /24: заходим на 443 без имени и читаем subjectAltName.
|
||||
nearby_scan() { # основа "a.b.c"
|
||||
local me; me=$(host_ip)
|
||||
seq 1 254 | xargs -P 8 -I{} bash -c '
|
||||
# Наш шов: 32 потока вместо 8 и таймаут 3 с — скан подсети занимает секунды, а не минуту.
|
||||
seq 1 254 | xargs -P 32 -I{} bash -c '
|
||||
ip=$1; [ "$ip" = "$2" ] && exit 0
|
||||
echo | timeout 4 openssl s_client -connect "$ip:443" -tls1_3 -alpn h2 2>/dev/null | openssl x509 -noout -ext subjectAltName 2>/dev/null \
|
||||
| tr "," "\n" | sed -n "s/^ *DNS://p"' _ "$1.{}" "$me" 2>/dev/null | sort -u | grep -E "$SNI_RE" | head -40 || true
|
||||
echo | timeout 3 openssl s_client -connect "$ip:443" -tls1_3 -alpn h2 2>/dev/null | openssl x509 -noout -ext subjectAltName 2>/dev/null \
|
||||
| tr "," "\n" | sed -n "s/^ *DNS://p"' _ "$1.{}" "$me" 2>/dev/null | sort -u | grep -E "$SNI_RE" | head -60 || true
|
||||
}
|
||||
|
||||
# Подходящие сайты: сначала в /24 сервера, если пусто – во втором /24 того же /23. До 8 имён.
|
||||
nearby_sites() {
|
||||
local me a b c base n names found=0
|
||||
local me a b c base n names out
|
||||
set +e; trap - ERR # выполняется в подпроцессе: сбой одной проверки не должен обрывать поиск
|
||||
me=$(host_ip)
|
||||
[[ $me =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || return 0
|
||||
IFS=. read -r a b c _ <<<"$me"
|
||||
# Наш шов: имена проверяются параллельно (8 сразу) — раньше по очереди, и это была основная
|
||||
# причина «долго сканирует». Функции передаём в подпроцессы через export -f.
|
||||
export -f same_net sni_alive sni_trusted sni_quality sni_ok host_ip
|
||||
export HOST
|
||||
for base in "$a.$b.$c" "$a.$b.$((c ^ 1))"; do
|
||||
names=$(nearby_scan "$base")
|
||||
for n in $names; do
|
||||
[[ $n == "$me" || $n =~ $SNI_DYN_RE || $n =~ $SNI_BRAND_RE || $n =~ $SNI_BAD_RE ]] && continue
|
||||
if same_net "$n" && sni_alive "$n" && sni_trusted "$n" && sni_quality "$n"; then echo "$n"; found=$((found + 1)); ((found >= 8)) && return 0; fi
|
||||
done
|
||||
((found > 0)) && return 0
|
||||
names=$(nearby_scan "$base" | grep -vE "^(${me})$" || true)
|
||||
[[ -n $names ]] || continue
|
||||
out=$(while IFS= read -r n; do
|
||||
[[ $n =~ $SNI_DYN_RE || $n =~ $SNI_BRAND_RE || $n =~ $SNI_BAD_RE ]] && continue
|
||||
printf '%s\n' "$n"
|
||||
done <<<"$names" | xargs -P 8 -I{} bash -c 'same_net "$1" && sni_alive "$1" && sni_trusted "$1" && sni_quality "$1" && printf "%s\n" "$1"' _ {} 2>/dev/null | head -8)
|
||||
[[ -n $out ]] && { printf '%s\n' "$out"; return 0; }
|
||||
done
|
||||
return 0
|
||||
}
|
||||
@@ -541,50 +547,60 @@ main() {
|
||||
# ---------------------------------------------------------------------------
|
||||
# Наш шов: выбор сайта маскировки по результатам скана подсети.
|
||||
# Печатает выбранное имя в stdout (пусто — оставляем автоподбор автора).
|
||||
# Роль: reality (только найденные и ручной ввод) или xhttp (плюс известные сайты).
|
||||
# role — подпись для человека: REALITY или XHTTP/MTProto.
|
||||
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 маскируем только под соседа или введённый вручную
|
||||
local src=${KL_ASK_SRC:-/dev/tty} ans i k n auto_idx known_idx own_idx try
|
||||
n=${#found[@]}
|
||||
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
|
||||
if ((n)); then
|
||||
printf 'Найдено в подсети:\n\n' >&2
|
||||
for i in "${!found[@]}"; do printf ' %d - %s\n' "$((i + 1))" "${found[i]}" >&2; done
|
||||
auto_idx=$((n + 1)); known_idx=$((n + 2)); own_idx=$((n + 3))
|
||||
printf ' %d - автовыбор из списка\n' "$auto_idx" >&2
|
||||
printf ' %d - выбрать общедоступный (например %s, %s)\n' "$known_idx" "${known[0]}" "${known[1]}" >&2
|
||||
printf ' %d - ввести свой домен\n\n' "$own_idx" >&2
|
||||
else
|
||||
printf ' в подсети подходящих сайтов не нашлось\n' >&2
|
||||
auto_idx=0; known_idx=1; own_idx=2
|
||||
printf 'В подсети доменов не найдено, выберите:\n\n' >&2
|
||||
printf ' 1 - общедоступный (например %s, %s)\n' "${known[0]}" "${known[1]}" >&2
|
||||
printf ' 2 - ввести свой домен\n\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
|
||||
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
|
||||
[[ $ans =~ ^[0-9]+$ ]] || { printf ' введите номер из списка\n' >&2; continue; }
|
||||
i=$((10#$ans))
|
||||
if ((n)) && ((i >= 1 && i <= n)); then printf '%s' "${found[i - 1]}"; return 0; fi
|
||||
((i == auto_idx)) && return 0
|
||||
if ((i == known_idx)); then
|
||||
printf '\nОбщедоступные сайты:\n' >&2
|
||||
for k in "${!known[@]}"; do printf ' %d - %s\n' "$((k + 1))" "${known[k]}" >&2; done
|
||||
printf ' 0 - назад\nВаш выбор: ' >&2
|
||||
ans=""
|
||||
read -r -t 300 ans <"$src" 2>/dev/null || true
|
||||
ans=${ans//[[:space:]]/}
|
||||
if [[ $ans =~ ^[0-9]+$ ]] && ((10#$ans >= 1 && 10#$ans <= ${#known[@]})); then
|
||||
printf '%s' "${known[10#$ans - 1]}"; return 0
|
||||
fi
|
||||
printf ' ничего не выбрано — оставляю автовыбор\n' >&2
|
||||
return 0
|
||||
fi
|
||||
if ((i == own_idx)); 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
|
||||
[[ $ans =~ ^[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]] && { printf '%s' "$ans"; return 0; }
|
||||
printf ' не похоже на адрес сайта — оставляю автовыбор\n' >&2
|
||||
return 0
|
||||
fi
|
||||
printf ' ещё раз (Enter — автоподбор): ' >&2
|
||||
printf ' такого номера нет\n' >&2
|
||||
done
|
||||
return 0
|
||||
}
|
||||
@@ -627,13 +643,17 @@ kl_sni_menu() {
|
||||
[[ -n $SNI2 ]] && ok "маскировка XHTTP/MTProto: ${B}$SNI2${N}"
|
||||
fi
|
||||
fi
|
||||
if ((${#nb[@]})); then ok "подходящих соседей найдено: ${#nb[@]}"
|
||||
else warn "в подсети подходящих сайтов не нашлось (адреса проверены, откликов нет)"; fi
|
||||
if [[ -z $SNI ]] && ((${#nb[@]})); then
|
||||
SNI=${nb[0]}; SNI2=${SNI2:-${nb[1]:-}}; SNI3=${nb[2]:-}
|
||||
ok "найден: ${B}$SNI${N} · та же подсеть · TLS 1.3, h2, сертификат верный"
|
||||
else
|
||||
warn "В подсети подходящего сайта не нашёл."
|
||||
echo " Лучший выход – свой домен: запустите снова с --domain vpn.example.com"
|
||||
if [[ $yes == no && -t 1 ]] && { : </dev/tty; } 2>/dev/null; then
|
||||
if [[ -n ${KL_PICK_SNI:-} && ${KL_PICK_SNI} == 1 && -z $SNI ]]; then
|
||||
: # сайт уже выбран в нашем меню — вопрос автора не нужен
|
||||
elif [[ $yes == no && -t 1 ]] && { : </dev/tty; } 2>/dev/null; then
|
||||
ask_tty " Продолжить с запасным сайтом из списка? [y/N] " || true
|
||||
[[ $REPLY =~ ^[yYдД]$ ]] || die "Остановился по вашей просьбе. Ставить можно снова в любой момент."
|
||||
else
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# Код выхода 1 при любом запуске (и при `__update_binary`) – старый `hy2 update` на нём
|
||||
# останавливается и не заменяет команду hy2.
|
||||
|
||||
KIT_VERSION="1.2.14"
|
||||
KIT_VERSION="1.2.15"
|
||||
|
||||
cat >&2 <<'MSG'
|
||||
Отдельный установщик Hysteria2 больше не поддерживается.
|
||||
|
||||
+16
-4
@@ -923,12 +923,13 @@ kit_local_cmd() {
|
||||
kit_local_uninstall() {
|
||||
kl_args "$@"
|
||||
local arg=${KL_REST[0]:-} ans found
|
||||
local reset_ssh=no
|
||||
local reset_ssh=no i=0
|
||||
case $arg in
|
||||
"") ;;
|
||||
--dry-run) KL_DRY=yes ;;
|
||||
--yes) KIT_LOCAL_UNINSTALL_CONFIRMED=yes ;;
|
||||
--reset-ssh) reset_ssh=yes ;;
|
||||
*) kl_die "kit local uninstall [--dry-run] [--reset-ssh]"; return 1 ;;
|
||||
*) kl_die "kit local uninstall [--dry-run] [--yes] [--reset-ssh]"; return 1 ;;
|
||||
esac
|
||||
|
||||
# Службы: и явный список, и всё, что найдётся по нашим шаблонам (чтобы новые юниты kit не оставались).
|
||||
@@ -1060,14 +1061,25 @@ kit_local_uninstall() {
|
||||
| grep -E '^(x-ui|kit-|kit\.)' | grep -E '\.(service|timer)$'); do
|
||||
kl_warn "осталась наша служба: $s"; left=1
|
||||
done
|
||||
# Добивка: убираем всё, что осталось и подходит под наши шаблоны (вдруг автор добавил новый файл
|
||||
# или что-то не удалилось с первого раза). Затем проверяем ещё раз.
|
||||
for f in /etc/kit* /usr/local/lib/kit* /usr/local/bin/kit /etc/cron.d/kit-* /var/www/kit \
|
||||
/etc/systemd/system/kit-*.service /etc/systemd/system/kit-*.timer \
|
||||
/etc/systemd/system/x-ui*.service /usr/lib/systemd/system/kit-*.service \
|
||||
/etc/x-ui /usr/local/x-ui /usr/bin/x-ui /usr/bin/x-ui.new /root/3x-ui.txt /root/cert; do
|
||||
[[ -e $f ]] || continue
|
||||
rm -rf "$f" && { kl_ok "добито: $f"; i=1; } || kl_warn "не удалось удалить: $f"
|
||||
done
|
||||
((i)) && systemctl daemon-reload
|
||||
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 || true; } | head -20)
|
||||
/etc/systemd/system/*kit* /etc/x-ui /usr/local/x-ui /root/3x-ui.txt /root/cert 2>/dev/null || true; } | head -20)
|
||||
[[ -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; }
|
||||
if ((left)); then
|
||||
kl_warn "что-то осталось — пришлите этот вывод, поправим список в kit local uninstall"
|
||||
kl_warn "осталось перечисленное выше — пришлите этот вывод, поправим список в kit local uninstall"
|
||||
else
|
||||
kl_ok "чисто: от установки не осталось ни файлов, ни служб, порт 443 свободен"
|
||||
kl_ok "проверка установщика пройдена: он снова поставит сервер с нуля"
|
||||
fi
|
||||
kl_ok "SSH не тронут (если не указан --reset-ssh)"
|
||||
kl_step "Готово: сервер в состоянии до установки, можно ставить заново"
|
||||
|
||||
+7
-7
@@ -12,7 +12,7 @@
|
||||
set -Eeuo pipefail
|
||||
export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам
|
||||
|
||||
KIT_VERSION="1.2.14"
|
||||
KIT_VERSION="1.2.15"
|
||||
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main"
|
||||
# Файлы новой версии берём из её тега, а не из меняющейся ветки main.
|
||||
kit_ref_raw() { echo "https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$1"; }
|
||||
@@ -2553,13 +2553,13 @@ menu_main() {
|
||||
echo
|
||||
box "3X-UI_autoVPS $KIT_VERSION – управление сервером" "1. Пользователи" "2. Протоколы, порты и маскировка" "3. Проверка сервера" "" \
|
||||
"4. Обновить kit и подписку" "5. Обновить панель 3X-UI" "6. Автообновление: включить или выключить" "" \
|
||||
"7. Резервная копия" "8. Версии" "" "9. Панель 3X-UI (меню x-ui)" "0. Выход"
|
||||
"10. Удалить всё установленное (наш пункт)"
|
||||
"7. Резервная копия" "8. Версии" "" "9. Удалить всё установленное" "10. Выход" \
|
||||
"11. Панель 3X-UI (меню x-ui)"
|
||||
echo
|
||||
menu_status
|
||||
update_hint
|
||||
echo
|
||||
ask_num "Выбор [0-9, 10]: "; c=$REPLY
|
||||
ask_num "Выбор [1-11]: "; c=$REPLY
|
||||
case $c in
|
||||
1) menu_users ;;
|
||||
2) menu_net ;;
|
||||
@@ -2569,9 +2569,9 @@ menu_main() {
|
||||
6) run_action menu_auto; pause ;;
|
||||
7) run_action cmd_backup; pause ;;
|
||||
8) run_action cmd_version; pause ;;
|
||||
9) command -v x-ui >/dev/null && x-ui || echo "Меню x-ui не найдено." ;;
|
||||
10) kit_local_uninstall ;;
|
||||
0 | "") return 0 ;;
|
||||
9) kit_local_uninstall ;;
|
||||
10 | 0 | "") return 0 ;;
|
||||
11) command -v x-ui >/dev/null && x-ui || echo "Меню x-ui не найдено." ;;
|
||||
*) echo "Нет такого пункта." ;;
|
||||
esac
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user