diff --git a/generate-config.sh b/generate-config.sh index ad39ae4..24040ab 100644 --- a/generate-config.sh +++ b/generate-config.sh @@ -1,7 +1,22 @@ #!/bin/sh -WARP_CONF="/app/warp.conf" -OUTPUT_CONFIG="/app/config.json" +WARP_CONF="${WARP_CONF:-/etc/sing-box-warp/warp.conf}" +OUTPUT_CONFIG="${OUTPUT_CONFIG:-/opt/sing-box-warp/config.json}" +ENABLE_TUN_FILE="${ENABLE_TUN_FILE:-/etc/sing-box-warp/enable-tun}" + +load_enable_tun() { + local value + + value=$(trim "${ENABLE_TUN:-}") + if [ -z "$value" ] && [ -f "$ENABLE_TUN_FILE" ]; then + value=$(trim "$(cat "$ENABLE_TUN_FILE")") + fi + + case "$value" in + 1|yes|true|y|Y|on|ON) ENABLE_TUN=1 ;; + *) ENABLE_TUN=0 ;; + esac +} urldecode() { echo "$1" | sed 's/%3[dD]/=/g; s/%2[bB]/+/g; s/%2[fF]/\//g; s/%2[cC]/,/g' @@ -11,6 +26,43 @@ trim() { echo "$1" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' } +# Default-route NIC (eth0, ens3, enp0s3, …); override with TUN_EXCLUDE_INTERFACE +detect_default_interface() { + local iface + + iface=$(trim "${TUN_EXCLUDE_INTERFACE:-}") + if [ -n "$iface" ]; then + echo "$iface" + return 0 + fi + + if command -v ip >/dev/null 2>&1; then + iface=$(ip -4 route show default 2>/dev/null \ + | awk '/default/ { for (i = 1; i <= NF; i++) if ($i == "dev") { print $(i + 1); exit } }') + if [ -n "$iface" ]; then + echo "$iface" + return 0 + fi + + iface=$(ip -4 route get 1.1.1.1 2>/dev/null \ + | awk '{ for (i = 1; i <= NF; i++) if ($i == "dev") { print $(i + 1); exit } }') + if [ -n "$iface" ]; then + echo "$iface" + return 0 + fi + fi + + if [ -r /proc/net/route ]; then + iface=$(awk '$2 == "00000000" && $1 != "Iface" { print $1; exit }' /proc/net/route) + if [ -n "$iface" ]; then + echo "$iface" + return 0 + fi + fi + + return 1 +} + normalize_cidr() { local addr local suffix @@ -83,7 +135,7 @@ parse_from_wg_url() { ALLOWED_IPS="0.0.0.0/0" TAG="wireguard-out" - LOG_LEVEL="debug" + LOG_LEVEL="error" } parse_from_ini() { @@ -180,6 +232,56 @@ parse_from_ini() { } write_config() { + if [ "$ENABLE_TUN" = "1" ]; then + EXCLUDE_IFACE=$(detect_default_interface) || { + echo "Warning: could not detect default network interface, using eth0" >&2 + EXCLUDE_IFACE="eth0" + } + echo "TUN enabled, exclude_interface: $EXCLUDE_IFACE" >&2 + TUN_INBOUND_PART=$(cat <&2 + TUN_INBOUND_PART="" + TUN_ROUTE_RULE_PART="" + fi + if [ -n "$I1" ] || [ -n "$I2" ]; then H4_COMMA="," else @@ -264,7 +366,7 @@ $I2_LINE "type": "mixed", "tag": "mixed-in", "listen_port": 2080 - } + }$TUN_INBOUND_PART ], "outbounds": [ { @@ -273,7 +375,97 @@ $I2_LINE } ], "route": { - "final": "$TAG", + "rules": [ + { + "action": "sniff" + }$TUN_ROUTE_RULE_PART, + { + "ip_is_private": true, + "outbound": "direct" + }, + { + "inbound": "mixed-in", + "outbound": "wireguard-out" + } + { + "protocol": "dns", + "action": "hijack-dns" + }, + { + "domain_suffix": [ + "myip.wtf", + "my-ip.io", + "ipify.org", + "myip.la", + "ip-api.com", + "ipleak.net", + "1e100.net", + "browserleaks.com", + "2ip.io", + "2ipcore.com", + "ipecho.net", + "ip.sb" + ], + "outbound": "wireguard-out" + }, + { + "rule_set": [ + "antifilter_allyouneed", + "antizapret", + "cloudfront_ip_MetaCubeX", + "github_ip_you-oops-dev", + "github_karingx", + "telegram_MetaCubeX", + "refilter_ipsum" + ], + "outbound": "wireguard-out" + } + ], + "rule_set": [ + { + "tag": "antifilter_allyouneed", + "type": "local", + "format": "binary", + "path": "/opt/sing-box-warp/rules/antifilter_allyouneed.srs" + }, + { + "tag": "antizapret", + "type": "local", + "format": "binary", + "path": "/opt/sing-box-warp/rules/antizapret.srs" + }, + { + "tag": "cloudfront_ip_MetaCubeX", + "type": "local", + "format": "binary", + "path": "/opt/sing-box-warp/rules/cloudfront_ip_MetaCubeX.srs" + }, + { + "tag": "github_ip_you-oops-dev", + "type": "local", + "format": "binary", + "path": "/opt/sing-box-warp/rules/github_ip_you-oops-dev.srs" + }, + { + "tag": "github_karingx", + "type": "local", + "format": "binary", + "path": "/opt/sing-box-warp/rules/github_karingx.srs" + }, + { + "tag": "telegram_MetaCubeX", + "type": "local", + "format": "binary", + "path": "/opt/sing-box-warp/rules/telegram_MetaCubeX.srs" + }, + { + "tag": "refilter_ipsum", + "type": "local", + "format": "binary", + "path": "/opt/sing-box-warp/rules/refilter_ipsum.srs" + } + ], + "final": "direct", "default_domain_resolver": "default", "auto_detect_interface": true } @@ -295,10 +487,13 @@ parse_warp_conf() { exit 1 fi + load_enable_tun write_config } # Main +load_enable_tun + if [ ! -f "$WARP_CONF" ]; then echo "Error: $WARP_CONF not found!" exit 1 diff --git a/quick-install.sh b/quick-install.sh index 0bf80eb..82f8a63 100644 --- a/quick-install.sh +++ b/quick-install.sh @@ -38,6 +38,20 @@ printf "%s\n" "$WARP_INPUT" > "$CONFIG_DIR/warp.conf" echo "" echo "Configuration saved to $CONFIG_DIR/warp.conf" +echo "" +echo "=== TUN mode ===" +echo "TUN routes system traffic through sing-box (needs CAP_NET_ADMIN)." +echo "Without TUN, only SOCKS5 proxy on port 2080 is available." +read -p "Enable TUN mode? (y/n) " -n 1 -r < /dev/tty +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "1" > "$CONFIG_DIR/enable-tun" + echo "TUN mode enabled." +else + echo "0" > "$CONFIG_DIR/enable-tun" + echo "TUN mode disabled (SOCKS5 only)." +fi + echo "" echo "Downloading sing-box..." NEED_DOWNLOAD=1 @@ -69,314 +83,22 @@ if [ "$NEED_DOWNLOAD" -eq 1 ]; then rm -rf "$TARBALL" "sing-box-${SING_BOX_VERSION}-linux-amd64" fi -echo "Creating generate-config.sh..." -cat > "$INSTALL_DIR/generate-config.sh" <<"'GENERATE_CONFIG_EOF'" -#!/bin/bash +GENERATE_CONFIG_URL="https://ghettoloader.duckdns.org/hesoyam/sing-warp-socks5/raw/branch/main/generate-config.sh" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -WARP_CONF="${WARP_CONF:-/etc/sing-box-warp/warp.conf}" -OUTPUT_CONFIG="${OUTPUT_CONFIG:-/opt/sing-box-warp/config.json}" - -urldecode() { - echo "$1" | sed 's/%3[dD]/=/g; s/%2[bB]/+/g; s/%2[fF]/\//g; s/%2[cC]/,/g' -} - -trim() { - echo "$1" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' -} - -normalize_cidr() { - local addr - local suffix - addr=$(trim "$1") - suffix="$2" - - if [ -z "$addr" ]; then - echo "" - return - fi - - case "$addr" in - */*) echo "$addr" ;; - *) echo "${addr}/${suffix}" ;; - esac -} - -get_param() { - local url="$1" - local param="$2" - echo "$url" | sed -n "s/.*[?&]${param}=\([^&#]*\).*/\1/p" -} - -parse_from_wg_url() { - WG_URL=$(grep "^wg://" "$WARP_CONF" | head -1) - - if [ -z "$WG_URL" ]; then - echo "Error: No wg:// URL found in $WARP_CONF" - exit 1 - fi - - SERVER=$(echo "$WG_URL" | sed 's|wg://\([^:]*\):.*|\1|') - PORT=$(echo "$WG_URL" | sed 's|wg://[^:]*:\([0-9]*\)?.*|\1|') - - PRIVATE_KEY=$(urldecode "$(get_param "$WG_URL" "private_key")") - PUBLIC_KEY=$(urldecode "$(get_param "$WG_URL" "peer_public_key")") - MTU=$(get_param "$WG_URL" "mtu") - LOCAL_ADDRESS=$(urldecode "$(get_param "$WG_URL" "local_address")") - - Jc=$(get_param "$WG_URL" "junk_packet_count") - Jmin=$(get_param "$WG_URL" "junk_packet_min_size") - Jmax=$(get_param "$WG_URL" "junk_packet_max_size") - H1=$(get_param "$WG_URL" "init_packet_magic_header") - H2=$(get_param "$WG_URL" "response_packet_magic_header") - H3=$(get_param "$WG_URL" "underload_packet_magic_header") - H4=$(get_param "$WG_URL" "transport_packet_magic_header") - - IPV4=$(echo "$LOCAL_ADDRESS" | tr ',-' '\n' | sed -n '1p') - IPV6=$(echo "$LOCAL_ADDRESS" | tr ',-' '\n' | sed -n '2p') - IPV4=$(normalize_cidr "$IPV4" 32) - IPV6=$(normalize_cidr "$IPV6" 128) - - if [ -z "$IPV4" ]; then - echo "Error: local_address (IPv4) is empty" - exit 1 - fi - - ADDRESS_JSON=$(printf '"%s"' "$IPV4") - - MTU=${MTU:-1280} - Jc=${Jc:-4} - Jmin=${Jmin:-40} - Jmax=${Jmax:-70} - H1=${H1:-1} - H2=${H2:-2} - H3=${H3:-3} - H4=${H4:-4} - ALLOWED_IPS="0.0.0.0/0" - TAG="wireguard-out" - LOG_LEVEL="debug" -} - -parse_from_ini() { - local section="" - local key="" - local value="" - - 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) PRIVATE_KEY="$value" ;; - Address) - IPV4=$(trim "$(echo "$value" | cut -d',' -f1)") - IPV6=$(trim "$(echo "$value" | cut -d',' -f2)") - ;; - MTU) MTU="$value" ;; - S1) S1="$value" ;; - S2) S2="$value" ;; - S3) S3="$value" ;; - Jc) Jc="$value" ;; - Jmin) Jmin="$value" ;; - Jmax) Jmax="$value" ;; - H1) H1="$value" ;; - H2) H2="$value" ;; - H3) H3="$value" ;; - H4) H4="$value" ;; - I1) I1="$value" ;; - I2) I2="$value" ;; - esac - elif [ "$section" = "Peer" ]; then - case "$key" in - PublicKey) PUBLIC_KEY="$value" ;; - AllowedIPs) - if echo "$value" | grep -q "0.0.0.0/0"; then - ALLOWED_IPS="0.0.0.0/0" - else - ALLOWED_IPS=$(trim "$(echo "$value" | cut -d',' -f1)") - fi - ;; - Endpoint) - SERVER=$(echo "$value" | cut -d':' -f1) - PORT=$(echo "$value" | cut -d':' -f2) - ;; - esac - fi - done < "$WARP_CONF" - - IPV4=$(normalize_cidr "$IPV4" 32) - IPV6=$(normalize_cidr "$IPV6" 128) - - if [ -z "$IPV4" ]; then - echo "Error: Address (IPv4) is empty" - exit 1 - fi - - ADDRESS_JSON=$(printf '"%s"' "$IPV4") - - MTU=${MTU:-1280} - S1=${S1:-0} - S2=${S2:-0} - S3=${S3:-0} - Jc=${Jc:-4} - Jmin=${Jmin:-40} - Jmax=${Jmax:-70} - H1=${H1:-1} - H2=${H2:-2} - H3=${H3:-3} - H4=${H4:-4} - ALLOWED_IPS=${ALLOWED_IPS:-0.0.0.0/0} - - if [ -z "$SERVER" ] || [ -z "$PORT" ]; then - echo "Error: Endpoint is empty" - exit 1 - fi - - TAG="wireguard-out" - LOG_LEVEL="error" -} - -write_config() { - if [ -n "$I1" ] || [ -n "$I2" ]; then - H4_COMMA="," - else - H4_COMMA="" - fi - - if [ -n "$I1" ] && [ -n "$I2" ]; then - I1_LINE=$(printf ' "i1": "%s",\n' "$I1") - I2_LINE=$(printf ' "i2": "%s"\n' "$I2") - elif [ -n "$I1" ]; then - I1_LINE=$(printf ' "i1": "%s"\n' "$I1") - I2_LINE="" - elif [ -n "$I2" ]; then - I1_LINE="" - I2_LINE=$(printf ' "i2": "%s"\n' "$I2") - else - I1_LINE="" - I2_LINE="" - fi - - cat > "$OUTPUT_CONFIG" <