From 10d74c6a6b481f9949efd4706a9b1fa448b6907f Mon Sep 17 00:00:00 2001 From: Ivan K Date: Sat, 30 Nov 2024 14:07:54 +0300 Subject: [PATCH 01/10] feat: Add proxy configuration options --- .../resources/view/podkop/podkop.js | 59 +++++++- podkop/files/etc/init.d/podkop | 142 ++++++++++++++---- 2 files changed, 170 insertions(+), 31 deletions(-) diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js index 5049292..1765eda 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js @@ -21,11 +21,38 @@ return view.extend({ o.value('proxy', ('Proxy')); o.ucisection = 'main'; - o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy')); + o.value('url', _('Connection URL')); + o.value('outbound', _('Outbound Config')); + o.default = 'url'; o.depends('mode', 'proxy'); + o.ucisection = 'main'; + + o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o.depends('proxy_config_type', 'url'); o.rows = 5; o.ucisection = 'main'; + o = s.taboption('basic', form.TextValue, 'outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format')); + o.depends('proxy_config_type', 'outbound'); + o.rows = 10; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + try { + const parsed = JSON.parse(value); + if (!parsed.type || !parsed.server || !parsed.server_port) { + return _('JSON must contain at least type, server and server_port fields'); + } + return true; + } catch (e) { + return _('Invalid JSON format'); + } + }; + o = s.taboption('basic', form.ListValue, 'interface', _('Network Interface'), _('Select network interface for VPN connection')); o.depends('mode', 'vpn'); o.ucisection = 'main'; @@ -315,10 +342,38 @@ return view.extend({ o.depends('second_enable', '1'); o.ucisection = 'second'; - o = s.taboption('secondary_config', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o = s.taboption('secondary_config', form.ListValue, 'second_proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy')); + o.value('url', _('Connection URL')); + o.value('outbound', _('Outbound Config')); + o.default = 'url'; o.depends('second_mode', 'proxy'); o.ucisection = 'second'; + o = s.taboption('secondary_config', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o.depends('second_proxy_config_type', 'url'); + o.rows = 5; + o.ucisection = 'second'; + + o = s.taboption('secondary_config', form.TextValue, 'second_outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format')); + o.depends('second_proxy_config_type', 'outbound'); + o.rows = 10; + o.ucisection = 'second'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + try { + const parsed = JSON.parse(value); + if (!parsed.type || !parsed.server || !parsed.server_port) { + return _('JSON must contain at least type, server and server_port fields'); + } + return true; + } catch (e) { + return _('Invalid JSON format'); + } + }; + o = s.taboption('secondary_config', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); o.depends('second_mode', 'vpn'); o.ucisection = 'second'; diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 42801cf..5aba541 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -37,14 +37,27 @@ start_service() { config_get_bool second_enable "second" "second_enable" "0" config_get second_mode "second" "second_mode" "0" if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then - config_get proxy_string "second" "second_proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_shadowsocks "$proxy_string" "1603" - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_vless "$proxy_string" "1603" + config_get proxy_config_type "second" "second_proxy_config_type" + + if [ "$proxy_config_type" = "outbound" ]; then + config_get outbound_json "second" "second_outbound_json" + if [ -n "$outbound_json" ]; then + log "Using JSON outbound configuration for second proxy" + sing_box_config_outbound_json "$outbound_json" "1603" + else + log "Missing outbound JSON configuration" + return + fi else - log "Unsupported proxy type: $proxy_string" - return + config_get proxy_string "second" "second_proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_shadowsocks "$proxy_string" "1603" + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_vless "$proxy_string" "1603" + else + log "Unsupported proxy type or missing configuration" + return + fi fi add_route_tproxy podkop2 sing_box_config_check @@ -78,27 +91,53 @@ start_service() { outbound_main=$(mktemp) outbound_second=$(mktemp) - config_get proxy_string main "proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_main" main - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_outbound_vless "$proxy_string" "$outbound_main" main + # Main proxy config + config_get proxy_config_type main "proxy_config_type" + if [ "$proxy_config_type" = "outbound" ]; then + config_get outbound_json main "outbound_json" + if [ -n "$outbound_json" ]; then + echo "$outbound_json" > "$outbound_main" + jq '.tag = "main"' "$outbound_main" > "${outbound_main}.tmp" && mv "${outbound_main}.tmp" "$outbound_main" + else + log "Missing main outbound JSON configuration" + return + fi else - log "Unsupported proxy type: $proxy_string" - return + config_get proxy_string main "proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_main" main + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_outbound_vless "$proxy_string" "$outbound_main" main + else + log "Unsupported proxy type or missing configuration for main" + return + fi fi - config_get proxy_string "second" "second_proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_second" second - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_outbound_vless "$proxy_string" "$outbound_second" second + # Second proxy config + config_get proxy_config_type second "second_proxy_config_type" + if [ "$proxy_config_type" = "outbound" ]; then + config_get outbound_json second "second_outbound_json" + if [ -n "$outbound_json" ]; then + echo "$outbound_json" > "$outbound_second" + jq '.tag = "second"' "$outbound_second" > "${outbound_second}.tmp" && mv "${outbound_second}.tmp" "$outbound_second" + else + log "Missing second outbound JSON configuration" + return + fi else - log "Unsupported proxy type: $proxy_string" - return + config_get proxy_string "second" "second_proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_second" second + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_outbound_vless "$proxy_string" "$outbound_second" second + else + log "Unsupported proxy type or missing configuration for second" + return + fi fi - jq --argjson outbounds "$(jq -s '{"outbounds": (.[0].outbounds + .[1].outbounds)}' "$outbound_main" "$outbound_second")" \ + jq --argjson outbounds "$(jq -s '{"outbounds": [{"type":"selector","tag":"proxy","outbounds":["main","second"]}] + .[0].outbounds + .[1].outbounds}' "$outbound_main" "$outbound_second")" \ '.outbounds += $outbounds.outbounds' /etc/podkop/sing-box-two-proxy-template.json >/etc/sing-box/config.json rm -f "$outbound_main" "$outbound_second" @@ -111,14 +150,27 @@ start_service() { config_get_bool second_enable "second" "second_enable" "0" config_get second_mode "second" "second_mode" "0" if [ "$second_enable" -eq "0" ] || [ "$second_mode" = "vpn" ]; then - config_get proxy_string main "proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_shadowsocks "$proxy_string" "1602" - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_vless "$proxy_string" "1602" + config_get proxy_config_type main "proxy_config_type" + + if [ "$proxy_config_type" = "outbound" ]; then + config_get outbound_json main "outbound_json" + if [ -n "$outbound_json" ]; then + log "Using JSON outbound configuration" + sing_box_config_outbound_json "$outbound_json" "1602" + else + log "Missing outbound JSON configuration" + return + fi else - log "Unsupported proxy type: $proxy_string" - return + config_get proxy_string main "proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_shadowsocks "$proxy_string" "1602" + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_vless "$proxy_string" "1602" + else + log "Unsupported proxy type or missing configuration" + return + fi fi add_route_tproxy podkop fi @@ -725,6 +777,38 @@ dnsmasq_config_check() { fi } +sing_box_config_outbound_json() { + local json_config="$1" + local listen_port="$2" + + # Create temporary file with base config structure + cat > /tmp/base_config.json << EOF +{ + "log": { + "level": "warn" + }, + "inbounds": [ + { + "type": "tproxy", + "listen": "::", + "listen_port": $listen_port, + "sniff": false + } + ], + "outbounds": [], + "route": { + "auto_detect_interface": true + } +} +EOF + + # Add the outbound config using jq + jq --argjson outbound "$json_config" '.outbounds += [$outbound]' /tmp/base_config.json > /etc/sing-box/config.json + + # Cleanup + rm -f /tmp/base_config.json +} + sing_box_uci() { local config="/etc/config/sing-box" if grep -q "option enabled '0'" "$config" || From 3c6e8366e1e58cc69081b07ed6ab00114b1d46d2 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Sat, 30 Nov 2024 14:41:24 +0300 Subject: [PATCH 02/10] refactor: Optimize configuration generation --- podkop/files/etc/init.d/podkop | 278 ++++++++++++++++++++++++--------- 1 file changed, 203 insertions(+), 75 deletions(-) diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 5aba541..52979ad 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -826,36 +826,61 @@ sing_box_config_shadowsocks() { local STRING="$1" local listen_port="$2" - local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 --decode) - local method=$(echo "$encrypted_part" | cut -d':' -f1) - local password=$(echo "$encrypted_part" | cut -d':' -f2-) + # Определяем тип SS (2022 или old) по наличию : в base64 части + if echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 -d 2>/dev/null | grep -q ":"; then + # Old SS format + local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 --decode) + local method=$(echo "$encrypted_part" | cut -d':' -f1) + local password=$(echo "$encrypted_part" | cut -d':' -f2-) + else + # SS 2022 format + local method_and_password=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) + local method=$(echo "$method_and_password" | cut -d':' -f1) + local password=$(echo "$method_and_password" | cut -d':' -f2- | sed 's/%3D/=/g') + + # Если method в base64, декодируем + if echo "$method" | base64 -d &>/dev/null; then + method=$(echo "$method" | base64 -d) + fi + fi local server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) local port=$(echo "$STRING" | sed -n 's|.*:\([0-9]\+\).*|\1|p') - local label=$(echo "$STRING" | cut -d'#' -f2) - template_config="/etc/podkop/sing-box-shadowsocks-template.json" + # Create base config + cat > /tmp/ss_config.json << EOF +{ + "log": { + "level": "warn" + }, + "inbounds": [ + { + "type": "tproxy", + "listen": "::", + "listen_port": $listen_port, + "sniff": false + } + ], + "outbounds": [ + { + "type": "shadowsocks", + "server": "$server", + "server_port": $port, + "method": "$method", + "password": "$password", + "udp_over_tcp": { + "enabled": true, + "version": 2 + } + } + ], + "route": { + "auto_detect_interface": true + } +} +EOF - jq --arg server "$server" \ - --arg port "$port" \ - --arg method "$method" \ - --arg password "$password" \ - --arg listen_port "$listen_port" \ - '.inbounds[] |= - if .type == "tproxy" then - .listen_port = ($listen_port | tonumber) - else - . - end | - .outbounds[] |= - if .type == "shadowsocks" then - .server = $server | - .server_port = ($port | tonumber) | - .method = $method | - .password = $password - else - . - end' "$template_config" >/etc/sing-box/config.json + mv /tmp/ss_config.json /etc/sing-box/config.json } sing_box_config_vless() { @@ -863,68 +888,171 @@ sing_box_config_vless() { local listen_port="$2" get_param() { - echo "$STRING" | sed -n "s/.*[?&]$1=\([^&?#]*\).*/\1/p" + local param="$1" + local value=$(echo "$STRING" | sed -n "s/.*[?&]$param=\([^&?#]*\).*/\1/p" | sed 's/%2F/\//g; s/%2C/,/g; s/%3D/=/g') + echo "$value" } + # Extract basic parameters uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | awk -F'/' '{print $1}') + port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | cut -d'/' -f1) + # Get all possible parameters type=$(get_param "type") - flow=$(get_param "flow") + security=$(get_param "security") sni=$(get_param "sni") fp=$(get_param "fp") - security=$(get_param "security") + flow=$(get_param "flow") + + # Reality specific pbk=$(get_param "pbk") sid=$(get_param "sid") - encoding=$(get_param "packetEncoding") - alpn=$(echo "$(get_param "alpn" | sed 's/%2C/,/g; s/%2F/\//g')" | jq -R -s -c 'split(",")' | sed 's/\\n//g') - label=$(echo "$STRING" | cut -d'#' -f2) - template_config="/etc/podkop/sing-box-vless-template.json" + # TLS specific + alpn=$(get_param "alpn") + if [ -z "$alpn" ]; then + alpn="h2,http/1.1" + fi + alpn_json=$(echo "$alpn" | tr ',' '\n' | jq -R . | jq -s .) - jq --arg server "$server" \ - --arg port "$port" \ - --arg uuid "$uuid" \ - --arg type "$type" \ - --arg flow "$flow" \ - --arg sni "$sni" \ - --arg fp "$fp" \ - --arg security "$security" \ - --arg pbk "$pbk" \ - --arg sid "$sid" \ - --argjson alpn "$alpn" \ - --arg encoding "$encoding" \ - --arg listen_port "$listen_port" \ - '.inbounds[] |= - if .type == "tproxy" then - .listen_port = ($listen_port | tonumber) - else - . - end | - .outbounds[] |= - (.server = $server | - .server_port = ($port | tonumber) | - .uuid = $uuid | - if $security == "reality" then - if $flow == "" then del(.flow) else .flow = $flow end | - if $encoding == "" then del(.packet_encoding) else .packet_encoding = $encoding end | - .tls.server_name = $sni | - .tls.utls.fingerprint = $fp | - .tls.reality.public_key = $pbk | - .tls.reality.short_id = $sid - elif $security == "tls" then - .tls.alpn = $alpn | - .tls.server_name = $sni | - del(.flow) | - del(.tls.utls) | - del(.tls.reality) - elif $security == "" or $security == "none" then - del(.flow) | - del(.tls) - else - . - end)' "$template_config" >/etc/sing-box/config.json + # WebSocket specific + path=$(get_param "path") + host=$(get_param "host") + + # Create base config + cat > /tmp/vless_config.json << EOF +{ + "log": { + "level": "warn" + }, + "inbounds": [ + { + "type": "tproxy", + "listen": "::", + "listen_port": $listen_port, + "sniff": false + } + ], + "outbounds": [ + { + "type": "vless", + "server": "$server", + "server_port": $port, + "uuid": "$uuid" +EOF + + # Add transport configuration if needed + if [ "$type" = "ws" ]; then + cat >> /tmp/vless_config.json << EOF +, + "transport": { + "type": "ws", + "path": "$path" +EOF + if [ -n "$host" ]; then + cat >> /tmp/vless_config.json << EOF +, + "headers": { + "Host": "$host" + } +EOF + fi + echo " }" >> /tmp/vless_config.json + elif [ "$type" = "grpc" ]; then + cat >> /tmp/vless_config.json << EOF +, + "transport": { + "type": "grpc" + } +EOF + fi + + # Add security configuration + if [ "$security" = "reality" ]; then + if [ -n "$flow" ]; then + echo " ,\"flow\": \"$flow\"" >> /tmp/vless_config.json + fi + cat >> /tmp/vless_config.json << EOF +, + "tls": { + "enabled": true, + "server_name": "$sni", + "utls": { + "enabled": true, + "fingerprint": "$fp" + }, + "reality": { + "enabled": true, + "public_key": "$pbk", + "short_id": "$sid" + } + } +EOF + elif [ "$security" = "tls" ]; then + if [ -n "$flow" ]; then + echo " ,\"flow\": \"$flow\"" >> /tmp/vless_config.json + fi + cat >> /tmp/vless_config.json << EOF +, + "tls": { + "enabled": true, + "server_name": "$sni", + "insecure": $([ "$(get_param 'allowInsecure')" = "1" ] && echo "true" || echo "false"), + "utls": { + "enabled": true, + "fingerprint": "$fp" + } +EOF + if [ -n "$alpn" ]; then + echo " ,\"alpn\": $alpn_json" >> /tmp/vless_config.json + fi + echo " }" >> /tmp/vless_config.json + fi + + # Close outbound and add route + cat >> /tmp/vless_config.json << EOF + } + ], + "route": { + "auto_detect_interface": true + } +} +EOF + + mv /tmp/vless_config.json /etc/sing-box/config.json +} + +sing_box_config_outbound_json() { + local json_config="$1" + local listen_port="$2" + + # Create temporary file with base config structure + cat > /tmp/base_config.json << EOF +{ + "log": { + "level": "warn" + }, + "inbounds": [ + { + "type": "tproxy", + "listen": "::", + "listen_port": $listen_port, + "sniff": false + } + ], + "outbounds": [], + "route": { + "auto_detect_interface": true + } +} +EOF + + # Add the outbound config using jq + jq --argjson outbound "$json_config" '.outbounds += [$outbound]' /tmp/base_config.json > /etc/sing-box/config.json + + # Cleanup + rm -f /tmp/base_config.json } sing_box_config_outbound_shadowsocks() { From 2fe12f3f4d27c3e15bc14fee94dee1c58a4ae6f5 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Sat, 30 Nov 2024 14:42:57 +0300 Subject: [PATCH 03/10] refactor: Add support configurations from String-examle.md --- podkop/files/etc/init.d/podkop | 99 ------------------- ...ing-box-shadowsocks-outbound-template.json | 16 --- .../podkop/sing-box-shadowsocks-template.json | 29 ------ .../sing-box-vless-outbound-template.json | 26 ----- .../etc/podkop/sing-box-vless-template.json | 39 -------- 5 files changed, 209 deletions(-) delete mode 100644 podkop/files/etc/podkop/sing-box-shadowsocks-outbound-template.json delete mode 100644 podkop/files/etc/podkop/sing-box-shadowsocks-template.json delete mode 100644 podkop/files/etc/podkop/sing-box-vless-outbound-template.json delete mode 100644 podkop/files/etc/podkop/sing-box-vless-template.json diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 52979ad..d678b34 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -1055,105 +1055,6 @@ EOF rm -f /tmp/base_config.json } -sing_box_config_outbound_shadowsocks() { - local STRING="$1" - local outbound="$2" - local name="$3" - - local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 --decode) - local method=$(echo "$encrypted_part" | cut -d':' -f1) - local password=$(echo "$encrypted_part" | cut -d':' -f2-) - - local server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - local port=$(echo "$STRING" | cut -d':' -f3 | cut -d'#' -f1) - label=$(echo "$STRING" | cut -d'#' -f2) - - template_config="/etc/podkop/sing-box-shadowsocks-outbound-template.json" - - jq --arg server "$server" \ - --arg port "$port" \ - --arg method "$method" \ - --arg password "$password" \ - --arg tag "$name" \ - '.outbounds[] |= - if .type == "shadowsocks" then - .server = $server | - .server_port = ($port | tonumber) | - .method = $method | - .password = $password | - .tag = $tag - else - . - end' "$template_config" >$outbound -} - -sing_box_config_outbound_vless() { - local STRING="$1" - local outbound="$2" - local name="$3" - - get_param() { - echo "$STRING" | sed -n "s/.*[?&]$1=\([^&?#]*\).*/\1/p" - } - - uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) - server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | awk -F'/' '{print $1}') - - type=$(get_param "type") - flow=$(get_param "flow") - sni=$(get_param "sni") - fp=$(get_param "fp") - security=$(get_param "security") - pbk=$(get_param "pbk") - sid=$(get_param "sid") - alpn=$(echo "$(get_param "alpn" | sed 's/%2C/,/g; s/%2F/\//g')" | jq -R -s -c 'split(",")' | sed 's/\\n//g') - encoding=$(get_param "packetEncoding") - label=$(echo "$STRING" | cut -d'#' -f2) - - template_config="/etc/podkop/sing-box-vless-outbound-template.json" - - jq --arg server "$server" \ - --arg port "$port" \ - --arg uuid "$uuid" \ - --arg type "$type" \ - --arg flow "$flow" \ - --arg sni "$sni" \ - --arg fp "$fp" \ - --arg security "$security" \ - --arg pbk "$pbk" \ - --arg sid "$sid" \ - --argjson alpn "$alpn" \ - --arg encoding "$encoding" \ - --arg tag "$name" \ - '.outbounds[] |= - (.server = $server | - .server_port = ($port | tonumber) | - .uuid = $uuid | - if $security == "reality" then - if $flow == "" then del(.flow) else .flow = $flow end | - if $encoding == "" then del(.packet_encoding) else .packet_encoding = $encoding end | - .tls.server_name = $sni | - .tls.utls.fingerprint = $fp | - .tls.reality.public_key = $pbk | - .tls.reality.short_id = $sid | - .tag = $tag - elif $security == "tls" then - .tls.alpn = $alpn | - .tls.server_name = $sni | - del(.flow) | - del(.tls.utls) | - del(.tls.reality) | - .tag = $tag - elif $security == "" or $security == "none" then - del(.flow) | - del(.tls) | - .tag = $tag - else - . - end)' "$template_config" >$outbound -} - sing_box_config_check() { if ! sing-box -c /etc/sing-box/config.json check >/dev/null 2>&1; then log "Sing-box configuration is invalid" diff --git a/podkop/files/etc/podkop/sing-box-shadowsocks-outbound-template.json b/podkop/files/etc/podkop/sing-box-shadowsocks-outbound-template.json deleted file mode 100644 index d2694f4..0000000 --- a/podkop/files/etc/podkop/sing-box-shadowsocks-outbound-template.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "outbounds": [ - { - "type": "shadowsocks", - "server": "$HOST", - "server_port": "$PORT", - "method": "$METHOD", - "password": "$PASS", - "udp_over_tcp": { - "enabled": true, - "version": 2 - }, - "tag": "$TAG" - } - ] -} \ No newline at end of file diff --git a/podkop/files/etc/podkop/sing-box-shadowsocks-template.json b/podkop/files/etc/podkop/sing-box-shadowsocks-template.json deleted file mode 100644 index da5999f..0000000 --- a/podkop/files/etc/podkop/sing-box-shadowsocks-template.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "log": { - "level": "warn" - }, - "inbounds": [ - { - "type": "tproxy", - "listen": "::", - "listen_port": 1602, - "sniff": false - } - ], - "outbounds": [ - { - "type": "shadowsocks", - "server": "$HOST", - "server_port": "$PORT", - "method": "$METHOD", - "password": "$PASS", - "udp_over_tcp": { - "enabled": true, - "version": 2 - } - } - ], - "route": { - "auto_detect_interface": true - } -} \ No newline at end of file diff --git a/podkop/files/etc/podkop/sing-box-vless-outbound-template.json b/podkop/files/etc/podkop/sing-box-vless-outbound-template.json deleted file mode 100644 index 12a046b..0000000 --- a/podkop/files/etc/podkop/sing-box-vless-outbound-template.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "outbounds": [ - { - "type": "vless", - "server": "$HOST", - "server_port": "$PORT", - "uuid": "$UUID", - "flow": "xtls-rprx-vision", - "tls": { - "enabled": true, - "insecure": false, - "server_name": "$FAKE_SERVER", - "utls": { - "enabled": true, - "fingerprint": "chrome" - }, - "reality": { - "enabled": true, - "public_key": "$PUBLIC_KEY", - "short_id": "$SHORT_ID" - } - }, - "tag": "$TAG" - } - ] -} \ No newline at end of file diff --git a/podkop/files/etc/podkop/sing-box-vless-template.json b/podkop/files/etc/podkop/sing-box-vless-template.json deleted file mode 100644 index 17c9913..0000000 --- a/podkop/files/etc/podkop/sing-box-vless-template.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "log": { - "level": "warn" - }, - "inbounds": [ - { - "type": "tproxy", - "listen": "::", - "listen_port": 1602, - "sniff": false - } - ], - "outbounds": [ - { - "type": "vless", - "server": "$HOST", - "server_port": "$PORT", - "uuid": "$UUID", - "flow": "xtls-rprx-vision", - "tls": { - "enabled": true, - "insecure": false, - "server_name": "$FAKE_SERVER", - "utls": { - "enabled": true, - "fingerprint": "chrome" - }, - "reality": { - "enabled": true, - "public_key": "$PUBLIC_KEY", - "short_id": "$SHORT_ID" - } - } - } - ], - "route": { - "auto_detect_interface": true - } -} \ No newline at end of file From 82c7c290d9915d467a2e4e754e17f7957f71f4dc Mon Sep 17 00:00:00 2001 From: Ivan K Date: Sat, 30 Nov 2024 17:16:16 +0300 Subject: [PATCH 04/10] feat: Enhance custom domain/subnet input --- .../resources/view/podkop/podkop.js | 213 +++++++++++++--- luci-app-podkop/po/ru/podkop.po | 74 +++++- luci-app-podkop/po/templates/podkop.pot | 72 ++++++ podkop/Makefile | 3 - podkop/files/etc/init.d/podkop | 237 +++++++++++++----- .../podkop/sing-box-two-proxy-template.json | 35 --- 6 files changed, 502 insertions(+), 132 deletions(-) delete mode 100644 podkop/files/etc/podkop/sing-box-two-proxy-template.json diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js index 1765eda..72e09ae 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js @@ -115,14 +115,17 @@ return view.extend({ o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('basic', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); - o.default = '0'; + o = s.taboption('basic', form.ListValue, 'custom_domains_list_enabled', _('User Domain List Type'), _('Select how to add your custom domains')); + o.value('disabled', _('Disabled')); + o.value('dynamic', _('Dynamic List')); + o.value('text', _('Text List')); + o.default = 'disabled'; o.rmempty = false; o.ucisection = 'main'; o = s.taboption('basic', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; - o.depends('custom_domains_list_enabled', '1'); + o.depends('custom_domains_list_enabled', 'dynamic'); o.rmempty = false; o.ucisection = 'main'; o.validate = function (section_id, value) { @@ -138,6 +141,31 @@ return view.extend({ return true; }; + o = s.taboption('basic', form.TextValue, 'custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)')); + o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com'; + o.depends('custom_domains_list_enabled', 'text'); + o.rows = 10; + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const domains = value.split(/[,\s\n]/) + .map(d => d.trim()) + .filter(d => d.length > 0); + + const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; + + for (const domain of domains) { + if (!domainRegex.test(domain)) { + return _('Invalid domain format: ' + domain + '. Enter domain without protocol'); + } + } + return true; + }; + o = s.taboption('basic', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs')); o.default = '0'; o.rmempty = false; @@ -164,14 +192,18 @@ return view.extend({ } }; - o = s.taboption('basic', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); - o.default = '0'; + + o = s.taboption('basic', form.ListValue, 'custom_subnets_list_enabled', _('User Subnet List Type'), _('Select how to add your custom subnets')); + o.value('disabled', _('Disabled')); + o.value('dynamic', _('Dynamic List')); + o.value('text', _('Text List (comma/space/newline separated)')); + o.default = 'disabled'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('basic', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 103.21.244.0/22)')); - o.placeholder = 'Subnets list'; - o.depends('custom_subnets_list_enabled', '1'); + o = s.taboption('basic', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses')); + o.placeholder = 'IP or subnet'; + o.depends('custom_subnets_list_enabled', 'dynamic'); o.rmempty = false; o.ucisection = 'main'; o.validate = function (section_id, value) { @@ -179,15 +211,15 @@ return view.extend({ return true; } - const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/; if (!subnetRegex.test(value)) { - return _('Invalid subnet format. Use format: X.X.X.X/Y (like 103.21.244.0/22)'); + return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y'); } + // Разбираем IP и маску const [ip, cidr] = value.split('/'); const ipParts = ip.split('.'); - const cidrNum = parseInt(cidr); for (const part of ipParts) { const num = parseInt(part); @@ -196,13 +228,59 @@ return view.extend({ } } - if (cidrNum < 0 || cidrNum > 32) { - return _('CIDR must be between 0 and 32'); + if (cidr !== undefined) { + const cidrNum = parseInt(cidr); + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32'); + } } return true; }; + o = s.taboption('basic', form.TextValue, 'custom_subnets_text', _('User Subnets List'), _('Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline')); + o.placeholder = '103.21.244.0/22\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 10.10.10.10'; + o.depends('custom_subnets_list_enabled', 'text'); + o.rows = 10; + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + // Split by commas, spaces and newlines + const subnets = value.split(/[,\s\n]/) + .map(s => s.trim()) + .filter(s => s.length > 0); + + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/; + + for (const subnet of subnets) { + if (!subnetRegex.test(subnet)) { + return _('Invalid format: ' + subnet + '. Use format: X.X.X.X or X.X.X.X/Y'); + } + + const [ip, cidr] = subnet.split('/'); + const ipParts = ip.split('.'); + + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP parts must be between 0 and 255 in: ' + subnet); + } + } + + if (cidr !== undefined) { + const cidrNum = parseInt(cidr); + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32 in: ' + subnet); + } + } + } + return true; + }; + o = s.taboption('basic', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs')); o.default = '0'; o.rmempty = false; @@ -409,15 +487,18 @@ return view.extend({ o.rmempty = false; o.ucisection = 'second'; - o = s.taboption('secondary_config', form.Flag, 'second_custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); - o.default = '0'; + o = s.taboption('secondary_config', form.ListValue, 'second_custom_domains_list_enabled', _('User Domain List Type'), _('Select how to add your custom domains')); + o.value('disabled', _('Disabled')); + o.value('dynamic', _('Dynamic List')); + o.value('text', _('Text List')); + o.default = 'disabled'; o.rmempty = false; o.depends('second_enable', '1'); o.ucisection = 'second'; o = s.taboption('secondary_config', form.DynamicList, 'second_custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; - o.depends('second_custom_domains_list_enabled', '1'); + o.depends('second_custom_domains_list_enabled', 'dynamic'); o.rmempty = false; o.ucisection = 'second'; o.validate = function (section_id, value) { @@ -433,15 +514,10 @@ return view.extend({ return true; }; - o = s.taboption('secondary_config', form.Flag, 'second_custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); - o.default = '0'; - o.rmempty = false; - o.depends('second_enable', '1'); - o.ucisection = 'second'; - - o = s.taboption('secondary_config', form.DynamicList, 'second_custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 103.21.244.0/22)')); - o.placeholder = 'Subnets list'; - o.depends('second_custom_subnets_list_enabled', '1'); + o = s.taboption('secondary_config', form.TextValue, 'second_custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)')); + o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com'; + o.depends('second_custom_domains_list_enabled', 'text'); + o.rows = 10; o.rmempty = false; o.ucisection = 'second'; o.validate = function (section_id, value) { @@ -449,15 +525,47 @@ return view.extend({ return true; } - const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; + const domains = value.split(/[,\s\n]/) + .map(d => d.trim()) + .filter(d => d.length > 0); + + const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; + + for (const domain of domains) { + if (!domainRegex.test(domain)) { + return _('Invalid domain format: ' + domain + '. Enter domain without protocol'); + } + } + return true; + }; + + o = s.taboption('secondary_config', form.ListValue, 'second_custom_subnets_list_enabled', _('User Subnet List Type'), _('Select how to add your custom subnets')); + o.value('disabled', _('Disabled')); + o.value('dynamic', _('Dynamic List')); + o.value('text', _('Text List')); + o.default = 'disabled'; + o.rmempty = false; + o.depends('second_enable', '1'); + o.ucisection = 'second'; + + o = s.taboption('secondary_config', form.DynamicList, 'second_custom_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses')); + o.placeholder = 'IP or subnet'; + o.depends('second_custom_subnets_list_enabled', 'dynamic'); + o.rmempty = false; + o.ucisection = 'second'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/; if (!subnetRegex.test(value)) { - return _('Invalid subnet format. Use format: X.X.X.X/Y (like 103.21.244.0/22)'); + return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y'); } const [ip, cidr] = value.split('/'); const ipParts = ip.split('.'); - const cidrNum = parseInt(cidr); for (const part of ipParts) { const num = parseInt(part); @@ -466,13 +574,58 @@ return view.extend({ } } - if (cidrNum < 0 || cidrNum > 32) { - return _('CIDR must be between 0 and 32'); + if (cidr !== undefined) { + const cidrNum = parseInt(cidr); + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32'); + } } return true; }; + o = s.taboption('secondary_config', form.TextValue, 'second_custom_subnets_text', _('User Subnets List'), _('Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline')); + o.placeholder = '103.21.244.0/22\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 10.10.10.10'; + o.depends('second_custom_subnets_list_enabled', 'text'); + o.rows = 10; + o.rmempty = false; + o.ucisection = 'second'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const subnets = value.split(/[,\s\n]/) + .map(s => s.trim()) + .filter(s => s.length > 0); + + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/; + + for (const subnet of subnets) { + if (!subnetRegex.test(subnet)) { + return _('Invalid format: ' + subnet + '. Use format: X.X.X.X or X.X.X.X/Y'); + } + + const [ip, cidr] = subnet.split('/'); + const ipParts = ip.split('.'); + + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP parts must be between 0 and 255 in: ' + subnet); + } + } + + if (cidr !== undefined) { + const cidrNum = parseInt(cidr); + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32 in: ' + subnet); + } + } + } + return true; + }; + return m.render(); } }); \ No newline at end of file diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index 5641275..fb3775b 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -218,4 +218,76 @@ msgid "CIDR must be between 0 and 32" msgstr "CIDR должен быть между 0 и 32" msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)" -msgstr "Неверный формат IP. Используйте формат: X.X.X.X (например: 192.168.1.1)" \ No newline at end of file +msgstr "Неверный формат IP. Используйте формат: X.X.X.X (например: 192.168.1.1)" + +msgid "User Domain List Type" +msgstr "Тип пользовательского списка доменов" + +msgid "Select how to add your custom domains" +msgstr "Выберите способ добавления пользовательских доменов" + +msgid "Disabled" +msgstr "Отключено" + +msgid "Dynamic List" +msgstr "Динамический список" + +msgid "Text List" +msgstr "Текстовый список" + +msgid "User Domains List" +msgstr "Список пользовательских доменов" + +msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)" +msgstr "Введите имена доменов через запятую, пробел или новую строку (пример: sub.example.com, example.com или один домен на строку)" + +msgid "Invalid domain format: %s. Enter domain without protocol" +msgstr "Неверный формат домена: %s. Введите домен без протокола" + +msgid "User Subnet List Type" +msgstr "Тип пользовательского списка подсетей" + +msgid "Select how to add your custom subnets" +msgstr "Выберите способ добавления пользовательских подсетей" + +msgid "Text List (comma/space/newline separated)" +msgstr "Текстовый список (разделенный запятыми/пробелами/новыми строками)" + +msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses" +msgstr "Введите подсети в нотации CIDR (пример: 103.21.244.0/22) или отдельные IP-адреса" + +msgid "User Subnets List" +msgstr "Список пользовательских подсетей" + +msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline" +msgstr "Введите подсети в нотации CIDR или отдельные IP-адреса через запятую, пробел или новую строку" + +msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y" +msgstr "Неверный формат. Используйте формат: X.X.X.X или X.X.X.X/Y" + +msgid "IP parts must be between 0 and 255 in: %s" +msgstr "Части IP-адреса должны быть между 0 и 255 в: %s" + +msgid "Configuration Type" +msgstr "Тип конфигурации" + +msgid "Select how to configure the proxy" +msgstr "Выберите способ настройки прокси" + +msgid "Connection URL" +msgstr "URL подключения" + +msgid "Outbound Config" +msgstr "Конфигурация Outbound" + +msgid "Outbound Configuration" +msgstr "Конфигурация исходящего соединения" + +msgid "Enter complete outbound configuration in JSON format" +msgstr "Введите полную конфигурацию исходящего соединения в формате JSON" + +msgid "JSON must contain at least type, server and server_port fields" +msgstr "JSON должен содержать как минимум поля type, server и server_port" + +msgid "Invalid JSON format" +msgstr "Неверный формат JSON" \ No newline at end of file diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index 1b7af04..d04a25f 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -218,4 +218,76 @@ msgid "CIDR must be between 0 and 32" msgstr "" msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)" +msgstr "" + +msgid "User Domain List Type" +msgstr "" + +msgid "Select how to add your custom domains" +msgstr "" + +msgid "Disabled" +msgstr "" + +msgid "Dynamic List" +msgstr "" + +msgid "Text List" +msgstr "" + +msgid "User Domains List" +msgstr "" + +msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)" +msgstr "" + +msgid "Invalid domain format: %s. Enter domain without protocol" +msgstr "" + +msgid "User Subnet List Type" +msgstr "" + +msgid "Select how to add your custom subnets" +msgstr "" + +msgid "Text List (comma/space/newline separated)" +msgstr "" + +msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses" +msgstr "" + +msgid "User Subnets List" +msgstr "" + +msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline" +msgstr "" + +msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y" +msgstr "" + +msgid "IP parts must be between 0 and 255 in: %s" +msgstr "" + +msgid "Configuration Type" +msgstr "" + +msgid "Select how to configure the proxy" +msgstr "" + +msgid "Connection URL" +msgstr "" + +msgid "Outbound Config" +msgstr "" + +msgid "Outbound Configuration" +msgstr "" + +msgid "Enter complete outbound configuration in JSON format" +msgstr "" + +msgid "JSON must contain at least type, server and server_port fields" +msgstr "" + +msgid "Invalid JSON format" msgstr "" \ No newline at end of file diff --git a/podkop/Makefile b/podkop/Makefile index 35e706a..0e1d273 100644 --- a/podkop/Makefile +++ b/podkop/Makefile @@ -50,9 +50,6 @@ define Package/podkop/install $(INSTALL_DIR) $(1)/etc/config $(INSTALL_CONF) ./files/etc/config/podkop $(1)/etc/config/podkop - $(INSTALL_DIR) $(1)/etc/podkop - $(INSTALL_DATA) ./files/etc/podkop/* $(1)/etc/podkop/ - $(INSTALL_DIR) $(1)/etc/hotplug.d/iface $(INSTALL_DATA) ./files/etc/hotplug.d/iface/50-podkop $(1)/etc/hotplug.d/iface/50-podkop endef diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index d678b34..8b06e3a 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -96,20 +96,25 @@ start_service() { if [ "$proxy_config_type" = "outbound" ]; then config_get outbound_json main "outbound_json" if [ -n "$outbound_json" ]; then - echo "$outbound_json" > "$outbound_main" - jq '.tag = "main"' "$outbound_main" > "${outbound_main}.tmp" && mv "${outbound_main}.tmp" "$outbound_main" + echo '{"outbounds":[' > "$outbound_main" + echo "$outbound_json" | jq '. + {tag: "main"}' >> "$outbound_main" + echo ']}' >> "$outbound_main" else log "Missing main outbound JSON configuration" + rm -f "$outbound_main" "$outbound_second" return fi else config_get proxy_string main "proxy_string" if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_main" main + sing_box_config_shadowsocks "$proxy_string" "1602" + jq '.outbounds[0] + {tag: "main"} | {outbounds: [.]}' /etc/sing-box/config.json > "$outbound_main" elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_outbound_vless "$proxy_string" "$outbound_main" main + sing_box_config_vless "$proxy_string" "1602" + jq '.outbounds[0] + {tag: "main"} | {outbounds: [.]}' /etc/sing-box/config.json > "$outbound_main" else log "Unsupported proxy type or missing configuration for main" + rm -f "$outbound_main" "$outbound_second" return fi fi @@ -119,26 +124,62 @@ start_service() { if [ "$proxy_config_type" = "outbound" ]; then config_get outbound_json second "second_outbound_json" if [ -n "$outbound_json" ]; then - echo "$outbound_json" > "$outbound_second" - jq '.tag = "second"' "$outbound_second" > "${outbound_second}.tmp" && mv "${outbound_second}.tmp" "$outbound_second" + echo '{"outbounds":[' > "$outbound_second" + echo "$outbound_json" | jq '. + {tag: "second"}' >> "$outbound_second" + echo ']}' >> "$outbound_second" else log "Missing second outbound JSON configuration" + rm -f "$outbound_main" "$outbound_second" return fi else config_get proxy_string "second" "second_proxy_string" if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_second" second + sing_box_config_shadowsocks "$proxy_string" "1603" + jq '.outbounds[0] + {tag: "second"} | {outbounds: [.]}' /etc/sing-box/config.json > "$outbound_second" elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_outbound_vless "$proxy_string" "$outbound_second" second + sing_box_config_vless "$proxy_string" "1603" + jq '.outbounds[0] + {tag: "second"} | {outbounds: [.]}' /etc/sing-box/config.json > "$outbound_second" else log "Unsupported proxy type or missing configuration for second" + rm -f "$outbound_main" "$outbound_second" return fi fi - jq --argjson outbounds "$(jq -s '{"outbounds": [{"type":"selector","tag":"proxy","outbounds":["main","second"]}] + .[0].outbounds + .[1].outbounds}' "$outbound_main" "$outbound_second")" \ - '.outbounds += $outbounds.outbounds' /etc/podkop/sing-box-two-proxy-template.json >/etc/sing-box/config.json + jq -s '{ + "log": {"level": "warn"}, + "inbounds": [ + { + "type": "tproxy", + "listen": "::", + "listen_port": 1602, + "sniff": false, + "tag": "main" + }, + { + "type": "tproxy", + "listen": "::", + "listen_port": 1603, + "sniff": false, + "tag": "second" + } + ], + "outbounds": (.[0].outbounds + .[1].outbounds), + "route": { + "rules": [ + { + "inbound": "main", + "outbound": "main" + }, + { + "inbound": "second", + "outbound": "second" + } + ], + "auto_detect_interface": true + } + }' "$outbound_main" "$outbound_second" > /etc/sing-box/config.json rm -f "$outbound_main" "$outbound_second" @@ -326,6 +367,7 @@ remove_cron_job() { } list_update() { + # Main domains processing config_get_bool domain_list_enabled "main" "domain_list_enabled" "0" if [ "$domain_list_enabled" -eq 1 ]; then log "Adding a common domains list" @@ -335,15 +377,24 @@ list_update() { dnsmasq_config_check podkop-domains.lst fi - config_get_bool custom_domains_list_enabled "main" "custom_domains_list_enabled" "0" - if [ "$custom_domains_list_enabled" -eq 1 ]; then + # Main custom domains processing + config_get custom_domains_list_type "main" "custom_domains_list_enabled" "disabled" + if [ "$custom_domains_list_type" != "disabled" ]; then log "Adding a custom domains list" add_set "podkop_domains" "main" rm -f /tmp/dnsmasq.d/podkop-custom-domains.lst - config_list_foreach main custom_domains "list_custom_domains_create" "podkop" + + if [ "$custom_domains_list_type" = "dynamic" ]; then + config_list_foreach main custom_domains "list_custom_domains_create" "podkop" + elif [ "$custom_domains_list_type" = "text" ]; then + config_get custom_domains_text main "custom_domains_text" + process_domains_text "$custom_domains_text" "podkop" + fi + dnsmasq_config_check podkop-custom-domains.lst fi + # Main custom download domains config_get_bool custom_download_domains_list_enabled "main" "custom_download_domains_list_enabled" "0" if [ "$custom_download_domains_list_enabled" -eq 1 ]; then log "Adding a custom domains list from URL" @@ -351,6 +402,7 @@ list_update() { config_list_foreach main custom_download_domains "list_custom_download_domains_create" "podkop" fi + # Main domains delist config_get_bool delist_domains_enabled "main" "delist_domains_enabled" "0" if [ "$delist_domains_enabled" -eq 1 ] && [ "$domain_list_enabled" -eq 1 ]; then log "Exclude domains from the common list" @@ -358,20 +410,56 @@ list_update() { dnsmasq_config_check podkop-domains.lst fi - if [ "$domain_list_enabled" -eq 1 ] || [ "$custom_domains_list_enabled" -eq 1 ]; then - /etc/init.d/dnsmasq restart + # Main subnets processing + config_get_bool subnets_list_enabled "main" "subnets_list_enabled" "0" + if [ "$subnets_list_enabled" -eq 1 ]; then + log "Adding a subnets from list" + mkdir -p /tmp/podkop + add_set "podkop_subnets" "main" + config_list_foreach main subnets "list_subnets_download" fi - config_get_bool second_custom_domains_list_enabled "second" "second_custom_domains_list_enabled" "0" - if [ "$second_custom_domains_list_enabled" -eq 1 ]; then + # Main custom subnets + config_get custom_subnets_list_type "main" "custom_subnets_list_enabled" "disabled" + if [ "$custom_subnets_list_type" != "disabled" ]; then + log "Adding a custom subnets list" + add_set "podkop_subnets" "main" + + if [ "$custom_subnets_list_type" = "dynamic" ]; then + config_list_foreach main custom_subnets list_custom_subnets_preprocess "podkop" + elif [ "$custom_subnets_list_type" = "text" ]; then + config_get custom_subnets_text main "custom_subnets_text" + process_subnets_text "$custom_subnets_text" "podkop" + fi + fi + + # Main custom download subnets + config_get_bool custom_download_subnets_list_enabled "main" "custom_download_subnets_list_enabled" "0" + if [ "$custom_download_subnets_list_enabled" -eq 1 ]; then + log "Adding a subnets from URL" + mkdir -p /tmp/podkop + add_set "podkop_subnets" "main" + config_list_foreach main custom_download_subnets "list_subnets_download" + fi + + # Second custom domains processing + config_get second_custom_domains_list_type "second" "second_custom_domains_list_enabled" "disabled" + if [ "$second_custom_domains_list_type" != "disabled" ]; then log "Adding a custom domains list. Second podkop" add_set "podkop2_domains" "second" rm -f /tmp/dnsmasq.d/podkop2-custom-domains.lst - config_list_foreach second second_custom_domains "list_delist_domains" - config_list_foreach second second_custom_domains "list_custom_domains_create" "podkop2" + + if [ "$second_custom_domains_list_type" = "dynamic" ]; then + config_list_foreach second second_custom_domains "list_custom_domains_create" "podkop2" + elif [ "$second_custom_domains_list_type" = "text" ]; then + config_get second_custom_domains_text second "second_custom_domains_text" + process_domains_text "$second_custom_domains_text" "podkop2" + fi + dnsmasq_config_check podkop2-custom-domains.lst fi + # Second service domains config_get_bool second_domain_service_enabled "second" "second_domain_service_enabled" "0" if [ "$second_domain_service_enabled" -eq 1 ]; then log "Adding a service for podkop2" @@ -382,38 +470,26 @@ list_update() { dnsmasq_config_check podkop2-domains.lst fi - if [ "$second_custom_domains_list_enabled" -eq 1 ] || [ "$second_domain_service_enabled" -eq 1 ]; then - /etc/init.d/dnsmasq restart - fi - - config_get_bool subnets_list_enabled "main" "subnets_list_enabled" "0" - if [ "$subnets_list_enabled" -eq 1 ]; then - log "Adding a subnets from list" - mkdir -p /tmp/podkop - add_set "podkop_subnets" "main" - config_list_foreach main subnets "list_subnets_download" - fi - - config_get_bool custom_download_subnets_list_enabled "main" "custom_download_subnets_list_enabled" "0" - if [ "$custom_download_subnets_list_enabled" -eq 1 ]; then - log "Adding a subnets from URL" - mkdir -p /tmp/podkop - add_set "podkop_subnets" "main" - config_list_foreach main custom_download_subnets "list_subnets_download" - fi - - config_get_bool custom_subnets_list_enabled "main" "custom_subnets_list_enabled" "0" - if [ "$custom_subnets_list_enabled" -eq 1 ]; then - log "Adding a custom subnets list" - add_set "podkop_subnets" "main" - config_list_foreach main custom_subnets "list_custom_subnets_create" "podkop" - fi - - config_get_bool second_custom_subnets_list_enabled "second" "second_custom_subnets_list_enabled" "0" - if [ "$second_custom_subnets_list_enabled" -eq 1 ]; then + # Second custom subnets + config_get second_custom_subnets_list_type "second" "second_custom_subnets_list_enabled" "disabled" + if [ "$second_custom_subnets_list_type" != "disabled" ]; then log "Adding a custom subnets list. Second" add_set "podkop2_subnets" "second" - config_list_foreach second second_custom_subnets "list_custom_subnets_create" "podkop2" + + if [ "$second_custom_subnets_list_type" = "dynamic" ]; then + config_list_foreach second second_custom_subnets list_custom_subnets_preprocess "podkop2" + elif [ "$second_custom_subnets_list_type" = "text" ]; then + config_get second_custom_subnets_text second "second_custom_subnets_text" + process_subnets_text "$second_custom_subnets_text" "podkop2" + fi + fi + + # Restart dnsmasq if needed + if [ "$domain_list_enabled" -eq 1 ] || [ "$custom_domains_list_type" != "disabled" ] || \ + [ "$custom_download_domains_list_enabled" -eq 1 ] || \ + [ "$second_custom_domains_list_type" != "disabled" ] || \ + [ "$second_domain_service_enabled" -eq 1 ]; then + /etc/init.d/dnsmasq restart fi } @@ -889,38 +965,33 @@ sing_box_config_vless() { get_param() { local param="$1" - local value=$(echo "$STRING" | sed -n "s/.*[?&]$param=\([^&?#]*\).*/\1/p" | sed 's/%2F/\//g; s/%2C/,/g; s/%3D/=/g') + local value=$(echo "$STRING" | sed -n "s/.*[?&]$param=\([^&?#]*\).*/\1/p") + value=$(echo "$value" | sed 's/%2F/\//gi; s/%2C/,/gi; s/%3D/=/gi; s/%2B/+/gi') echo "$value" } - # Extract basic parameters uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | cut -d'/' -f1) - # Get all possible parameters type=$(get_param "type") security=$(get_param "security") sni=$(get_param "sni") fp=$(get_param "fp") flow=$(get_param "flow") - # Reality specific pbk=$(get_param "pbk") sid=$(get_param "sid") - # TLS specific alpn=$(get_param "alpn") if [ -z "$alpn" ]; then alpn="h2,http/1.1" fi alpn_json=$(echo "$alpn" | tr ',' '\n' | jq -R . | jq -s .) - # WebSocket specific path=$(get_param "path") host=$(get_param "host") - # Create base config cat > /tmp/vless_config.json << EOF { "log": { @@ -942,7 +1013,6 @@ sing_box_config_vless() { "uuid": "$uuid" EOF - # Add transport configuration if needed if [ "$type" = "ws" ]; then cat >> /tmp/vless_config.json << EOF , @@ -968,7 +1038,6 @@ EOF EOF fi - # Add security configuration if [ "$security" = "reality" ]; then if [ -n "$flow" ]; then echo " ,\"flow\": \"$flow\"" >> /tmp/vless_config.json @@ -1010,7 +1079,6 @@ EOF echo " }" >> /tmp/vless_config.json fi - # Close outbound and add route cat >> /tmp/vless_config.json << EOF } ], @@ -1027,7 +1095,6 @@ sing_box_config_outbound_json() { local json_config="$1" local listen_port="$2" - # Create temporary file with base config structure cat > /tmp/base_config.json << EOF { "log": { @@ -1048,10 +1115,7 @@ sing_box_config_outbound_json() { } EOF - # Add the outbound config using jq jq --argjson outbound "$json_config" '.outbounds += [$outbound]' /tmp/base_config.json > /etc/sing-box/config.json - - # Cleanup rm -f /tmp/base_config.json } @@ -1060,4 +1124,51 @@ sing_box_config_check() { log "Sing-box configuration is invalid" return fi +} + +process_domains_text() { + local text="$1" + local name="$2" + + local tmp_file=$(mktemp) + echo "$text" > "$tmp_file" + + sed 's/[, ]\+/\n/g' "$tmp_file" | while IFS= read -r domain; do + domain=$(echo "$domain" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + if [ -n "$domain" ]; then + list_custom_domains_create "$domain" "$name" + fi + done + + rm -f "$tmp_file" +} + +process_subnets_text() { + local text="$1" + local name="$2" + + local tmp_file=$(mktemp) + echo "$text" > "$tmp_file" + + sed 's/[, ]\+/\n/g' "$tmp_file" | while IFS= read -r subnet; do + subnet=$(echo "$subnet" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + if [ -n "$subnet" ]; then + if ! echo "$subnet" | grep -q "/"; then + subnet="$subnet/32" + fi + list_custom_subnets_create "$subnet" "$name" + fi + done + + rm -f "$tmp_file" +} + +list_custom_subnets_preprocess() { + local subnet="$1" + local name="$2" + + if ! echo "$subnet" | grep -q "/"; then + subnet="$subnet/32" + fi + list_custom_subnets_create "$subnet" "$name" } \ No newline at end of file diff --git a/podkop/files/etc/podkop/sing-box-two-proxy-template.json b/podkop/files/etc/podkop/sing-box-two-proxy-template.json deleted file mode 100644 index 77e61a7..0000000 --- a/podkop/files/etc/podkop/sing-box-two-proxy-template.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "log": { - "level": "warn" - }, - "inbounds": [ - { - "type": "tproxy", - "listen": "::", - "listen_port": 1602, - "sniff": false, - "tag": "main" - }, - { - "type": "tproxy", - "listen": "::", - "listen_port": 1603, - "sniff": false, - "tag": "second" - } - ], - "outbounds": [], - "route": { - "rules": [ - { - "inbound": "main", - "outbound": "main" - }, - { - "inbound": "second", - "outbound": "second" - } - ], - "auto_detect_interface": true - } -} \ No newline at end of file From 00ee716236c4ba8cb6fd1d1b69424179cb449cde Mon Sep 17 00:00:00 2001 From: Ivan K Date: Sun, 1 Dec 2024 12:58:56 +0300 Subject: [PATCH 05/10] Feature: Support tcp type to vless config --- podkop/files/etc/init.d/podkop | 65 ++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 8b06e3a..5811992 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -966,33 +966,26 @@ sing_box_config_vless() { get_param() { local param="$1" local value=$(echo "$STRING" | sed -n "s/.*[?&]$param=\([^&?#]*\).*/\1/p") - value=$(echo "$value" | sed 's/%2F/\//gi; s/%2C/,/gi; s/%3D/=/gi; s/%2B/+/gi') + value=$(echo "$value" | sed 's/%2F/\//g; s/%2C/,/g; s/%3D/=/g; s/%2B/+/g; s/%20/ /g' | tr -d '\n' | tr -d '\r') echo "$value" } - uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) - server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | cut -d'/' -f1) + uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g') + server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g') + port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | cut -d'/' -f1 | cut -d'#' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g') type=$(get_param "type") security=$(get_param "security") sni=$(get_param "sni") fp=$(get_param "fp") flow=$(get_param "flow") - pbk=$(get_param "pbk") sid=$(get_param "sid") - - alpn=$(get_param "alpn") - if [ -z "$alpn" ]; then - alpn="h2,http/1.1" - fi - alpn_json=$(echo "$alpn" | tr ',' '\n' | jq -R . | jq -s .) - path=$(get_param "path") host=$(get_param "host") + spx=$(get_param "spx") - cat > /tmp/vless_config.json << EOF + cat > /tmp/vless_temp.json << EOF { "log": { "level": "warn" @@ -1010,39 +1003,43 @@ sing_box_config_vless() { "type": "vless", "server": "$server", "server_port": $port, - "uuid": "$uuid" + "uuid": "$uuid", + "packet_encoding": "", + "domain_strategy": "" EOF + if [ -n "$flow" ]; then + echo " ,\"flow\": \"$flow\"" >> /tmp/vless_temp.json + fi + if [ "$type" = "ws" ]; then - cat >> /tmp/vless_config.json << EOF + cat >> /tmp/vless_temp.json << EOF , "transport": { "type": "ws", "path": "$path" EOF if [ -n "$host" ]; then - cat >> /tmp/vless_config.json << EOF + cat >> /tmp/vless_temp.json << EOF , "headers": { "Host": "$host" } EOF fi - echo " }" >> /tmp/vless_config.json + echo " }" >> /tmp/vless_temp.json elif [ "$type" = "grpc" ]; then - cat >> /tmp/vless_config.json << EOF + cat >> /tmp/vless_temp.json << EOF , "transport": { "type": "grpc" } EOF + elif [ "$type" = "tcp" ]; then fi if [ "$security" = "reality" ]; then - if [ -n "$flow" ]; then - echo " ,\"flow\": \"$flow\"" >> /tmp/vless_config.json - fi - cat >> /tmp/vless_config.json << EOF + cat >> /tmp/vless_temp.json << EOF , "tls": { "enabled": true, @@ -1059,10 +1056,7 @@ EOF } EOF elif [ "$security" = "tls" ]; then - if [ -n "$flow" ]; then - echo " ,\"flow\": \"$flow\"" >> /tmp/vless_config.json - fi - cat >> /tmp/vless_config.json << EOF + cat >> /tmp/vless_temp.json << EOF , "tls": { "enabled": true, @@ -1074,12 +1068,13 @@ EOF } EOF if [ -n "$alpn" ]; then - echo " ,\"alpn\": $alpn_json" >> /tmp/vless_config.json + local alpn_json=$(echo "$alpn" | tr ',' '\n' | jq -R . | jq -s .) + echo " ,\"alpn\": $alpn_json" >> /tmp/vless_temp.json fi - echo " }" >> /tmp/vless_config.json + echo " }" >> /tmp/vless_temp.json fi - cat >> /tmp/vless_config.json << EOF + cat >> /tmp/vless_temp.json << EOF } ], "route": { @@ -1088,7 +1083,17 @@ EOF } EOF - mv /tmp/vless_config.json /etc/sing-box/config.json + if jq . /tmp/vless_temp.json >/tmp/vless_config.json 2>/dev/null; then + mv /tmp/vless_config.json /etc/sing-box/config.json + echo "Config created successfully" + else + echo "Error: Invalid JSON config generated" + cat /tmp/vless_temp.json + rm -f /tmp/vless_temp.json + return 1 + fi + + rm -f /tmp/vless_temp.json } sing_box_config_outbound_json() { From 63acd224e88895a8c27cbf3ad124fdfd00cbbab5 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Sun, 1 Dec 2024 14:12:56 +0300 Subject: [PATCH 06/10] Fix: remove duplicate function sing_box_config_outbound_json --- podkop/files/etc/init.d/podkop | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 5811992..1567f4f 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -857,7 +857,6 @@ sing_box_config_outbound_json() { local json_config="$1" local listen_port="$2" - # Create temporary file with base config structure cat > /tmp/base_config.json << EOF { "log": { @@ -878,10 +877,7 @@ sing_box_config_outbound_json() { } EOF - # Add the outbound config using jq jq --argjson outbound "$json_config" '.outbounds += [$outbound]' /tmp/base_config.json > /etc/sing-box/config.json - - # Cleanup rm -f /tmp/base_config.json } @@ -1096,34 +1092,6 @@ EOF rm -f /tmp/vless_temp.json } -sing_box_config_outbound_json() { - local json_config="$1" - local listen_port="$2" - - cat > /tmp/base_config.json << EOF -{ - "log": { - "level": "warn" - }, - "inbounds": [ - { - "type": "tproxy", - "listen": "::", - "listen_port": $listen_port, - "sniff": false - } - ], - "outbounds": [], - "route": { - "auto_detect_interface": true - } -} -EOF - - jq --argjson outbound "$json_config" '.outbounds += [$outbound]' /tmp/base_config.json > /etc/sing-box/config.json - rm -f /tmp/base_config.json -} - sing_box_config_check() { if ! sing-box -c /etc/sing-box/config.json check >/dev/null 2>&1; then log "Sing-box configuration is invalid" From 2d05025533d1030b2566a0183e60e94129fb39a6 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Wed, 4 Dec 2024 11:09:23 +0300 Subject: [PATCH 07/10] Fix: if/fi construction --- podkop/files/etc/init.d/podkop | 1 + 1 file changed, 1 insertion(+) diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 1567f4f..58d6375 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -1032,6 +1032,7 @@ EOF } EOF elif [ "$type" = "tcp" ]; then + : # tcp doesn't need additional transport configuration fi if [ "$security" = "reality" ]; then From e66ee9dda61eae41213106b37955fec07c536a5b Mon Sep 17 00:00:00 2001 From: Ivan K Date: Mon, 9 Dec 2024 12:23:51 +0300 Subject: [PATCH 08/10] feat: Add declare ALPN variable --- podkop/files/etc/init.d/podkop | 1 + 1 file changed, 1 insertion(+) diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 58d6375..fdf7fb9 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -980,6 +980,7 @@ sing_box_config_vless() { path=$(get_param "path") host=$(get_param "host") spx=$(get_param "spx") + alpn=$(get_param "alpn") cat > /tmp/vless_temp.json << EOF { From a4fcbfd70a470ef8b8bd978cf75733f9cd91dcb7 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Thu, 12 Dec 2024 16:34:56 +0300 Subject: [PATCH 09/10] chore: move to jq --- podkop/files/etc/init.d/podkop | 192 ++++++++++++++------------------- 1 file changed, 80 insertions(+), 112 deletions(-) diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index fdf7fb9..94715a8 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -970,128 +970,96 @@ sing_box_config_vless() { server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g') port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | cut -d'/' -f1 | cut -d'#' -f1 | tr -d '\n' | tr -d '\r' | sed 's/False//g') - type=$(get_param "type") - security=$(get_param "security") - sni=$(get_param "sni") - fp=$(get_param "fp") - flow=$(get_param "flow") - pbk=$(get_param "pbk") - sid=$(get_param "sid") - path=$(get_param "path") - host=$(get_param "host") - spx=$(get_param "spx") - alpn=$(get_param "alpn") - - cat > /tmp/vless_temp.json << EOF -{ - "log": { - "level": "warn" - }, - "inbounds": [ - { - "type": "tproxy", - "listen": "::", - "listen_port": $listen_port, - "sniff": false - } - ], - "outbounds": [ - { - "type": "vless", - "server": "$server", - "server_port": $port, - "uuid": "$uuid", - "packet_encoding": "", - "domain_strategy": "" -EOF - - if [ -n "$flow" ]; then - echo " ,\"flow\": \"$flow\"" >> /tmp/vless_temp.json - fi - - if [ "$type" = "ws" ]; then - cat >> /tmp/vless_temp.json << EOF -, - "transport": { - "type": "ws", - "path": "$path" -EOF - if [ -n "$host" ]; then - cat >> /tmp/vless_temp.json << EOF -, - "headers": { - "Host": "$host" - } -EOF - fi - echo " }" >> /tmp/vless_temp.json - elif [ "$type" = "grpc" ]; then - cat >> /tmp/vless_temp.json << EOF -, - "transport": { - "type": "grpc" - } -EOF - elif [ "$type" = "tcp" ]; then - : # tcp doesn't need additional transport configuration - fi - - if [ "$security" = "reality" ]; then - cat >> /tmp/vless_temp.json << EOF -, - "tls": { - "enabled": true, - "server_name": "$sni", - "utls": { - "enabled": true, - "fingerprint": "$fp" + jq -n \ + --arg listen_port "$listen_port" \ + --arg server "$server" \ + --argjson port "$port" \ + --arg uuid "$uuid" \ + --arg type "$(get_param "type")" \ + --arg flow "$(get_param "flow")" \ + --arg sni "$(get_param "sni")" \ + --arg fp "$(get_param "fp")" \ + --arg security "$(get_param "security")" \ + --arg pbk "$(get_param "pbk")" \ + --arg sid "$(get_param "sid")" \ + --arg alpn "$(get_param "alpn")" \ + --arg path "$(get_param "path")" \ + --arg host "$(get_param "host")" \ + --arg spx "$(get_param "spx")" \ + --arg insecure "$(get_param "allowInsecure")" \ + '{ + "log": { + "level": "warn" }, - "reality": { - "enabled": true, - "public_key": "$pbk", - "short_id": "$sid" + "inbounds": [ + { + "type": "tproxy", + "listen": "::", + "listen_port": ($listen_port|tonumber), + "sniff": false + } + ], + "outbounds": [ + { + "type": "vless", + "server": $server, + "server_port": ($port|tonumber), + "uuid": $uuid, + "packet_encoding": "", + "domain_strategy": "" + } + ], + "route": { + "auto_detect_interface": true } - } -EOF - elif [ "$security" = "tls" ]; then - cat >> /tmp/vless_temp.json << EOF -, - "tls": { - "enabled": true, - "server_name": "$sni", - "insecure": $([ "$(get_param 'allowInsecure')" = "1" ] && echo "true" || echo "false"), - "utls": { - "enabled": true, - "fingerprint": "$fp" + } | + + if $flow != "" then .outbounds[0].flow = $flow else . end | + + if $type == "ws" then + .outbounds[0].transport = { + "type": "ws", + "path": $path + } | + if $host != "" then + .outbounds[0].transport.headers = { + "Host": $host + } + else . end + elif $type == "grpc" then + .outbounds[0].transport = { + "type": "grpc" } -EOF - if [ -n "$alpn" ]; then - local alpn_json=$(echo "$alpn" | tr ',' '\n' | jq -R . | jq -s .) - echo " ,\"alpn\": $alpn_json" >> /tmp/vless_temp.json - fi - echo " }" >> /tmp/vless_temp.json - fi + else . end | - cat >> /tmp/vless_temp.json << EOF - } - ], - "route": { - "auto_detect_interface": true - } -} -EOF + if $security == "reality" or $security == "tls" then + .outbounds[0].tls = { + "enabled": true, + "server_name": $sni, + "utls": { + "enabled": true, + "fingerprint": $fp + }, + "insecure": ($insecure == "1") + } | + if $alpn != "" then + .outbounds[0].tls.alpn = ($alpn | split(",")) + else . end | + if $security == "reality" then + .outbounds[0].tls.reality = { + "enabled": true, + "public_key": $pbk, + "short_id": $sid + } + else . end + else . end' > /etc/sing-box/config.json - if jq . /tmp/vless_temp.json >/tmp/vless_config.json 2>/dev/null; then - mv /tmp/vless_config.json /etc/sing-box/config.json + if [ $? -eq 0 ]; then echo "Config created successfully" else echo "Error: Invalid JSON config generated" - cat /tmp/vless_temp.json - rm -f /tmp/vless_temp.json return 1 fi - - rm -f /tmp/vless_temp.json } sing_box_config_check() { From e4eb4fe67ac9f4399cbbb3f0af8752ec70714d65 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Thu, 12 Dec 2024 16:59:09 +0300 Subject: [PATCH 10/10] chore: add missing default vars --- podkop/files/etc/config/podkop | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/podkop/files/etc/config/podkop b/podkop/files/etc/config/podkop index e629944..7d9231d 100644 --- a/podkop/files/etc/config/podkop +++ b/podkop/files/etc/config/podkop @@ -1,17 +1,21 @@ config main 'main' option mode '' option interface '' - option proxy_string '' + option proxy_config_type '' + #option outbound_json '' + #option proxy_string '' option domain_list_enabled '1' option domain_list 'ru_inside' option subnets_list_enabled '0' #list subnets 'twitter' - option custom_domains_list_enabled '0' + option custom_domains_list_type 'disable' #list custom_domains '' + #option custom_domains_text '' option custom_download_domains_list_enabled '0' #list custom_download_domains '' - option custom_subnets_list_enabled '0' + option custom_domains_list_type 'disable' #list custom_subnets '' + #custom_subnets_text '' option custom_download_subnets_list_enabled '0' #list custom_download_subnets '' option all_traffic_from_ip_enabled '0' @@ -24,15 +28,20 @@ config main 'main' option socks5 '0' option exclude_ntp '0' option update_interval '' + option custom_domains_text config second 'second' option second_enable '0' option second_mode 'proxy' option second_interface '' - option second_proxy_string '' + option second_proxy_config_type '' + #option second_outbound_json '' + #option second_proxy_string '' option second_domain_service_enabled '0' #list second_service_list 'youtube' - option second_custom_domains_list_enabled '0' + option second_custom_domains_type 'disable' #list second_custom_domains 'ifconfig.io' - option second_custom_subnets_list_enabled '0' - #list second_custom_subnets '' \ No newline at end of file + #option second_custom_domains_text '' + option second_custom_subnets_type 'disable' + #list second_custom_subnets '' + #porion second_custom_subnets_text '' \ No newline at end of file