Rework into using nftables, along with proper Xray DNS configuration

This commit is contained in:
2025-04-13 13:41:16 +05:00
parent a5c8961d78
commit f9645b0ef6
17 changed files with 249 additions and 195 deletions

View File

@@ -1,9 +0,0 @@
{
"log":
{
"access": "/etc/xray/log/access.log",
"dnsLog": false,
"error": "/etc/xray/log/error.log",
"loglevel": "none"
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}
}

19
etc/xray/config/dns.jsonc Normal file
View File

@@ -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"
}
}

View File

@@ -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"
}
},

View File

@@ -0,0 +1,8 @@
{
"log": {
// "access": "/etc/xray/log/access.log",
// "error": "/etc/xray/log/error.log",
"loglevel": "none",
"dnsLog": true
}
}

View File

@@ -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
}
}
}
]
}

View File

@@ -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
}
}
}
}

View File

@@ -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"
}
]
}
}
}

9
etc/xray/custom_rules.sh Normal file
View File

@@ -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

View File

@@ -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
}

33
etc/xray/nft.conf Normal file
View File

@@ -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
}
}

6
etc/xray/revert.sh Normal file
View File

@@ -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

View File

@@ -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