diff --git a/README.md b/README.md index 47e1fa6..5161c38 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,21 @@ openwrt-xray ------------ +Requirements +------------ +- OpenWRT 22.03 and higher +- Router should be in 192.168.0.0/16 subnet (default rules operate on that) + Install ------- 1. Drop the files onto OpenWRT (22.03 and higher) router 2. Run `install_xray.sh`: `chmod +x /root/install_xray.sh && /root/install_xray.sh` 3. Configure this installation: -- Edit this rule in `/etc/xray/startup.sh`: `iptables -t mangle -A XRAY -d 1.1.1.1 -j RETURN` to match your public static IP address -- In `/root/xray_config/04_outbounds.json` add your connection details -- You can optionally add excluding/blocking rules to `startup.sh`, see possible additions in `fwd_functions.sh` beside it. +- In `/etc/xray/config/outbounds.jsonc` add your connection details (but be carefult with specified streamSettings->sockOpt, these are required) +- You can optionally add excluding/blocking rules to `/etc/xray/custom_rules.sh`, see possible additions in `fwd_functions.sh` beside it. 4. Enable the `xray` service in LuCI (System -> Startup, it should be at the end of the list) and reboot your router. -(In case it fails to work, you may disable the service and reboot the router again to revert the effects) +(In case it fails to work, you may disable the service and reboot the router again to revert the effects, or use `/etc/xray/revert.sh`) crontab ------- diff --git a/etc/init.d/xray b/etc/init.d/xray index 2db57cb..3b66f27 100644 --- a/etc/init.d/xray +++ b/etc/init.d/xray @@ -25,7 +25,7 @@ start_service() { config_get dialer "config" "dialer" config_get format "config" "format" "json" - # runs iptables setup + # runs nftables setup /etc/xray/startup.sh procd_open_instance "$CONF" diff --git a/etc/xray/config/01_log.json b/etc/xray/config/01_log.json deleted file mode 100644 index fcc1b18..0000000 --- a/etc/xray/config/01_log.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "log": - { - "access": "/etc/xray/log/access.log", - "dnsLog": false, - "error": "/etc/xray/log/error.log", - "loglevel": "none" - } -} \ No newline at end of file diff --git a/etc/xray/config/02_transport.json b/etc/xray/config/02_transport.json deleted file mode 100644 index 8ace9a8..0000000 --- a/etc/xray/config/02_transport.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "transport": - { - "domainStrategy": "IPIfNonMatch", - "grpcSettings": - { - "health_check_timeout": 20, - "idle_timeout": 60, - "initial_windows_size": 35536, - "permit_without_stream": true - }, - "httpSettings": - { - "health_check_timeout": 15, - "read_idle_timeout": 10 - }, - "sockopt": - { - "tcpFastOpen": true, - "tcpMptcp": true, - "tcpNoDelay": true - } - } -} \ No newline at end of file diff --git a/etc/xray/config/06_policy.json b/etc/xray/config/06_policy.json deleted file mode 100644 index 7cebfcc..0000000 --- a/etc/xray/config/06_policy.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "policy": - { - "levels": - { - "0": - { - // If you have issues with SSH connections, it's recommended to increase this value. See the docs - "connIdle": 30 - } - } - } -} \ No newline at end of file diff --git a/etc/xray/config/dns.jsonc b/etc/xray/config/dns.jsonc new file mode 100644 index 0000000..b9ea750 --- /dev/null +++ b/etc/xray/config/dns.jsonc @@ -0,0 +1,19 @@ +{ + "dns": { + "tag": "dns-in", + "hosts": { + "dns.google": "8.8.8.8" + }, + "servers": [ + "https://dns.google/dns-query", + { + "address": "localhost", + "disableFallback": true, + "domains": [ + "regexp:.*\\.lan" + ] + } + ], + "queryStrategy": "UseIPv4" + } +} \ No newline at end of file diff --git a/etc/xray/config/03_inbounds.json b/etc/xray/config/inbounds.jsonc similarity index 66% rename from etc/xray/config/03_inbounds.json rename to etc/xray/config/inbounds.jsonc index 9e1ec7e..fb07390 100644 --- a/etc/xray/config/03_inbounds.json +++ b/etc/xray/config/inbounds.jsonc @@ -1,18 +1,14 @@ { - "inbounds": - [ + "inbounds": [ { "port": 61219, "protocol": "dokodemo-door", - "settings": - { + "settings": { "followRedirect": true, "network": "tcp,udp" }, - "sniffing": - { - "destOverride": - [ + "sniffing": { + "destOverride": [ "http", "tls", "quic" @@ -20,10 +16,8 @@ "enabled": true, "routeOnly": true }, - "streamSettings": - { - "sockopt": - { + "streamSettings": { + "sockopt": { "tproxy": "tproxy" } }, diff --git a/etc/xray/config/log.jsonc b/etc/xray/config/log.jsonc new file mode 100644 index 0000000..6a4185a --- /dev/null +++ b/etc/xray/config/log.jsonc @@ -0,0 +1,8 @@ +{ + "log": { + // "access": "/etc/xray/log/access.log", + // "error": "/etc/xray/log/error.log", + "loglevel": "none", + "dnsLog": true + } +} \ No newline at end of file diff --git a/etc/xray/config/04_outbounds.json b/etc/xray/config/outbounds.jsonc similarity index 52% rename from etc/xray/config/04_outbounds.json rename to etc/xray/config/outbounds.jsonc index 783cefd..8c7d10d 100644 --- a/etc/xray/config/04_outbounds.json +++ b/etc/xray/config/outbounds.jsonc @@ -1,17 +1,14 @@ { - "outbounds": - [ + "outbounds": [ { + "tag": "vless-reality", "protocol": "vless", - "settings": - { - "vnext": - [ + "settings": { + "vnext": [ { "address": "1.1.1.1", "port": 443, - "users": - [ + "users": [ { "encryption": "none", "flow": "xtls-rprx-vision", @@ -22,39 +19,52 @@ } ] }, - "streamSettings": - { + "streamSettings": { "network": "tcp", - "realitySettings": - { + "realitySettings": { "fingerprint": "chrome", "publicKey": "", "serverName": "", "shortId": "", "spiderX": "/" }, - "security": "reality" - }, - "tag": "vless-reality" + "security": "reality", + // Important: This is required for rules to work correctly! + "sockopt": { + "domainStrategy": "UseIP", + "mark": 2 + } + } }, { + "tag": "direct", "protocol": "freedom", - "tag": "direct" - }, - { - "protocol": "blackhole", - "settings": - { - "response": - { - "type": "http" + "streamSettings": { + "sockopt": { + "mark": 2 } }, - "tag": "block" + "settings": { + "domainStrategy": "UseIP" + } }, { + "tag": "block", + "protocol": "blackhole", + "settings": { + "response": { + "type": "http" + } + } + }, + { + "tag": "dns-out", "protocol": "dns", - "tag": "dns" + "streamSettings": { + "sockopt": { + "mark": 2 + } + } } ] } \ No newline at end of file diff --git a/etc/xray/config/policy.jsonc b/etc/xray/config/policy.jsonc new file mode 100644 index 0000000..fde2774 --- /dev/null +++ b/etc/xray/config/policy.jsonc @@ -0,0 +1,10 @@ +{ + "policy": { + "levels": { + "0": { + // If you have issues with SSH connections, it's recommended to increase this value. See the docs + "connIdle": 30 + } + } + } +} \ No newline at end of file diff --git a/etc/xray/config/05_routing.json b/etc/xray/config/routing.jsonc similarity index 50% rename from etc/xray/config/05_routing.json rename to etc/xray/config/routing.jsonc index 90e6a06..7f8894c 100644 --- a/etc/xray/config/05_routing.json +++ b/etc/xray/config/routing.jsonc @@ -1,25 +1,31 @@ { "routing": { + "domainStrategy": "IPIfNonMatch", "rules": [ // Capture DNS { - "inboundTag": ["redirect", "tproxy"], - "outboundTag": "dns", - "type": "field", + "inboundTag": "tproxy", + "outboundTag": "dns-out", "port": 53 }, // Block QUIC { - "inboundTag": ["redirect", "tproxy"], + "inboundTag": "tproxy", "outboundTag": "block", - "type": "field", - "protocol": ["quic"] + "protocol": [ + "quic" + ] + }, + // Force DNS to go through direct + // If needed, you can force DNS to go through other outbound using tags for specific servers in dns.jsonc + { + "inboundTag": "dns-in", + "outboundTag": "direct" }, // Force specific source IPs to go direct { - "inboundTag": ["redirect", "tproxy"], + "inboundTag": "tproxy", "outboundTag": "direct", - "type": "field", "source": [ "192.168.2.255", "192.168.2.254" @@ -27,9 +33,8 @@ }, // Block common ads and other stuff { - "inboundTag": ["redirect", "tproxy"], + "inboundTag": "tproxy", "outboundTag": "block", - "type": "field", "domain": [ "geosite:category-ads-all", "google-analytics", @@ -42,39 +47,36 @@ }, // Force BitTorrent to go through direct { - "inboundTag": ["redirect", "tproxy"], + "inboundTag": "tproxy", "outboundTag": "direct", - "type": "field", - "protocol": ["bittorrent"] - }, - // Explicitly force direct + "protocol": "bittorrent" + }, + // Explicitly force direct (domains) { - "inboundTag": ["redirect", "tproxy"], + "inboundTag": "tproxy", "outboundTag": "direct", - "type": "field", "domain": [ - "regexp:^([\\w\\-\\.]+\\.)ru$", // .ru - "regexp:^([\\w\\-\\.]+\\.)su$", // .su - "regexp:^([\\w\\-\\.]+\\.)xn--p1ai$", // .рф - "regexp:^([\\w\\-\\.]+\\.)xn--p1acf$", // .рус - "regexp:^([\\w\\-\\.]+\\.)xn--80asehdb$", // .онлайн + "regexp:^([\\w\\-\\.]+\\.)ru$", // .ru + // "regexp:^([\\w\\-\\.]+\\.)su$", // .su + "regexp:^([\\w\\-\\.]+\\.)xn--p1ai$", // .рф + "regexp:^([\\w\\-\\.]+\\.)xn--p1acf$", // .рус + "regexp:^([\\w\\-\\.]+\\.)xn--80asehdb$", // .онлайн "regexp:^([\\w\\-\\.]+\\.)xn--c1avg$", // .орг - "regexp:^([\\w\\-\\.]+\\.)xn--80aswg$", // .сайт - "regexp:^([\\w\\-\\.]+\\.)xn--80adxhks$", // .москва - "regexp:^([\\w\\-\\.]+\\.)moscow$", // .moscow - "regexp:^([\\w\\-\\.]+\\.)xn--d1acj3b$", // .дети - "regexp:^([\\w\\-\\.]+\\.)yandex$", // .yandex + "regexp:^([\\w\\-\\.]+\\.)xn--80aswg$", // .сайт + "regexp:^([\\w\\-\\.]+\\.)xn--80adxhks$", // .москва + "regexp:^([\\w\\-\\.]+\\.)moscow$", // .moscow + "regexp:^([\\w\\-\\.]+\\.)xn--d1acj3b$", // .дети + "regexp:^([\\w\\-\\.]+\\.)yandex$", // .yandex "geosite:category-ru", "geosite:category-gov-ru", "geosite:yandex", "geosite:steam", "geosite:vk", "geosite:category-gov-ru", - "regexp:^assets(\\d*?)\\.xboxlive\\.com$", + // "regexp:^assets(\\d*?)\\.xboxlive\\.com$", "domain:rt.ru", "domain:ngenix.net", "domain:plex.tv", - "geoip:ru", "domain:kaspersky.com", "domain:koronapay.com", "domain:binance.com", @@ -87,13 +89,23 @@ "domain:veesp.com" ] }, - + // Explicitly force direct (IPs) + { + "inboundTag": "tproxy", + "outboundTag": "direct", + "ip": [ + "geoip:ru", + "geoip:am" + ] + }, // No rules found? Go vless-reality { - "inboundTag": ["redirect", "tproxy"], - "outboundTag": "vless-reality", - "type": "field" + "inboundTag": [ + "tproxy", + "dns-in" + ], + "outboundTag": "vless-reality" } ] } -} +} \ No newline at end of file diff --git a/etc/xray/custom_rules.sh b/etc/xray/custom_rules.sh new file mode 100644 index 0000000..388be9b --- /dev/null +++ b/etc/xray/custom_rules.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Source the function definitions +. /etc/xray/fwd_functions.sh + +# Add your custom rules here +# See the fwd_functions.sh for the available functions +# Example: Exclude traefik HTTP+HTTPS +# direct_port_range_for_ip "192.168.1.165" 80 443 diff --git a/etc/xray/fwd_functions.sh b/etc/xray/fwd_functions.sh index 2f768e1..455baa2 100644 --- a/etc/xray/fwd_functions.sh +++ b/etc/xray/fwd_functions.sh @@ -1,63 +1,69 @@ #!/bin/sh -# Function to add iptables rules for a specific IP and port +# Function to add nftables rules for a specific IP and port direct_port_for_ip() { ip=$1 port=$2 - iptables -t mangle -A XRAY -d "$ip"/32 -p tcp --dport "$port" -j RETURN - iptables -t mangle -A XRAY -d "$ip"/32 -p udp --dport "$port" -j RETURN - iptables -t mangle -A XRAY -s "$ip"/32 -p tcp --sport "$port" -j RETURN - iptables -t mangle -A XRAY -s "$ip"/32 -p udp --sport "$port" -j RETURN + nft insert rule ip xray prerouting ip daddr "$ip" tcp dport "$port" counter return + nft insert rule ip xray prerouting ip daddr "$ip" udp dport "$port" counter return + nft insert rule ip xray output ip daddr "$ip" tcp dport "$port" counter return + nft insert rule ip xray output ip daddr "$ip" udp dport "$port" counter return } -# Function to add iptables rules for a single port without specifying IP +# Function to add nftables rules for a single port without specifying IP direct_port() { port=$1 - iptables -t mangle -A XRAY -p tcp --dport "$port" -j RETURN - iptables -t mangle -A XRAY -p udp --dport "$port" -j RETURN - iptables -t mangle -A XRAY -p tcp --sport "$port" -j RETURN - iptables -t mangle -A XRAY -p udp --sport "$port" -j RETURN + nft insert rule ip xray prerouting tcp dport "$port" counter return + nft insert rule ip xray prerouting udp dport "$port" counter return + nft insert rule ip xray output tcp dport "$port" counter return + nft insert rule ip xray output udp dport "$port" counter return } -# Function to add iptables rules for a range of ports for a specific IP +# Function to add nftables rules for a range of ports for a specific IP direct_port_range_for_ip() { ip=$1 start_port=$2 end_port=$3 - port=$start_port - while [ "$port" -le "$end_port" ]; do - direct_port_for_ip "$ip" "$port" - port=$((port + 1)) - done + nft insert rule ip xray prerouting ip daddr "$ip" tcp dport { "$start_port"-"$end_port" } counter return + nft insert rule ip xray prerouting ip daddr "$ip" udp dport { "$start_port"-"$end_port" } counter return + nft insert rule ip xray output ip daddr "$ip" tcp dport { "$start_port"-"$end_port" } counter return + nft insert rule ip xray output ip daddr "$ip" udp dport { "$start_port"-"$end_port" } counter return } -# Function to add iptables rules for a range of ports without specifying IP +# Function to add nftables rules for a range of ports without specifying IP direct_port_range() { start_port=$1 end_port=$2 - port=$start_port - while [ "$port" -le "$end_port" ]; do - direct_port "$port" - port=$((port + 1)) - done + nft insert rule ip xray prerouting tcp dport { "$start_port"-"$end_port" } counter return + nft insert rule ip xray prerouting udp dport { "$start_port"-"$end_port" } counter return + nft insert rule ip xray output tcp dport { "$start_port"-"$end_port" } counter return + nft insert rule ip xray output udp dport { "$start_port"-"$end_port" } counter return } -# Function to add iptables rules for an IP without specifying ports +# Function to add nftables rules for an IP without specifying ports direct_ip() { ip=$1 - iptables -t mangle -A XRAY -d "$ip"/32 -j RETURN - iptables -t mangle -A XRAY -s "$ip"/32 -j RETURN + nft insert rule ip xray prerouting ip saddr "$ip" counter return + nft insert rule ip xray output ip saddr "$ip" counter return + nft insert rule ip xray prerouting ip daddr "$ip" counter return + nft insert rule ip xray output ip daddr "$ip" counter return } -# Function to add iptables rules for blocking IP +# Function to add nftables rules for blocking IP block_ip() { ip=$1 - iptables -I FORWARD 1 -d "$ip"/32 -j DROP - iptables -I FORWARD 1 -s "$ip"/32 -j DROP + # Block in prerouting chain + nft insert rule ip xray prerouting ip daddr "$ip" counter drop + nft insert rule ip xray prerouting ip saddr "$ip" counter drop + + # Block in output chain + nft insert rule ip xray output ip daddr "$ip" counter drop + nft insert rule ip xray output ip saddr "$ip" counter drop } + diff --git a/etc/xray/nft.conf b/etc/xray/nft.conf new file mode 100644 index 0000000..35ccb87 --- /dev/null +++ b/etc/xray/nft.conf @@ -0,0 +1,33 @@ +#!/usr/sbin/nft -f + +define RESERVED_IP = { + 10.0.0.0/8, + 100.64.0.0/10, + 127.0.0.0/8, + 169.254.0.0/16, + 172.16.0.0/12, + 192.0.0.0/24, + 224.0.0.0/4, + 240.0.0.0/4, + 255.255.255.255/32 +} + +table ip xray { + chain prerouting { + type filter hook prerouting priority mangle; policy accept; + ip daddr $RESERVED_IP return + ip daddr 192.168.0.0/16 tcp dport != 53 return + ip daddr 192.168.0.0/16 udp dport != 53 return + ip protocol tcp tproxy to 127.0.0.1:61219 meta mark set 1 + ip protocol udp tproxy to 127.0.0.1:61219 meta mark set 1 + } + chain output { + type route hook output priority mangle; policy accept; + ip daddr $RESERVED_IP return + ip daddr 192.168.0.0/16 tcp dport != 53 return + ip daddr 192.168.0.0/16 udp dport != 53 return + meta mark 2 return + ip protocol tcp meta mark set 1 + ip protocol udp meta mark set 1 + } +} \ No newline at end of file diff --git a/etc/xray/revert.sh b/etc/xray/revert.sh new file mode 100644 index 0000000..aa2c4e0 --- /dev/null +++ b/etc/xray/revert.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +nft delete table ip xray +ip route del local default dev lo table 100 +ip rule del table 100 +rm -f /tmp/xray_startup_executed \ No newline at end of file diff --git a/etc/xray/startup.sh b/etc/xray/startup.sh index e04ad57..6881b5f 100644 --- a/etc/xray/startup.sh +++ b/etc/xray/startup.sh @@ -1,54 +1,43 @@ #!/bin/sh -# Ensure this script runs only once per boot -if [ -f /tmp/xray_startup_executed ]; then - # The file exists, so do not run the script - echo "This script was executed already. To revert the results, reboot the device" - exit 0 -fi - -# Source the function definitions . /etc/xray/fwd_functions.sh -# create chain +# Get WAN device name first +WAN_DEVICE=$(uci get network.wan.device) + +if [ -z "$WAN_DEVICE" ]; then + echo "Error: Could not determine WAN device" + exit 1 +fi + +# Get WAN interface IP address using the device name, excluding localhost and private IPs +# Comment this out, if it doesn't work for you +WAN_IP=$(ip addr show $WAN_DEVICE | grep 'inet ' | awk '{print $2}' | cut -d/ -f1 | grep -v '^127\.' | grep -v '^192\.168\.') +# WAN_IP="1.1.1.1" + +if [ -z "$WAN_IP" ]; then + echo "Error: Could not determine WAN IP address for device $WAN_DEVICE" + exit 1 +fi + +if [ -f /tmp/xray_startup_executed ]; then + sh /etc/xray/revert.sh +fi + +# Create routing table and rules +ip route add local default dev lo table 100 ip rule add fwmark 1 table 100 -ip route add local 0.0.0.0/0 dev lo table 100 -iptables -t mangle -N XRAY -# exclude private ipv4 -iptables -t mangle -A XRAY -d 255.255.255.255/32 -j RETURN -iptables -t mangle -A XRAY -d 0.0.0.0/8 -j RETURN -iptables -t mangle -A XRAY -d 10.0.0.0/8 -j RETURN -iptables -t mangle -A XRAY -d 100.64.0.0/10 -j RETURN -iptables -t mangle -A XRAY -d 127.0.0.0/8 -j RETURN -iptables -t mangle -A XRAY -d 169.254.0.0/16 -j RETURN -iptables -t mangle -A XRAY -d 172.16.0.0/12 -j RETURN -iptables -t mangle -A XRAY -d 192.0.0.0/24 -j RETURN -iptables -t mangle -A XRAY -d 192.0.2.0/24 -j RETURN -iptables -t mangle -A XRAY -d 192.168.0.0/16 -j RETURN -iptables -t mangle -A XRAY -d 198.18.0.0/15 -j RETURN -iptables -t mangle -A XRAY -d 198.51.100.0/24 -j RETURN -iptables -t mangle -A XRAY -d 203.0.113.0/24 -j RETURN -iptables -t mangle -A XRAY -d 224.0.0.0/4 -j RETURN -iptables -t mangle -A XRAY -d 240.0.0.0/4 -j RETURN +# Load nftables rules from nft.conf +nft -f /etc/xray/nft.conf +# Execute custom rules if they exist +if [ -f /etc/xray/custom_rules.sh ]; then + sh /etc/xray/custom_rules.sh +fi -# !!! PROVIDE YOUR OWN IP HERE !!! -iptables -t mangle -A XRAY -d 1.1.1.1 -j RETURN - - - -# exclude from Xray the following: -# SAMPLE - you can test the rules using /root/fwd_manual.sh script -# traefik HTTP+HTTPS -#direct_port_range_for_ip "10.241.1.165" 80 443 - - - -# add forwarding rule -iptables -t mangle -A XRAY -p tcp -j TPROXY --on-port 61219 --tproxy-mark 1 -iptables -t mangle -A XRAY -p udp -j TPROXY --on-port 61219 --tproxy-mark 1 -iptables -t mangle -A PREROUTING -j XRAY +# Add rules to bypass the firewall for the WAN IP +direct_ip "$WAN_IP" # required for check above touch /tmp/xray_startup_executed \ No newline at end of file diff --git a/root/fwd_manual.sh b/root/fwd_manual.sh index f81d477..e81053f 100644 --- a/root/fwd_manual.sh +++ b/root/fwd_manual.sh @@ -3,4 +3,4 @@ # Source the function definitions . /etc/xray/fwd_functions.sh -direct_ip "10.241.1.3" \ No newline at end of file +direct_ip "192.168.1.3" \ No newline at end of file