From 9655cdfee1cb08a3bd479d66bd66cb13b7b422a8 Mon Sep 17 00:00:00 2001 From: dperegudov Date: Thu, 17 Sep 2026 00:42:11 +0300 Subject: [PATCH] add Proton additional out on 3080 --- README.md | 79 +++++++++++++-- generate-config.sh | 237 +++++++++++++++++++++++++++++++++++++++++--- proton.conf.example | 15 +++ quick-install.sh | 48 +++++++++ 4 files changed, 357 insertions(+), 22 deletions(-) create mode 100644 proton.conf.example diff --git a/README.md b/README.md index 1d99416..3b14cac 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sing-Box WARP Project +# Sing-Box WARP Project (Additional Proton out) > Author remark: > вы спросите - нахуя такие сложности? А потому что нативный warp не поддерживает amneziaWG ключи для обфускации WireGuard. А ебаться в саму амнезию нет желания. К тому же sing-box сразу может поднять локальный сокс что упрощает просовывание этого варпа во всякого рода панели. > UPD: Теперь в рф захотели блокировать github и появилась проблема установки панелей. Данное решение покрывает проблемы со скачиванием любых репозиториев или иного self-host софта с github и попутно покрывает ряд других блокнутых ресурсов при включении TUN во время установки. @@ -12,12 +12,25 @@ - `quick-install.sh` — установка и настройка systemd-сервиса - `generate-config.sh` — генератор `config.json` из WireGuard AWG3.0 - `warp.conf.example` — пример WARP-конфигурации +- `proton.conf.example` — пример второй (Proton) WireGuard-конфигурации ## Как это работает 1. При установке вводится WARP-конфиг (AWG 3.0) → сохраняется в `/etc/sing-box-warp/warp.conf` -2. `generate-config.sh` парсит конфиг и пишет `/opt/sing-box-warp/config.json` -3. SOCKS5 доступен на порту `2080`; опционально TUN для сплит-маршрутизации +2. Опционально вводится второй конфиг (Proton VPN, обычный WireGuard или `wg://`) → `/etc/sing-box-warp/proton.conf` +3. `generate-config.sh` парсит конфиги и пишет `/opt/sing-box-warp/config.json` +4. SOCKS5 WARP доступен на порту `2080`; при добавлении Proton — второй SOCKS5 на `3080` +5. Опционально TUN для сплит-маршрутизации + +### Схема маршрутизации + +Скрипт работает в двух режимах: **только WARP** или **WARP + Proton**. + +- SOCKS-инпуты жёстко привязаны к своим выходам: `2080 → warp-out`, `3080 → proton-out`. +- Всё остальное (TUN-трафик, списки маршрутизации `.srs`, сайты проверки IP, proxy-DNS) идёт в + «дефолтный» выход. Дефолт выбирается при установке: **WARP** или **Proton** (вопрос задаётся + только если Proton добавлен). +- Если Proton не добавлен — второй SOCKS-инпут (`3080`) и `proton-out` не создаются, дефолт всегда WARP. ## Установка @@ -29,12 +42,30 @@ curl -fsSL https://gitea.digitalforest.my/hesoyam/sing-warp-socks5/raw/branch/ma Вставьте текстом всё содержимое сгенерированного AWG конфига в терминал. Enter затем ctrl+D -Скрипт спросит включение TUN. Включите, если с сервера недоступны заблокированные ресурсы (GitHub и т.п.). Без TUN — только SOCKS5 на `2080`. +Затем скрипт спросит про Proton. Брать протон тут https://account.protonvpn.com/downloads (Нужен аккаунт) +Ответьте `n`, чтобы оставить только WARP, или `y` и вставьте +второй WireGuard-конфиг (формат `[Interface]`/`[Peer]` как у Proton VPN, либо `wg://`), завершив ввод Ctrl+D. + +``` +Add Proton configuration? (y/n) +``` + +Далее спрашивается включение TUN. Включите, если с сервера недоступны заблокированные ресурсы (GitHub и т.п.). Без TUN — только SOCKS5. ``` Enable TUN mode? (y/n) ``` +Если Proton добавлен — скрипт спросит, куда направлять весь не-SOCKS трафик (дефолтный выход). +SOCKS-инпуты при этом всегда остаются: `2080 → WARP`, `3080 → Proton`. + +``` +=== Default route === + 1) WARP (warp-out) + 2) Proton (proton-out) +Select default route [1/2] +``` + Повторный запуск инсталлера останавливает старый сервис, переустанавливает файлы и запускает заново. ## Формат warp.conf @@ -63,15 +94,36 @@ Endpoint = engage.cloudflareclient.com:2408 PersistentKeepalive = 25 ``` -## Использование +## Формат proton.conf (опционально) -SOCKS5 без авторизации: `localhost:2080` +Обычный WireGuard-конфиг (например, экспорт из Proton VPN) или `wg://`-ссылка. -```bash -curl --proxy socks5://localhost:2080 ip-api.com +```ini +[Interface] +PrivateKey = {privkey} +Address = 10.2.0.2/32 +DNS = 10.2.0.1 + +[Peer] +PublicKey = {pubkey} +AllowedIPs = 0.0.0.0/0, ::/0 +Endpoint = 149.50.211.159:51820 +PersistentKeepalive = 25 ``` -С TUN трафик по спискам `.srs` идёт через WARP; SOCKS5 при этом остаётся доступным. +## Использование + +SOCKS5 без авторизации: + +- WARP — `localhost:2080` +- Proton (если добавлен) — `0.0.0.0:3080` + +```bash +curl --proxy socks5://localhost:2080 ip-api.com # через WARP +curl --proxy socks5://localhost:3080 ip-api.com # через Proton +``` + +С TUN трафик по спискам `.srs` идёт через дефолтный выход (WARP или Proton); SOCKS5 при этом остаётся доступным. ```bash curl ip-api.com @@ -89,7 +141,14 @@ sudo systemctl restart sing-box-warp ## Обновление конфигурации -Отредактируйте `/etc/sing-box-warp/warp.conf` (и при необходимости `/etc/sing-box-warp/enable-tun`), затем: +Отредактируйте нужные файлы в `/etc/sing-box-warp/`: + +- `warp.conf` — конфиг WARP +- `proton.conf` — конфиг Proton (удалите файл, чтобы отключить Proton) +- `enable-tun` — `1`/`0` для TUN +- `default-route` — `warp` или `proton` (куда идёт весь не-SOCKS трафик) + +затем: ```bash sudo /opt/sing-box-warp/generate-config.sh diff --git a/generate-config.sh b/generate-config.sh index ecc3943..ed43952 100644 --- a/generate-config.sh +++ b/generate-config.sh @@ -1,8 +1,10 @@ #!/bin/sh WARP_CONF="${WARP_CONF:-/etc/sing-box-warp/warp.conf}" +PROTON_CONF="${PROTON_CONF:-/etc/sing-box-warp/proton.conf}" OUTPUT_CONFIG="${OUTPUT_CONFIG:-/opt/sing-box-warp/config.json}" ENABLE_TUN_FILE="${ENABLE_TUN_FILE:-/etc/sing-box-warp/enable-tun}" +DEFAULT_ROUTE_FILE="${DEFAULT_ROUTE_FILE:-/etc/sing-box-warp/default-route}" load_enable_tun() { local value @@ -18,6 +20,22 @@ load_enable_tun() { esac } +# Where all non-SOCKS traffic goes (TUN rules, routing lists, IP-leak sites). +# The SOCKS inbounds keep their own hard-coded outbound. +load_default_route() { + local value + + value=$(trim "${DEFAULT_ROUTE:-}") + if [ -z "$value" ] && [ -f "$DEFAULT_ROUTE_FILE" ]; then + value=$(trim "$(cat "$DEFAULT_ROUTE_FILE")") + fi + + case "$value" in + 2|proton|PROTON|Proton) DEFAULT_ROUTE=proton ;; + *) DEFAULT_ROUTE=warp ;; + esac +} + urldecode() { echo "$1" | sed 's/%3[dD]/=/g; s/%2[bB]/+/g; s/%2[fF]/\//g; s/%2[cC]/,/g' } @@ -80,6 +98,39 @@ normalize_cidr() { esac } +# Convert a comma-separated AllowedIPs list into a JSON array body: +# "0.0.0.0/0, ::/0" -> "0.0.0.0/0", "::/0" +build_allowed_ips_json() { + local input + local result="" + local item + local old_ifs + + input="$1" + if [ -z "$input" ]; then + echo '"0.0.0.0/0", "::/0"' + return + fi + + old_ifs="$IFS" + IFS=',' + for item in $input; do + item=$(trim "$item") + [ -z "$item" ] && continue + if [ -z "$result" ]; then + result="\"$item\"" + else + result="$result, \"$item\"" + fi + done + IFS="$old_ifs" + + if [ -z "$result" ]; then + result='"0.0.0.0/0", "::/0"' + fi + echo "$result" +} + # Extract parameter from URL get_param() { local url="$1" @@ -134,7 +185,7 @@ parse_from_wg_url() { H4=${H4:-4} ALLOWED_IPS="0.0.0.0/0" - TAG="wireguard-out" + TAG="warp-out" LOG_LEVEL="error" } @@ -226,11 +277,112 @@ parse_from_ini() { exit 1 fi - TAG="wireguard-out" + TAG="warp-out" LOG_LEVEL="error" } +parse_proton_from_ini() { + local section="" + local key="" + local value="" + local p_ipv4="" + local p_allowed="" + + while IFS= read -r line || [ -n "$line" ]; do + line=$(trim "$line") + [ -z "$line" ] && continue + + case "$line" in + \#*|\;*) continue ;; + esac + + case "$line" in + "[Interface]") section="Interface"; continue ;; + "[Peer]") section="Peer"; continue ;; + esac + + key=$(trim "$(echo "$line" | cut -d'=' -f1)") + value=$(trim "$(echo "$line" | cut -d'=' -f2-)") + [ -z "$key" ] && continue + + if [ "$section" = "Interface" ]; then + case "$key" in + PrivateKey) PROTON_PRIVATE_KEY="$value" ;; + Address) p_ipv4=$(trim "$(echo "$value" | cut -d',' -f1)") ;; + MTU) PROTON_MTU="$value" ;; + esac + elif [ "$section" = "Peer" ]; then + case "$key" in + PublicKey) PROTON_PUBLIC_KEY="$value" ;; + AllowedIPs) p_allowed="$value" ;; + Endpoint) + PROTON_SERVER=$(echo "$value" | cut -d':' -f1) + PROTON_PORT=$(echo "$value" | cut -d':' -f2) + ;; + esac + fi + done < "$PROTON_CONF" + + PROTON_ADDRESS=$(normalize_cidr "$p_ipv4" 32) + PROTON_ALLOWED_IPS=$(build_allowed_ips_json "$p_allowed") +} + +parse_proton_from_wg_url() { + local url + local local_addr + local p_ipv4 + + url=$(grep "^wg://" "$PROTON_CONF" | head -1) + + PROTON_SERVER=$(echo "$url" | sed 's|wg://\([^:]*\):.*|\1|') + PROTON_PORT=$(echo "$url" | sed 's|wg://[^:]*:\([0-9]*\)?.*|\1|') + PROTON_PRIVATE_KEY=$(urldecode "$(get_param "$url" "private_key")") + PROTON_PUBLIC_KEY=$(urldecode "$(get_param "$url" "peer_public_key")") + PROTON_MTU=$(get_param "$url" "mtu") + + local_addr=$(urldecode "$(get_param "$url" "local_address")") + p_ipv4=$(echo "$local_addr" | tr ',-' '\n' | sed -n '1p') + PROTON_ADDRESS=$(normalize_cidr "$p_ipv4" 32) + PROTON_ALLOWED_IPS='"0.0.0.0/0", "::/0"' +} + +parse_proton_conf() { + local first_line + first_line=$(head -n 1 "$PROTON_CONF" | tr -d '\r') + first_line=$(trim "$first_line") + + if echo "$first_line" | grep -q "^wg://"; then + parse_proton_from_wg_url + elif echo "$first_line" | grep -q "^\[Interface\]"; then + parse_proton_from_ini + else + echo "Error: Unsupported proton.conf format (expected wg:// or [Interface])" + exit 1 + fi + + PROTON_MTU=${PROTON_MTU:-1280} + + if [ -z "$PROTON_PRIVATE_KEY" ] || [ -z "$PROTON_PUBLIC_KEY" ] \ + || [ -z "$PROTON_SERVER" ] || [ -z "$PROTON_PORT" ] \ + || [ -z "$PROTON_ADDRESS" ]; then + echo "Error: proton.conf is missing required fields (PrivateKey/PublicKey/Endpoint/Address)" + exit 1 + fi + + ENABLE_PROTON=1 +} + write_config() { + # Default outbound for everything that is not a hard SOCKS rule + # (TUN rules, routing lists, IP-leak sites, proxy DNS). + # Proton can only be the default when it is actually enabled. + if [ "$DEFAULT_ROUTE" = "proton" ] && [ "$ENABLE_PROTON" = "1" ]; then + DEFAULT_OUTBOUND="proton-out" + else + DEFAULT_OUTBOUND="warp-out" + fi + echo "Default route outbound: $DEFAULT_OUTBOUND" >&2 + if [ "$ENABLE_TUN" = "1" ]; then EXCLUDE_IFACE=$(detect_default_interface) || { echo "Warning: could not detect default network interface, using eth0" >&2 @@ -275,7 +427,7 @@ TUNEOF } RULEEOF ) - RULE_SET_RULE_PART=$(cat <<'RULEEOF' + RULE_SET_RULE_PART=$(cat <&2 + WARP_IN_TAG="warp-in" + PROTON_ENDPOINT_PART=$(cat <&2 + WARP_IN_TAG="mixed-in" + PROTON_ENDPOINT_PART="" + PROTON_INBOUND_PART="" + PROTON_ROUTE_RULE_PART="" + fi + if [ -n "$I1" ] || [ -n "$I2" ]; then H4_COMMA="," else @@ -397,7 +601,7 @@ DEFSEOF "tag": "dns-proxy", "type": "tls", "server": "8.8.8.8", - "detour": "wireguard-out" + "detour": "$DEFAULT_OUTBOUND" }, { "tag": "local", @@ -438,14 +642,14 @@ DEFSEOF $I1_LINE $I2_LINE } - } + }$PROTON_ENDPOINT_PART ], "inbounds": [ { "type": "mixed", - "tag": "mixed-in", + "tag": "$WARP_IN_TAG", "listen_port": 2080 - }$TUN_INBOUND_PART + }$PROTON_INBOUND_PART$TUN_INBOUND_PART ], "outbounds": [ { @@ -463,9 +667,9 @@ $I2_LINE "outbound": "direct" }, { - "inbound": "mixed-in", - "outbound": "wireguard-out" - }, + "inbound": "$WARP_IN_TAG", + "outbound": "warp-out" + },$PROTON_ROUTE_RULE_PART { "protocol": "dns", "action": "hijack-dns" @@ -485,7 +689,7 @@ $I2_LINE "ipecho.net", "ip.sb" ], - "outbound": "wireguard-out" + "outbound": "$DEFAULT_OUTBOUND" }$RULE_SET_RULE_PART ], $ROUTE_RULE_SET_SECTION @@ -512,11 +716,20 @@ parse_warp_conf() { fi load_enable_tun + + if [ -f "$PROTON_CONF" ] && [ -s "$PROTON_CONF" ]; then + parse_proton_conf + fi + + load_default_route + write_config } # Main +ENABLE_PROTON=0 load_enable_tun +load_default_route if [ ! -f "$WARP_CONF" ]; then echo "Error: $WARP_CONF not found!" diff --git a/proton.conf.example b/proton.conf.example new file mode 100644 index 0000000..8e4e56d --- /dev/null +++ b/proton.conf.example @@ -0,0 +1,15 @@ +[Interface] +# Bouncing = 1 +# NAT-PMP (Port Forwarding) = off +# VPN Accelerator = on +PrivateKey = {privkey} +Address = 10.2.0.2/32 +DNS = 10.2.0.1 + +[Peer] +# SG-FREE#4 +PublicKey = {pubkey} +AllowedIPs = 0.0.0.0/0, ::/0 +Endpoint = 123.123.123.123:51820 + +PersistentKeepalive = 25 \ No newline at end of file diff --git a/quick-install.sh b/quick-install.sh index 39f47ff..c1f2f80 100644 --- a/quick-install.sh +++ b/quick-install.sh @@ -58,6 +58,8 @@ clean_existing_files() { rm -rf "$INSTALL_DIR/rules" rm -f "$CONFIG_DIR/enable-tun" 2>/dev/null || true + rm -f "$CONFIG_DIR/proton.conf" 2>/dev/null || true + rm -f "$CONFIG_DIR/default-route" 2>/dev/null || true rm -rf "${CACHE_DIR:?}"/* } @@ -79,8 +81,10 @@ prepare_reinstall() { start_service() { echo "Generating config..." WARP_CONF="$CONFIG_DIR/warp.conf" \ + PROTON_CONF="$CONFIG_DIR/proton.conf" \ OUTPUT_CONFIG="$INSTALL_DIR/config.json" \ ENABLE_TUN_FILE="$CONFIG_DIR/enable-tun" \ + DEFAULT_ROUTE_FILE="$CONFIG_DIR/default-route" \ "$INSTALL_DIR/generate-config.sh" echo "Starting ${SERVICE_NAME}..." @@ -122,6 +126,28 @@ printf "%s\n" "$WARP_INPUT" > "$CONFIG_DIR/warp.conf" echo "" echo "Configuration saved to $CONFIG_DIR/warp.conf" +echo "" +echo "=== Proton Configuration (optional) ===" +echo "Adds a second WireGuard endpoint (e.g. Proton VPN) exposed as SOCKS5 on port 3080." +echo "The main WARP proxy stays on port 2080." +read -p "Add Proton configuration? (y/n) " -n 1 -r < /dev/tty +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Paste your Proton config ([Interface]/[Peer] INI or wg://)." + echo "Finish input with Ctrl-D." + PROTON_INPUT=$(cat < /dev/tty) + if [ -z "$PROTON_INPUT" ]; then + echo "Proton input is empty, skipping Proton configuration." + rm -f "$CONFIG_DIR/proton.conf" + else + printf "%s\n" "$PROTON_INPUT" > "$CONFIG_DIR/proton.conf" + echo "Proton configuration saved to $CONFIG_DIR/proton.conf" + fi +else + rm -f "$CONFIG_DIR/proton.conf" + echo "Skipping Proton configuration." +fi + echo "" echo "=== TUN mode ===" echo "TUN routes system traffic through sing-box (needs CAP_NET_ADMIN)." @@ -138,6 +164,26 @@ else echo "TUN mode disabled (SOCKS5 only)." fi +DEFAULT_ROUTE="warp" +if [ -f "$CONFIG_DIR/proton.conf" ]; then + echo "" + echo "=== Default route ===" + echo "Choose where all non-SOCKS traffic goes (TUN rules, routing lists, IP-leak sites)." + echo "SOCKS inbounds always keep their own outbound: 2080 -> WARP, 3080 -> Proton." + echo " 1) WARP (warp-out)" + echo " 2) Proton (proton-out)" + read -p "Select default route [1/2] " -n 1 -r < /dev/tty + echo + if [[ $REPLY == "2" ]]; then + DEFAULT_ROUTE="proton" + echo "Default route: Proton (proton-out)." + else + DEFAULT_ROUTE="warp" + echo "Default route: WARP (warp-out)." + fi +fi +echo "$DEFAULT_ROUTE" > "$CONFIG_DIR/default-route" + echo "" echo "Downloading sing-box..." NEED_DOWNLOAD=1 @@ -203,6 +249,8 @@ User=root WorkingDirectory=/opt/sing-box-warp Environment="HOME=/var/cache/sing-box-warp" Environment="WARP_CONF=/etc/sing-box-warp/warp.conf" +Environment="PROTON_CONF=/etc/sing-box-warp/proton.conf" +Environment="DEFAULT_ROUTE_FILE=/etc/sing-box-warp/default-route" Environment="OUTPUT_CONFIG=/opt/sing-box-warp/config.json" ExecStartPre=/opt/sing-box-warp/generate-config.sh ExecStart=/usr/local/bin/sing-box run -c /opt/sing-box-warp/config.json