feat: multi-VLESS server selection + direct mode (stop xray)

- Добавлен home-configs/vless/servers.conf с двумя VLESS серверами
- При выборе Y: интерактивное меню выбора сервера (IP + имя)
- Парсер vless:// URL через python3 (извлекает все поля)
- Конфиг xray генерируется динамически, а не копируется статический
- При выборе N: xray стопается и дизейблится (systemctl stop+disable)
- Автоотключение IPv6 (sysctl + persist), автонастройка Firefox SOCKS5
- Автонастройка системного прокси через gsettings
- Зачистка старых drop-in оверрайдов и дефолтных конфигов xray

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Виталий Никитенко
2026-06-05 11:54:59 +07:00
parent 8979313467
commit a8d11a3f9f
2 changed files with 205 additions and 2 deletions

View File

@@ -30,15 +30,104 @@ if ! command -v python3 &>/dev/null; then
fi
success "Python 3 найден"
# ── VLESS URL parser ───────────────────────────────────────────
# Принимает vless:// URL, устанавливает переменные VL_*
parse_vless_url() {
local url="$1"
eval "$(python3 -c "
import urllib.parse, sys
url = sys.argv[1]
rest = url[8:] # strip 'vless://'
at_pos = rest.index('@')
uuid = rest[:at_pos]
rest = rest[at_pos+1:]
colon_pos = rest.index(':')
q_pos = rest.index('?')
host = rest[:colon_pos]
port = rest[colon_pos+1:q_pos]
rest = rest[q_pos+1:]
hash_pos = rest.index('#') if '#' in rest else len(rest)
qs = rest[:hash_pos]
name = rest[hash_pos+1:] if '#' in rest else ''
params = urllib.parse.parse_qs(qs)
def get(p, default=''):
vals = params.get(p, [default])
return vals[0] if vals else default
print(f'VL_UUID={uuid}')
print(f'VL_ADDRESS={host}')
print(f'VL_PORT={port}')
print(f'VL_ENCRYPTION={get(\"encryption\")}')
print(f'VL_SECURITY={get(\"security\")}')
print(f'VL_SNI={get(\"sni\")}')
print(f'VL_FP={get(\"fp\", \"chrome\")}')
print(f'VL_PBK={get(\"pbk\")}')
print(f'VL_SID={get(\"sid\")}')
print(f'VL_TYPE={get(\"type\", \"xhttp\")}')
print(f'VL_PATH={urllib.parse.unquote(get(\"path\", \"/\"))}')
print(f'VL_MODE={get(\"mode\", \"auto\")}')
print(f'VL_NAME={name}')
" "$url")"
}
# ── 0. Выбор режима работы (vless / direct) ─────────────────
read -r -p "Установить встроенный vless? [Y/n] " _vless_ans
_vless_ans="${_vless_ans:-Y}"
if [[ "$_vless_ans" =~ ^[Yy]$ ]]; then
USE_VLESS=1
info "Режим: vless + proxychains4"
# Читаем список серверов
_VL_URLS=()
_VL_LABELS=()
_SERVERS_FILE="$SCRIPT_DIR/home-configs/vless/servers.conf"
if [ ! -f "$_SERVERS_FILE" ]; then
err "Файл servers.conf не найден: $_SERVERS_FILE"
fi
while IFS= read -r line; do
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "$line" ]] && continue
_vl_rest="${line#vless://}"
_vl_rest="${_vl_rest#*@}"
_vl_ip="${_vl_rest%%:*}"
_vl_name="${line##*#}"
[[ "$_vl_name" == "$line" ]] && _vl_name=""
_VL_URLS+=("$line")
_VL_LABELS+=("$_vl_ip ($_vl_name)")
done < "$_SERVERS_FILE"
if [ "${#_VL_URLS[@]}" -eq 0 ]; then
err "Нет VLESS серверов в $_SERVERS_FILE"
fi
echo ""
info "Доступные VLESS серверы:"
for i in "${!_VL_LABELS[@]}"; do
echo -e " ${GREEN}$((i+1))${NC}) ${_VL_LABELS[$i]}"
done
read -r -p "Выбери сервер [1-${#_VL_URLS[@]}]: " _vl_choice
_vl_choice="${_vl_choice:-1}"
if ! [[ "$_vl_choice" =~ ^[0-9]+$ ]] || [ "$_vl_choice" -lt 1 ] || [ "$_vl_choice" -gt "${#_VL_URLS[@]}" ]; then
err "Неверный выбор: $_vl_choice"
fi
_VL_SELECTED="${_VL_URLS[$((_vl_choice-1))]}"
parse_vless_url "$_VL_SELECTED"
info "Выбран: $VL_ADDRESS ($VL_NAME)"
else
USE_VLESS=0
info "Режим: direct (без проксирования)"
# Останавливаем и отключаем xray (мог остаться от предыдущей установки)
sudo systemctl stop xray 2>/dev/null || true
sudo systemctl disable xray 2>/dev/null || true
fi
# ── 1. npm prefix в домашнюю папку ──────────────────────────
@@ -88,6 +177,10 @@ if [ "$USE_VLESS" -eq 1 ]; then
fi
info "Устанавливаю xray..."
# Останавливаем старый процесс (мог остаться от предыдущей установки)
sudo systemctl stop xray 2>/dev/null || true
XRAY_VERSION="26.3.27"
XRAY_ARCH="64"
XRAY_URL="https://github.com/XTLS/Xray-core/releases/download/v${XRAY_VERSION}/Xray-linux-${XRAY_ARCH}.zip"
@@ -99,7 +192,58 @@ if [ "$USE_VLESS" -eq 1 ]; then
rm -rf "$TMPDIR"
sudo mkdir -p /etc/xray
sudo cp "$SCRIPT_DIR/home-configs/xray/config.json" /etc/xray/config.json
sudo tee /etc/xray/config.json > /dev/null << XRAYEOF
{
"log": { "loglevel": "warning" },
"inbounds": [
{
"port": 1080,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": { "udp": true }
},
{
"port": 2080,
"listen": "127.0.0.1",
"protocol": "http"
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "$VL_ADDRESS",
"port": $VL_PORT,
"users": [
{
"id": "$VL_UUID",
"encryption": "$VL_ENCRYPTION",
"flow": ""
}
]
}
]
},
"streamSettings": {
"network": "$VL_TYPE",
"security": "$VL_SECURITY",
"realitySettings": {
"serverName": "$VL_SNI",
"fingerprint": "$VL_FP",
"publicKey": "$VL_PBK",
"shortId": "$VL_SID"
},
"xhttpSettings": {
"path": "$VL_PATH",
"mode": "$VL_MODE"
}
}
}
]
}
XRAYEOF
sudo chmod 644 /etc/xray/config.json
sudo tee /etc/systemd/system/xray.service > /dev/null << 'SVCEOF'
@@ -115,12 +259,67 @@ Restart=on-failure
WantedBy=multi-user.target
SVCEOF
# Удаляем чужие drop-in оверрайды (могут переопределять ExecStart на старый конфиг)
sudo rm -rf /etc/systemd/system/xray.service.d/
# Удаляем старый дефолтный конфиг xray из других путей
sudo rm -rf /usr/local/etc/xray/
sudo systemctl daemon-reload
sudo systemctl enable --now xray
success "xray установлен и запущен"
# ── Отключение IPv6 (VLESS не тянет IPv6, браузеры зависают) ──
info "Отключаю IPv6 на уровне системы..."
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
sudo tee /etc/sysctl.d/99-disable-ipv6.conf > /dev/null << 'SYSCTEOF'
# Отключение IPv6 — требуется для стабильной работы VLESS/xray
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
SYSCTEOF
sudo systemctl restart systemd-resolved
success "IPv6 отключён, DNS-кэш очищен"
cp "$SCRIPT_DIR/home-configs/proxychains/proxychains-xray.conf" "$HOME/.proxychains-xray.conf"
success "Proxychains конфиг обновлён"
# ── Настройка Firefox на SOCKS5 + remote DNS ────────────────
info "Настраиваю Firefox на SOCKS5 прокси..."
FIREFOX_PROFILE=""
if [ -d "$HOME/snap/firefox/common/.mozilla/firefox" ]; then
FIREFOX_PROFILE=$(find "$HOME/snap/firefox/common/.mozilla/firefox" -name "*.default*" -type d | head -1)
elif [ -d "$HOME/.mozilla/firefox" ]; then
FIREFOX_PROFILE=$(find "$HOME/.mozilla/firefox" -name "*.default*" -type d | head -1)
fi
if [ -n "$FIREFOX_PROFILE" ]; then
cat > "$FIREFOX_PROFILE/user.js" << 'FJSEOF'
user_pref("network.proxy.type", 1);
user_pref("network.proxy.socks", "127.0.0.1");
user_pref("network.proxy.socks_port", 1080);
user_pref("network.proxy.socks_remote_dns", true);
user_pref("network.proxy.http", "");
user_pref("network.proxy.http_port", 0);
user_pref("network.proxy.ssl", "");
user_pref("network.proxy.ssl_port", 0);
FJSEOF
success "Firefox настроен на SOCKS5 (профиль: $FIREFOX_PROFILE)"
else
warn "Firefox не найден, пропускаю настройку прокси"
fi
# ── Настройка системного прокси (для Chrome/Chromium) ───────
info "Настраиваю системный прокси..."
if command -v gsettings &>/dev/null; then
gsettings set org.gnome.system.proxy mode 'manual' 2>/dev/null || true
gsettings set org.gnome.system.proxy.http host '127.0.0.1' 2>/dev/null || true
gsettings set org.gnome.system.proxy.http port 2080 2>/dev/null || true
gsettings set org.gnome.system.proxy.socks host '127.0.0.1' 2>/dev/null || true
gsettings set org.gnome.system.proxy.socks port 1080 2>/dev/null || true
success "Системный прокси настроен (HTTP 2080 + SOCKS 1080)"
else
warn "gsettings не найден, пропускаю настройку системного прокси"
fi
fi
# ── 3. Claude Code ───────────────────────────────────────────

View File

@@ -0,0 +1,4 @@
# VLESS servers — один URL на строку
# Имя после # используется для отображения в меню выбора
vless://495ac855-d718-4581-8344-f1e62105a904@92.118.8.29:443?encryption=mlkem768x25519plus.native.0rtt.gNGR8kO_UDWjoeer_TenoRbA09ajfrSfahqkS4q0dTeCFutCHGJjIdhSl8Fv8HyYUyWB_2GqMrSIzLS9l1BiidClOnliyUmZylQwQoUFl6CsLsZxdrrDz3w2isuIhtXPfZw8lANOErk7p7mm4QN0H2ZKCOQQaxkL3XA8EcxqNuGhK4XLqlFBDommPJUQfPuP7LV3V7s8YTcwq1EUr-Nyv0CZRdcF9WCNCWq26TVYXFwHSkeknBBFKYEJiytB8DpIvlmTvREQfEVnTziwOdYKikQXeOYdbcuKSWGb2okDZjFzYckULaWhavOwRWnOquZqdXiatrNPmCWnG2mfcSxA0UdLu8ALiOtuPjBGDUqqaXhZLLuH1zhdsUE4PMqqkNnMvsRwmvAYgWwSKWioz3wTQ1sKVfW1jTh8LyouZaVkuqRTunwWBcVCRDYEFUh4TBB_-WOpu7eh4ROmj9w0mhWBrtRO-uin8ZwqhkeScQhwoFUEvlwCsAl8AevGmAm1qpdOzfXNUgBTtVoXG9OTMUk9ZORiTzO0tutVTlKjaiBb1vOqgiFqiPuG1csvaPt63LQ1e9ywvEek7NkLVnrJopmCnTiu9UhE7GwZbxI8FBFYrBmPpIUszLiWLQZqvySbqBrIFTC5lIw9WymdW5wKONs5DZmYtFZOHUVEFtVFJvcZqwnEJARKI_M-rvkS7FWPPCkKHpEVe_IJsgOgoUzI9yQI5kNlS5XAzUG7ZMGWtzYjOZNZ1FYP5NbBTTy9I8kpLRQv3yp7PYBGdBQapaYX82Qjc4orJdghG5UOTQwsokojPVaAzSWG4qk5hoNugvogx9KHdsitKXCDlhdksQS5Obc8uMg1TxO2c0AnR2nNNVqgb_prVUlaNHcP4wARfCqrMwdgYQSSoLZluah9a2eZxGcC6CMxPBsThCEwNzIJUZiuPIvEQVFyJpiCHeUErGZFLpmaE0MK2cEzD9N7pRVgEExvwIMnO3jIGmCz80FFuAmEjDG5iyMkzQpsEnBHwAaihjkSwgCsZzTL26OtIDUUYMZ1N8ciRRqTvSGzn_K6EUp7JaWygZGw3QCBRocO7SEWpuGvakJc0QV32vpty3c8UeIcJZlgGKgtcNcsVYvLpEkkYviNCbpq6jnECRYUuXGp_oQv1mwqx2WMqGic7LVZNtYCJ3fHAsGi4yh5nfU0dNSCC0cgeyakNXvL3eyqHIB_e8RrEiMywUg7_MGP2oE9ZOqayLRapVTGQ-YATgcnZ_Eg5bKHNcRVqBpo0heElCtMPoEdQYE_HZSOm1cf9nFIYqRJ6_RPJNKGDOBwy1wJWJel54y-CeY3S2vAWRibOFiwVGW01tHNSCEQBaiLEzQ2Xml6IkSAHNm9eifPrdxuBChibzco4dUFpumbxrRkJYAuqmQfWgi36ziojeEDLlUd55ZDeCOVn4MoHMcD4yzAkpdUelyBafeUoQEs6hCDf2p7CvCquWKoEzcwvuccR0GcN5un2sqQAMVKw3Gs3fsRwkgcdsK69oE4KfUKVjrBGkIk9ChYCJMYGZJ-pGkWFp1qLua2QnNmPVJlpPjurRJVoiAiOVE&security=reality&sni=www.amd.com&fp=chrome&pbk=AMoGtXyztJYD6GRP6m8K82Mi-5vthrNrppJkEmconTU&sid=9cd28fd258f8b99c&type=xhttp&path=%2F&mode=auto#RF-VPS1
vless://91749001-1f60-4afb-b7b5-25d1480de980@194.87.213.143:443?encryption=mlkem768x25519plus.native.0rtt.D4eIFKFhlMVUsInLgmx5lWuTpBJ8B3lEWnA4gCKdZgw1rjlvHsWd38rC35BAQeKSeBmbZ_sIk6w1gzCCAcYKlJN8rMZyr4TEecnADumEI0e05fRcYCXH70BdHnJxkhklzUIwAKyZHmwlk7euozhYinwq-piRuEBvl0NS-tITgOaje8jLuMyrBTnBKBtzwDR43aMsDqZhzRi-qIaiy0GXjEgHmypFIFB3EOdmCukbvgyJuBAbttTHqdCedQCeJTNfs3tzmzqFKuJXVGRvAvipWGqC_4hzlTWaVXBiuysAmGMC0oqERTyUS9qKqytOr_oyu-jPzjCdIUTPAjdpw4kzXEiuetAuZNuwdbRAHAB_DadVIAsqyVapNIVP0UlN-PJE95pAS6Yfpru2uIBWcJZhV7LE2FKBtAcC31lHeVKy12pG_NlZopYltcmdacVeXBRd1wURrrDFNNTCKaG5wAFm3HFYrzY6qZfNanebgQeYIQm3rbbGGCLBYLyHxcmKeuWVIMTJlISicHdnNyok7KJ2gWKTWtREkfQMXrZKF8BeiSp-9FQX_UCwx0LMd0axB8hqCVtpGspbH8ugKamFr4olFyKrUdQuLhEM1_xzNgZT3dWhvCxDw1VOp3qMd6G4X4YhnABioLOPl2JEyjOLMdAFRlRrdXnNpJdTA0esrJFw5Mte2gNYDAgWEFoq5uupAZlYwniB69BCeEouQiU0GYdWkxwAqwy8xtCEs9Wi4RGPBmdZnhOb5LMUBACwvFm_haF5zgwDrTmUZWdKhXMDXOMOdrcQaDvMf3uk64Q6obe47hkn1LFQS5mKOYYAjblg1RV0y1ovWtFSVBuuCsckyNUPSjETKthQQ6OUgZmpN-pMlBgzGzi3rowqdjNzrMh894ufrptX2GBqEANIM3h5HkR2ObIlJdOv6rQRnmu09EVKp7BHAVLKJcJff9HJYWIqUueArllu5IivueGPG5gPMDIp5SoHVvk_eCMUY2lLtxEE1PCjbgt8ahKTQLcFCXmJf7h945F4jOsDLCxiBSSxaMZdK5AiGGcQm-mcQ4aXBLgiHLCfD3SU7UzIF9key8u35bBm2OVjyTWei-AOssdVV2qFMppuF7Myv4GaEMpQ1JGaLpxT9zSSdVqbO6aJNBkkFxJhcnWqm8CMWVkaGubHkSeDInS65_KFe6I8IrZVm9lBThN1mvdqWkCvZ_ooRiChX7hNKTAXH9cgW-OaOTY1eissDkmW2FEGcPU5iRcDY5kgubZYgTSaHKFAIqg-6NIQX1l3d6lx41EkwyKUCWU-zfjBGsdgszjHXykOrcNQ75o_M3k_-_IBdkOpaESrhSsj2bdNUVcx7fx1S2d6STiLIrW--WeoeBhb6BcytgkbnlRlmvfNDZbOTJYzXQLJtCRZv3xJyfcEB0UqvKFIaCtN4ubHTpyRZ5PHtYHDh9lmIASIItMqeXQlazktz-cajiOFX8RCIkcDFkZ-0KpmYiBToVpi-zSjdLCPPVMf14V5kqAP6EA1IiTJPwFuroWluOJ5j8mx4c0i8fbd1n_mTH_7Ff8Ognu-LFLBU9r9yWcVhBkm_JQ&security=reality&sni=vkvideo.ru&fp=chrome&pbk=BWwAjxuB6QbC2ta23L8_U4QjjkUs17NDi79Mm2RjQHQ&sid=17c863e8490260b6&type=xhttp&path=%2F&mode=packet-up#RF-Bridge-PQ