From 88f2c1066b9f9c68edb036ca755184e0fd1f6838 Mon Sep 17 00:00:00 2001 From: maxdmitrov <56165502+maxdmitrov@users.noreply.github.com> Date: Mon, 5 Oct 2026 21:50:38 +0300 Subject: [PATCH] =?UTF-8?q?kit=20net=20split:=20=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B5=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=B2=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=B5=20?= =?UTF-8?q?(=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B8),=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8=D0=B9=20GEOSITE=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=B1=D0=B0=D0=B7=D0=B5=20=D0=B2=20kit=20net=20?= =?UTF-8?q?split=20check,=20=D1=87=D0=B5=D1=81=D1=82=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE?= =?UTF-8?q?=D0=B1=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- manuals/3x-ui.md | 4 ++-- scripts/kit-sub.py | 26 ++++++++++++++++++++++++++ scripts/kit.sh | 28 +++++++++++++++++++++++++--- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71e2fbe..25da663 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ * Сайт для маскировки подбирается среди соседей по подсети, а не «у всех один». Свой домен по-прежнему лучший вариант. REALITY теперь с `xtls-rprx-vision`, Hysteria2 отвечает на чужой запрос страницей сайта. * `kit check --deep` подключается к серверу настоящим клиентом, `--fix` лечит безопасное. Общий лимит трафика считается по всем подключениям пользователя, а в подписке Clash есть «Авто»: клиент сам выбирает быстрейший протокол. DNS в подписках теперь по DoH через прокси: запросы имён не гуляют где попало. * Ссылка подписки в браузере открывается страницей с трафиком, сроком и ссылками (а не белым экраном). Рядом с подпиской теперь ссылки «открыть в приложении»: одно нажатие, и подписка добавлена с названием. Со своим доменом панель и подписку можно открывать и по домену: `--panel-on domain` при установке или `kit net panel domain` потом. Вернулся `--restore`: чистый сервер поднимается из `kit backup`, как из резервной копии настроек телефона. Ссылки `kit user link --amnezia` и `--telegram` для AmneziaVPN и Telegram теперь одной командой, а исправление времени на Debian 13 прислал [xlok1e](https://github.com/xlok1e), спасибо. -* Раздельная маршрутизация: `kit net split on` – и в приложениях на Mihomo (Clash Verge, FlClash, Mihomo Party) через VPN идёт только список (по умолчанию YouTube и ChatGPT), а всё остальное напрямую. Правила приезжают вместе с подпиской: домашним ничего настраивать не нужно. DNS не течёт: имена из списка у клиента не разрешаются, а запросы про них идут по DoH через VPN. Список в `/etc/kit-sub/rules.yaml`, проверка `kit net split check`, он входит в `kit backup`. Идея и первый набросок – [VasilevVitalii](https://github.com/VasilevVitalii), спасибо. У Happ, Hiddify, v2rayN и sing-box подписка прежняя. +* Раздельная маршрутизация: `kit net split on` – и в приложениях на Mihomo (Clash Verge, FlClash, Mihomo Party) через VPN идёт только список иностранных сервисов (по умолчанию YouTube, ChatGPT и Claude), а всё остальное напрямую. Правила приезжают вместе с подпиской: домашним ничего настраивать не нужно. DNS не течёт: имена из списка у клиента не разрешаются, а запросы про них идут по DoH через VPN. Список в `/etc/kit-sub/rules.yaml`, проверка `kit net split check`, он входит в `kit backup`. Идея и первый набросок – [VasilevVitalii](https://github.com/VasilevVitalii), спасибо. У Happ, Hiddify, v2rayN и sing-box подписка прежняя. * Исправил `kit backup`: из `/root` он падал с «No such file or directory», работал только из `/`. Теперь работает откуда угодно. Если панель не принимает запрос, `kit` говорит по-человечески (код ответа и вероятная причина), а не «Unsupported HTTP version in response». Сервер обновится сам этой ночью, нетерпеливым – `kit update`. diff --git a/manuals/3x-ui.md b/manuals/3x-ui.md index 94f3369..3688bb6 100644 --- a/manuals/3x-ui.md +++ b/manuals/3x-ui.md @@ -180,7 +180,7 @@ kit net on reality2 # создать второй REALITY на высо kit net fp firefox # сменить отпечаток клиента у всех подключений kit net masq on # Hysteria2 отвечает на чужой запрос страницей сайта kit net panel domain # панель и подписка открываются и по домену (только со своим доменом); kit net panel ip – как раньше -kit net split on # через VPN только список (по умолчанию YouTube и ChatGPT), остальное напрямую; kit net split check | off +kit net split on # через VPN только список (по умолчанию YouTube, ChatGPT и Claude), остальное напрямую; kit net split check | off ``` Со своим доменом панель и подписка по умолчанию открываются по IP (`https://IP/путь/`). Если нужен адрес с доменом (например, для Telegram-бота панели @@ -189,7 +189,7 @@ kit net split on # через VPN только список (по ум ### Раздельная маршрутизация (`kit net split`) Если нужно, чтобы через VPN шли только выбранные сервисы, а всё остальное напрямую, включите: `kit net split on`. На сервере появится -файл `/etc/kit-sub/rules.yaml` со списком (по умолчанию YouTube и ChatGPT). Правила приезжают вместе с подпиской, на телефонах и компьютерах +файл `/etc/kit-sub/rules.yaml` со списком иностранных сервисов (по умолчанию YouTube, ChatGPT и Claude; остальные – в комментариях, раскомментируйте нужные). Российские сайты в список не добавляйте: всё, чего в нём нет, и так идёт напрямую. Правила приезжают вместе с подпиской, на телефонах и компьютерах ничего настраивать не нужно: достаточно обновить подписку в приложении. Работает в приложениях на Mihomo (Clash Verge, FlClash, Mihomo Party); в Happ, Hiddify, v2rayN и sing-box подписка остаётся прежней, весь трафик идёт через VPN. diff --git a/scripts/kit-sub.py b/scripts/kit-sub.py index 45faa41..691182e 100644 --- a/scripts/kit-sub.py +++ b/scripts/kit-sub.py @@ -399,12 +399,38 @@ class Server(http.server.ThreadingHTTPServer): address_family = socket.AF_INET6 if ":" in CONF.get("listen", "") else socket.AF_INET +GEOSITE_URL = "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat" + + +def unknown_geosites(names): + """Сверяет категории GEOSITE с базой, которую скачают приложения: неизвестное имя ломает запуск конфига у клиента. + Нет доступа к GitHub – не проверяем (None-результат пустой).""" + if not names: + return [] + try: + with urllib.request.urlopen(GEOSITE_URL, timeout=30) as r: + data = r.read(32 * 1024 * 1024) + except (urllib.error.URLError, OSError): + print("Категории GEOSITE не проверил: база недоступна. Названия сверьте по github.com/MetaCubeX/meta-rules-dat (geo/geosite).") + return [] + bad = [] + for n in names: + b = n.upper().encode() + if b"\x0a" + bytes([len(b)]) + b not in data: + bad.append(n) + return bad + + def main(): if "--check-rules" in sys.argv: # kit net split check: тот же разбор, что и при выдаче подписки, но с отчётом на экран. loaded = load_rules(report=print) if loaded: print(f"Правил через VPN: {len(loaded[0])}; DNS для остального: {', '.join(loaded[1]) or 'системный'}") + unknown = unknown_geosites([v for k, v in loaded[0] if k == "GEOSITE"]) + if unknown: + print("Нет такой категории GEOSITE: " + ", ".join(unknown) + ". Приложение не сможет запустить конфиг – исправьте или удалите эти строки.") + raise SystemExit(2) raise SystemExit(0 if loaded else 1) cert, key = CONF.get("cert"), CONF.get("key") if cert and CREDS and os.path.exists(os.path.join(CREDS, "cert.pem")): diff --git a/scripts/kit.sh b/scripts/kit.sh index 4ef4d08..3bd3837 100644 --- a/scripts/kit.sh +++ b/scripts/kit.sh @@ -1736,7 +1736,7 @@ SPLIT_FILE=/etc/kit-sub/rules.yaml split_template() { cat <<'YAML' # Раздельная маршрутизация подписки (приложения на Mihomo: Clash Verge, FlClash, Mihomo Party). -# Через VPN идёт ТОЛЬКО перечисленное ниже, всё остальное – напрямую. +# Через VPN идёт ТОЛЬКО перечисленное ниже (сервисы, которым VPN нужен), всё остальное – напрямую. # Правка вступает в силу, когда приложение обновит подписку; перезапуск не нужен. # Проверить файл: kit net split check. Выключить: kit net split off. # @@ -1751,6 +1751,24 @@ split_template() { via_vpn: - GEOSITE,youtube - GEOSITE,openai + - GEOSITE,anthropic + # Раскомментируйте то, чем пользуетесь (уберите «# » в начале строки): + # - GEOSITE,telegram + # - GEOSITE,meta # Instagram, Facebook, WhatsApp + # - GEOSITE,twitter # X (Twitter) + # - GEOSITE,discord + # - GEOSITE,github + # - GEOSITE,linkedin + # - GEOSITE,netflix + # - GEOSITE,spotify + # - GEOSITE,tiktok + # - GEOSITE,reddit + # - GEOSITE,twitch + # - GEOSITE,notion + # - GEOSITE,figma + # - GEOSITE,zoom + # - GEOSITE,google # все сервисы Google + # - GEOSITE,perplexity # - DOMAIN-SUFFIX,example.org # Необязательно: DNS для всего остального (по умолчанию – системный DNS приложения). Только адреса https://… @@ -1777,7 +1795,7 @@ net_split() { # [on|off|check] if [[ -f $SPLIT_FILE.off ]]; then install -m 644 "$SPLIT_FILE.off" "$SPLIT_FILE"; else split_template >"$SPLIT_FILE"; fi chmod 755 /etc/kit-sub; chmod 644 "$SPLIT_FILE" say "Включил: $SPLIT_FILE" - echo "Сейчас через VPN идут только перечисленные сервисы (YouTube, ChatGPT), остальное напрямую." + echo "Сейчас через VPN идут только перечисленные сервисы (YouTube, ChatGPT, Claude), остальное напрямую." echo "Список правится так: nano $SPLIT_FILE, затем kit net split check." echo "Приложения на Mihomo подхватят при обновлении подписки." ;; off) @@ -1786,7 +1804,11 @@ net_split() { # [on|off|check] say "Выключил: подписка снова отдаёт весь трафик через VPN (список сохранён: $SPLIT_FILE.off)." ;; check) [[ -f $SPLIT_FILE ]] || die "Файла нет: $SPLIT_FILE (включить: kit net split on)" - python3 /usr/local/lib/kit-sub/kit_sub.py --check-rules || die "Список не применится – подписка останется без раздельной маршрутизации." ;; + python3 /usr/local/lib/kit-sub/kit_sub.py --check-rules && return 0 + case $? in + 2) die "Исправьте список: с неизвестной категорией приложения на Mihomo не смогут запустить конфиг." ;; + *) die "Список не применится – подписка останется без раздельной маршрутизации." ;; + esac ;; *) die "kit net split [on|off|check]" ;; esac }