Migrate to pure nftables routing (remove iptables/ipset)
- Replace hybrid iptables/ipset/nftables approach with pure nftables - Add nftables native set for Russian IP ranges (populated from RIPE) - Create update-direct-routes.sh script to load IP ranges from RIPE database - Remove ipset and iptables dependencies from postup.sh/postdown.sh - Add automatic weekly cron job for IP range updates - Update all documentation to reflect the new approach Benefits: - More reliable: no iptables/nftables conflicts - Simpler debugging: single tool for all rules (nft list ruleset) - Atomic rule loading: prevents partial failures - IP-based routing is more predictable than DNS-based Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -231,8 +231,8 @@ wg show wg0
|
|||||||
# Check routing table
|
# Check routing table
|
||||||
ip route show table proxy
|
ip route show table proxy
|
||||||
|
|
||||||
# Check ipset
|
# Check nftables set (will be empty until IP ranges are loaded)
|
||||||
ipset list direct
|
nft list set ip vpn-routing direct
|
||||||
|
|
||||||
# Check policy routing rules
|
# Check policy routing rules
|
||||||
ip rule show
|
ip rule show
|
||||||
@@ -240,9 +240,19 @@ ip rule show
|
|||||||
|
|
||||||
Expected output:
|
Expected output:
|
||||||
- Routing table `proxy` should have default route via `10.20.0.2`
|
- Routing table `proxy` should have default route via `10.20.0.2`
|
||||||
- ipset `direct` should exist (empty initially)
|
- nftables set `direct` should exist
|
||||||
- Policy routing rule for `10.10.0.0/24` with fwmark `0x1`
|
- Policy routing rule for `10.10.0.0/24` with fwmark `0x1`
|
||||||
|
|
||||||
|
### 5.4 Load Russian IP ranges
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Load Russian IP ranges into nftables (takes 1-2 minutes)
|
||||||
|
/etc/wireguard/update-direct-routes.sh
|
||||||
|
|
||||||
|
# Verify ranges were loaded
|
||||||
|
nft list set ip vpn-routing direct | head -20
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 6: Add First Client
|
## Step 6: Add First Client
|
||||||
@@ -331,23 +341,22 @@ curl ifconfig.me
|
|||||||
On RU VDS:
|
On RU VDS:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# After client visits .ru domains, check direct ipset
|
# Check that Russian IP ranges are loaded
|
||||||
ipset list direct
|
nft list set ip vpn-routing direct | wc -l
|
||||||
|
|
||||||
# Should show Russian IPs
|
# Should show many IP ranges (thousands)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.4 Advanced testing
|
### 7.4 Advanced testing
|
||||||
|
|
||||||
Test that `.ru` domains go direct:
|
Test that Russian IPs go direct:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# From client - visit some Russian sites
|
# From client - visit some Russian sites
|
||||||
curl -I https://yandex.ru
|
curl -I https://yandex.ru
|
||||||
curl -I https://mail.ru
|
curl -I https://mail.ru
|
||||||
|
|
||||||
# Then on RU VDS - check ipset
|
# These should be fast (direct routing)
|
||||||
ipset list direct
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Test that other domains go through DE:
|
Test that other domains go through DE:
|
||||||
@@ -447,14 +456,14 @@ ip route show table proxy
|
|||||||
ip rule show
|
ip rule show
|
||||||
```
|
```
|
||||||
|
|
||||||
**Check ipset:**
|
**Check nftables set:**
|
||||||
```bash
|
```bash
|
||||||
ipset list direct
|
nft list set ip vpn-routing direct | head -20
|
||||||
```
|
```
|
||||||
|
|
||||||
**Check iptables mangle:**
|
**Check nftables rules:**
|
||||||
```bash
|
```bash
|
||||||
iptables -t mangle -L -v
|
nft list chain ip vpn-routing prerouting
|
||||||
```
|
```
|
||||||
|
|
||||||
### Client can't connect
|
### Client can't connect
|
||||||
@@ -612,5 +621,8 @@ ping 10.20.0.2
|
|||||||
|
|
||||||
# Check routing
|
# Check routing
|
||||||
ip route show table proxy
|
ip route show table proxy
|
||||||
ipset list direct
|
nft list set ip vpn-routing direct | head -20
|
||||||
|
|
||||||
|
# Update Russian IP ranges
|
||||||
|
/etc/wireguard/update-direct-routes.sh
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -124,12 +124,12 @@ ip addr show wg0
|
|||||||
|
|
||||||
## Phase 2: RU VDS Setup (Gateway)
|
## Phase 2: RU VDS Setup (Gateway)
|
||||||
|
|
||||||
The main node - handles user connections, DNS-based routing decisions.
|
The main node - handles user connections, IP-based routing decisions.
|
||||||
|
|
||||||
### Step 2.1: Install packages
|
### Step 2.1: Install packages
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
apt update && apt install -y wireguard dnsmasq nftables ipset
|
apt update && apt install -y wireguard dnsmasq nftables qrencode curl bc
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2.2: Enable IP forwarding
|
### Step 2.2: Enable IP forwarding
|
||||||
@@ -198,16 +198,18 @@ Create `/etc/wireguard/postup.sh`:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
# Create ipsets for routing decisions
|
# Load nftables rules (includes the 'direct' set and packet marking)
|
||||||
ipset create direct hash:net -exist
|
nft -f /etc/nftables.conf
|
||||||
ipset create proxy hash:net -exist
|
|
||||||
|
|
||||||
# Add default route via DE tunnel for 'proxy' table
|
# Add default route via DE tunnel for 'proxy' table
|
||||||
ip route add default via 10.20.0.2 table proxy
|
ip route add default via 10.20.0.2 dev wg1 table proxy 2>/dev/null || true
|
||||||
|
|
||||||
# Load nftables rules
|
# Policy routing: packets with fwmark 0x1 use 'proxy' table
|
||||||
nft -f /etc/nftables.conf
|
ip rule add from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "PostUp script completed successfully"
|
||||||
```
|
```
|
||||||
|
|
||||||
Make executable:
|
Make executable:
|
||||||
@@ -222,15 +224,16 @@ Create `/etc/wireguard/postdown.sh`:
|
|||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Remove policy routing rule
|
||||||
|
ip rule del from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
||||||
|
|
||||||
# Flush routing table
|
# Flush routing table
|
||||||
ip route flush table proxy
|
ip route flush table proxy 2>/dev/null || true
|
||||||
|
|
||||||
# Destroy ipsets
|
# Flush nftables vpn-routing table
|
||||||
ipset destroy direct 2>/dev/null
|
nft flush table ip vpn-routing 2>/dev/null || true
|
||||||
ipset destroy proxy 2>/dev/null
|
|
||||||
|
|
||||||
# Flush nftables
|
echo "PostDown script completed"
|
||||||
nft flush ruleset
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Make executable:
|
Make executable:
|
||||||
@@ -244,6 +247,10 @@ Create `/etc/nftables.conf`:
|
|||||||
|
|
||||||
```nft
|
```nft
|
||||||
#!/usr/sbin/nft -f
|
#!/usr/sbin/nft -f
|
||||||
|
#
|
||||||
|
# RU VDS nftables configuration
|
||||||
|
# Pure nftables - no iptables/ipset dependencies
|
||||||
|
#
|
||||||
|
|
||||||
flush ruleset
|
flush ruleset
|
||||||
|
|
||||||
@@ -251,36 +258,20 @@ table inet filter {
|
|||||||
chain input {
|
chain input {
|
||||||
type filter hook input priority 0; policy drop;
|
type filter hook input priority 0; policy drop;
|
||||||
|
|
||||||
# Allow established
|
|
||||||
ct state established,related accept
|
ct state established,related accept
|
||||||
|
|
||||||
# Allow loopback
|
|
||||||
iif lo accept
|
iif lo accept
|
||||||
|
|
||||||
# Allow SSH
|
|
||||||
tcp dport 22 accept
|
tcp dport 22 accept
|
||||||
|
|
||||||
# Allow WireGuard from anywhere (user connections)
|
|
||||||
udp dport 51820 accept
|
udp dport 51820 accept
|
||||||
|
|
||||||
# Allow DNS from VPN clients only
|
|
||||||
iifname "wg0" udp dport 53 accept
|
iifname "wg0" udp dport 53 accept
|
||||||
iifname "wg0" tcp dport 53 accept
|
iifname "wg0" tcp dport 53 accept
|
||||||
|
|
||||||
# Allow ICMP
|
|
||||||
icmp type echo-request accept
|
icmp type echo-request accept
|
||||||
}
|
}
|
||||||
|
|
||||||
chain forward {
|
chain forward {
|
||||||
type filter hook forward priority 0; policy drop;
|
type filter hook forward priority 0; policy drop;
|
||||||
|
|
||||||
# Allow forwarding from user VPN
|
|
||||||
iifname "wg0" accept
|
iifname "wg0" accept
|
||||||
|
|
||||||
# Allow forwarding from DE tunnel
|
|
||||||
iifname "wg1" accept
|
iifname "wg1" accept
|
||||||
|
|
||||||
# Allow established
|
|
||||||
ct state established,related accept
|
ct state established,related accept
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,27 +280,39 @@ table inet filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table ip vpn-routing {
|
||||||
|
# Set for Russian IPs (direct routing, no proxy)
|
||||||
|
# Populated by update-direct-routes.sh script
|
||||||
|
set direct {
|
||||||
|
type ipv4_addr
|
||||||
|
flags interval, timeout
|
||||||
|
timeout 6h
|
||||||
|
}
|
||||||
|
|
||||||
|
# Packet marking for policy routing
|
||||||
|
chain prerouting {
|
||||||
|
type filter hook prerouting priority mangle; policy accept;
|
||||||
|
|
||||||
|
# Only process traffic from VPN clients
|
||||||
|
ip saddr != 10.10.0.0/24 return
|
||||||
|
|
||||||
|
# Destinations in 'direct' set: no mark (direct routing)
|
||||||
|
ip daddr @direct return
|
||||||
|
|
||||||
|
# Everything else: mark for proxy routing
|
||||||
|
meta mark set 0x1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table inet nat {
|
table inet nat {
|
||||||
chain postrouting {
|
chain postrouting {
|
||||||
type nat hook postrouting priority 100;
|
type nat hook postrouting priority 100;
|
||||||
|
|
||||||
# NAT direct traffic (going out main interface)
|
|
||||||
oifname != "wg0" oifname != "wg1" ip saddr 10.10.0.0/24 masquerade
|
oifname != "wg0" oifname != "wg1" ip saddr 10.10.0.0/24 masquerade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table inet mangle {
|
|
||||||
chain prerouting {
|
|
||||||
type filter hook prerouting priority -150;
|
|
||||||
|
|
||||||
# Mark packets destined for 'proxy' ipset
|
|
||||||
ip daddr @proxy meta mark set 0x1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** nftables native sets will be used instead of ipset. See Step 2.10 for updated approach.
|
|
||||||
|
|
||||||
### Step 2.9: Configure dnsmasq
|
### Step 2.9: Configure dnsmasq
|
||||||
|
|
||||||
Disable systemd-resolved if running:
|
Disable systemd-resolved if running:
|
||||||
@@ -322,6 +325,9 @@ echo "nameserver 8.8.8.8" > /etc/resolv.conf
|
|||||||
Create `/etc/dnsmasq.d/vpn-routing.conf`:
|
Create `/etc/dnsmasq.d/vpn-routing.conf`:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
|
# dnsmasq configuration for VPN
|
||||||
|
# Routing is handled by nftables based on IP ranges, not DNS
|
||||||
|
|
||||||
# Listen only on VPN interface
|
# Listen only on VPN interface
|
||||||
interface=wg0
|
interface=wg0
|
||||||
bind-interfaces
|
bind-interfaces
|
||||||
@@ -329,122 +335,89 @@ bind-interfaces
|
|||||||
# Upstream DNS
|
# Upstream DNS
|
||||||
server=8.8.8.8
|
server=8.8.8.8
|
||||||
server=8.8.4.4
|
server=8.8.4.4
|
||||||
|
server=1.1.1.1
|
||||||
|
|
||||||
# Don't read /etc/resolv.conf
|
# Don't read /etc/resolv.conf
|
||||||
no-resolv
|
no-resolv
|
||||||
|
|
||||||
# Cache size
|
# Cache size
|
||||||
cache-size=10000
|
cache-size=10000
|
||||||
|
|
||||||
# Log queries (optional, disable in production)
|
|
||||||
# log-queries
|
|
||||||
|
|
||||||
# Russian TLDs - route directly (add to 'direct' ipset)
|
|
||||||
ipset=/ru/direct
|
|
||||||
ipset=/рф/direct
|
|
||||||
ipset=/su/direct
|
|
||||||
|
|
||||||
# Everything else goes to proxy (default)
|
|
||||||
# This is handled by routing table, not ipset
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Important:** dnsmasq's ipset feature requires ipset, not nftables sets. We'll use a hybrid approach.
|
### Step 2.10: Create Russian IP ranges update script
|
||||||
|
|
||||||
### Step 2.10: Updated routing approach (hybrid ipset + nftables)
|
Create `/etc/wireguard/update-direct-routes.sh`:
|
||||||
|
|
||||||
Since dnsmasq works with ipset, we'll use ipset for the sets and nftables for the rules.
|
|
||||||
|
|
||||||
Update `/etc/nftables.conf`:
|
|
||||||
|
|
||||||
```nft
|
|
||||||
#!/usr/sbin/nft -f
|
|
||||||
|
|
||||||
flush ruleset
|
|
||||||
|
|
||||||
table inet filter {
|
|
||||||
chain input {
|
|
||||||
type filter hook input priority 0; policy drop;
|
|
||||||
|
|
||||||
ct state established,related accept
|
|
||||||
iif lo accept
|
|
||||||
tcp dport 22 accept
|
|
||||||
udp dport 51820 accept
|
|
||||||
iifname "wg0" udp dport 53 accept
|
|
||||||
iifname "wg0" tcp dport 53 accept
|
|
||||||
icmp type echo-request accept
|
|
||||||
}
|
|
||||||
|
|
||||||
chain forward {
|
|
||||||
type filter hook forward priority 0; policy drop;
|
|
||||||
|
|
||||||
iifname "wg0" accept
|
|
||||||
iifname "wg1" accept
|
|
||||||
ct state established,related accept
|
|
||||||
}
|
|
||||||
|
|
||||||
chain output {
|
|
||||||
type filter hook output priority 0; policy accept;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table inet nat {
|
|
||||||
chain postrouting {
|
|
||||||
type nat hook postrouting priority 100;
|
|
||||||
|
|
||||||
# NAT direct traffic going out main interface
|
|
||||||
oifname != "wg0" oifname != "wg1" ip saddr 10.10.0.0/24 masquerade
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Update `/etc/wireguard/postup.sh`:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Downloads Russian IP ranges from RIPE and loads them into nftables
|
||||||
|
# Run after services are started, and weekly via cron
|
||||||
|
#
|
||||||
|
|
||||||
# Create ipsets
|
set -e
|
||||||
ipset create direct hash:net -exist
|
|
||||||
ipset flush direct
|
|
||||||
|
|
||||||
# Add default route via DE tunnel for 'proxy' table
|
RIPE_URL="https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest"
|
||||||
ip route add default via 10.20.0.2 table proxy 2>/dev/null || true
|
TEMP_FILE="/tmp/ripe-delegated.txt"
|
||||||
|
|
||||||
# Policy routing: packets NOT going to 'direct' ipset use 'proxy' table
|
echo "Downloading RIPE delegation data..."
|
||||||
ip rule add from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
curl -s "$RIPE_URL" -o "$TEMP_FILE"
|
||||||
|
|
||||||
# Load nftables
|
echo "Parsing Russian IP allocations..."
|
||||||
nft -f /etc/nftables.conf
|
RU_RANGES=$(grep '|RU|ipv4|' "$TEMP_FILE" | while IFS='|' read -r _ _ _ start count _ _ _; do
|
||||||
|
if [[ "$count" =~ ^[0-9]+$ ]] && [ "$count" -gt 0 ]; then
|
||||||
|
prefix=$(echo "32 - l($count)/l(2)" | bc -l | cut -d. -f1)
|
||||||
|
echo "$start/$prefix"
|
||||||
|
fi
|
||||||
|
done)
|
||||||
|
|
||||||
# Add iptables rule for fwmark (nftables mangle is complex with ipset)
|
echo "Flushing existing 'direct' set..."
|
||||||
iptables -t mangle -A PREROUTING -m set ! --match-set direct dst -s 10.10.0.0/24 -j MARK --set-mark 0x1
|
nft flush set ip vpn-routing direct 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Adding ranges to nftables..."
|
||||||
|
echo "$RU_RANGES" | while read -r cidr; do
|
||||||
|
[[ -n "$cidr" ]] && nft add element ip vpn-routing direct { "$cidr" } 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f "$TEMP_FILE"
|
||||||
|
echo "Done. Russian IP ranges loaded."
|
||||||
```
|
```
|
||||||
|
|
||||||
Update `/etc/wireguard/postdown.sh`:
|
Make executable and set up cron:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
chmod +x /etc/wireguard/update-direct-routes.sh
|
||||||
|
|
||||||
ip rule del from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
# Weekly updates
|
||||||
ip route flush table proxy 2>/dev/null || true
|
cat > /etc/cron.weekly/update-vpn-routes << 'EOF'
|
||||||
iptables -t mangle -F PREROUTING 2>/dev/null || true
|
#!/bin/bash
|
||||||
ipset destroy direct 2>/dev/null || true
|
/etc/wireguard/update-direct-routes.sh >> /var/log/vpn-routes-update.log 2>&1
|
||||||
nft flush ruleset
|
EOF
|
||||||
|
chmod +x /etc/cron.weekly/update-vpn-routes
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2.11: Enable and start services
|
### Step 2.11: Enable and start services
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl enable --now dnsmasq
|
systemctl enable nftables dnsmasq wg-quick@wg0 wg-quick@wg1
|
||||||
systemctl enable --now wg-quick@wg0
|
systemctl start dnsmasq
|
||||||
systemctl enable --now wg-quick@wg1
|
systemctl start wg-quick@wg1
|
||||||
|
systemctl start wg-quick@wg0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2.12: Verify
|
### Step 2.12: Load Russian IP ranges
|
||||||
|
|
||||||
|
After the tunnel is up:
|
||||||
|
```bash
|
||||||
|
/etc/wireguard/update-direct-routes.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2.13: Verify
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wg show
|
wg show
|
||||||
ip route show table proxy
|
ip route show table proxy
|
||||||
ipset list direct
|
ip rule show
|
||||||
|
nft list set ip vpn-routing direct | head -20
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -620,19 +593,18 @@ dig @127.0.0.1 google.com
|
|||||||
### Routing not working
|
### Routing not working
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check ipset
|
# Check nftables set
|
||||||
ipset list direct
|
nft list set ip vpn-routing direct
|
||||||
|
|
||||||
# Check routing table
|
# Check routing table
|
||||||
ip route show table proxy
|
ip route show table proxy
|
||||||
ip rule show
|
ip rule show
|
||||||
|
|
||||||
# Check marks
|
# Check nftables rules
|
||||||
iptables -t mangle -L -v
|
nft list chain ip vpn-routing prerouting
|
||||||
|
|
||||||
# Test marking
|
# Test packet marking (watch counters)
|
||||||
ping -c 1 8.8.8.8
|
nft list ruleset | grep -A5 "chain prerouting"
|
||||||
conntrack -L | grep 8.8.8.8
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Traffic not NATed
|
### Traffic not NATed
|
||||||
@@ -660,16 +632,17 @@ cat /proc/sys/net/ipv4/ip_forward
|
|||||||
- [ ] **RU VDS**
|
- [ ] **RU VDS**
|
||||||
- [ ] WireGuard installed
|
- [ ] WireGuard installed
|
||||||
- [ ] dnsmasq installed
|
- [ ] dnsmasq installed
|
||||||
- [ ] ipset installed
|
- [ ] nftables installed
|
||||||
- [ ] IP forwarding enabled
|
- [ ] IP forwarding enabled
|
||||||
- [ ] Keys generated
|
- [ ] Keys generated
|
||||||
- [ ] Routing table 'proxy' added
|
- [ ] Routing table 'proxy' added
|
||||||
- [ ] wg0.conf configured (users)
|
- [ ] wg0.conf configured (users)
|
||||||
- [ ] wg1.conf configured (DE tunnel)
|
- [ ] wg1.conf configured (DE tunnel)
|
||||||
- [ ] postup.sh / postdown.sh created
|
- [ ] postup.sh / postdown.sh created
|
||||||
- [ ] nftables configured
|
- [ ] nftables configured (with vpn-routing table)
|
||||||
- [ ] dnsmasq configured
|
- [ ] dnsmasq configured
|
||||||
- [ ] Services enabled and started
|
- [ ] Services enabled and started
|
||||||
|
- [ ] Russian IP ranges loaded (update-direct-routes.sh)
|
||||||
|
|
||||||
- [ ] **Key Exchange**
|
- [ ] **Key Exchange**
|
||||||
- [ ] DE public key → RU wg1.conf
|
- [ ] DE public key → RU wg1.conf
|
||||||
|
|||||||
@@ -169,15 +169,12 @@ systemctl stop dnsmasq
|
|||||||
# Start systemd-resolved if it was stopped
|
# Start systemd-resolved if it was stopped
|
||||||
systemctl start systemd-resolved
|
systemctl start systemd-resolved
|
||||||
|
|
||||||
# Flush firewall (same as above)
|
# Flush firewall
|
||||||
nft flush ruleset
|
nft flush ruleset
|
||||||
iptables -F
|
|
||||||
iptables -X
|
# Remove policy routing
|
||||||
iptables -t nat -F
|
ip rule del from 10.10.0.0/24 fwmark 0x1 table proxy 2>/dev/null || true
|
||||||
iptables -t nat -X
|
ip route flush table proxy 2>/dev/null || true
|
||||||
iptables -P INPUT ACCEPT
|
|
||||||
iptables -P FORWARD ACCEPT
|
|
||||||
iptables -P OUTPUT ACCEPT
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Post-Deployment Preparation
|
## Post-Deployment Preparation
|
||||||
|
|||||||
@@ -11,18 +11,17 @@ This project implements a split-tunnel VPN network for bypassing internet restri
|
|||||||
- RU VDS (176.124.216.197) - Gateway with selective routing
|
- RU VDS (176.124.216.197) - Gateway with selective routing
|
||||||
- DE VDS (194.31.173.178) - Exit node in Germany
|
- DE VDS (194.31.173.178) - Exit node in Germany
|
||||||
|
|
||||||
**Key feature:** DNS-based domain routing
|
**Key feature:** IP-based selective routing
|
||||||
- `.ru`, `.рф`, `.su` domains → Direct routing (fast, no proxy)
|
- Russian IP ranges (from RIPE database) → Direct routing (fast, no proxy)
|
||||||
- All other domains → Routed through Germany (bypass blocks)
|
- All other IPs → Routed through Germany (bypass blocks)
|
||||||
|
|
||||||
## Technology Stack
|
## Technology Stack
|
||||||
|
|
||||||
| Component | Purpose |
|
| Component | Purpose |
|
||||||
|-----------|---------|
|
|-----------|---------|
|
||||||
| WireGuard | VPN tunnels (lightweight, fast) |
|
| WireGuard | VPN tunnels (lightweight, fast) |
|
||||||
| dnsmasq | DNS server with ipset integration |
|
| dnsmasq | DNS server for VPN clients |
|
||||||
| nftables | Firewall and NAT |
|
| nftables | Firewall, NAT, and packet marking (pure nftables, no iptables) |
|
||||||
| ipset | IP address sets for routing decisions |
|
|
||||||
| iproute2 | Policy-based routing |
|
| iproute2 | Policy-based routing |
|
||||||
|
|
||||||
## What's Included
|
## What's Included
|
||||||
@@ -47,8 +46,9 @@ This project implements a split-tunnel VPN network for bypassing internet restri
|
|||||||
- `wg1.conf` - DE tunnel interface
|
- `wg1.conf` - DE tunnel interface
|
||||||
- `postup.sh` - Routing setup script
|
- `postup.sh` - Routing setup script
|
||||||
- `postdown.sh` - Routing cleanup script
|
- `postdown.sh` - Routing cleanup script
|
||||||
- `nftables.conf` - Firewall rules
|
- `nftables.conf` - Firewall and packet marking rules
|
||||||
- `vpn-routing.conf` - dnsmasq domain routing
|
- `vpn-routing.conf` - dnsmasq config
|
||||||
|
- `update-direct-routes.sh` - Russian IP ranges loader
|
||||||
- `rt_tables` - Custom routing table definitions
|
- `rt_tables` - Custom routing table definitions
|
||||||
- `99-vpn.conf` - Kernel parameters
|
- `99-vpn.conf` - Kernel parameters
|
||||||
|
|
||||||
@@ -83,9 +83,9 @@ This project implements a split-tunnel VPN network for bypassing internet restri
|
|||||||
- Enable/disable without key regeneration
|
- Enable/disable without key regeneration
|
||||||
|
|
||||||
### Smart Routing
|
### Smart Routing
|
||||||
- Automatic domain-based routing
|
- IP-based routing using RIPE database
|
||||||
- No manual IP list maintenance
|
- Automatic weekly updates of Russian IP ranges
|
||||||
- DNS-driven routing decisions
|
- Pure nftables implementation (no iptables/ipset mixing)
|
||||||
- Optimal performance for local traffic
|
- Optimal performance for local traffic
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
@@ -119,7 +119,7 @@ This project implements a split-tunnel VPN network for bypassing internet restri
|
|||||||
│ │ │
|
│ │ │
|
||||||
│ ▼ │
|
│ ▼ │
|
||||||
│ Routing Decision │
|
│ Routing Decision │
|
||||||
│ (dnsmasq + ipset) │
|
│ (nftables + RIPE data) │
|
||||||
│ │
|
│ │
|
||||||
│ WireGuard tunnel │
|
│ WireGuard tunnel │
|
||||||
│ (all other domains) │
|
│ (all other domains) │
|
||||||
@@ -206,7 +206,7 @@ Comprehensive testing checklist covers:
|
|||||||
|-------|-------|-----|
|
|-------|-------|-----|
|
||||||
| Tunnel down | `wg show` | Restart wg-quick services |
|
| Tunnel down | `wg show` | Restart wg-quick services |
|
||||||
| DNS not working | `systemctl status dnsmasq` | Restart dnsmasq |
|
| DNS not working | `systemctl status dnsmasq` | Restart dnsmasq |
|
||||||
| Routing wrong | `ipset list direct` | Check dnsmasq config |
|
| Routing wrong | `nft list set ip vpn-routing direct` | Run update-direct-routes.sh |
|
||||||
| Client can't connect | `wg show wg0 peers` | Verify peer added |
|
| Client can't connect | `wg show wg0 peers` | Verify peer added |
|
||||||
| Slow performance | `ping` tests | Check MTU settings |
|
| Slow performance | `ping` tests | Check MTU settings |
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,17 @@ systemctl start wg-quick@wg1
|
|||||||
systemctl start wg-quick@wg0
|
systemctl start wg-quick@wg0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 5: Verify Tunnel (2 min)
|
### Step 5: Verify Tunnel & Load Routes (3 min)
|
||||||
|
|
||||||
**On RU VDS:**
|
**On RU VDS:**
|
||||||
```bash
|
```bash
|
||||||
|
# Test tunnel
|
||||||
ping -c 4 10.20.0.2
|
ping -c 4 10.20.0.2
|
||||||
wg show wg1
|
wg show wg1
|
||||||
# Should see successful ping and recent handshake
|
# Should see successful ping and recent handshake
|
||||||
|
|
||||||
|
# Load Russian IP ranges (takes 1-2 minutes)
|
||||||
|
/etc/wireguard/update-direct-routes.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 6: Add First Client (5 min)
|
### Step 6: Add First Client (5 min)
|
||||||
@@ -182,8 +186,11 @@ journalctl -u dnsmasq -n 50
|
|||||||
|
|
||||||
**Routing not working?**
|
**Routing not working?**
|
||||||
```bash
|
```bash
|
||||||
# Check ipset after visiting .ru sites
|
# Check if Russian IP ranges are loaded
|
||||||
ipset list direct
|
nft list set ip vpn-routing direct | wc -l
|
||||||
|
|
||||||
|
# Reload if needed
|
||||||
|
/etc/wireguard/update-direct-routes.sh
|
||||||
|
|
||||||
# Check routing table
|
# Check routing table
|
||||||
ip route show table proxy
|
ip route show table proxy
|
||||||
|
|||||||
34
README.md
34
README.md
@@ -58,12 +58,16 @@ A WireGuard-based VPN network with selective domain routing. Traffic to `.ru` an
|
|||||||
|
|
||||||
1. Client connects to RU VDS via WireGuard
|
1. Client connects to RU VDS via WireGuard
|
||||||
2. Client uses RU VDS as DNS server (10.10.0.1)
|
2. Client uses RU VDS as DNS server (10.10.0.1)
|
||||||
3. dnsmasq on RU VDS resolves DNS queries:
|
3. Russian IP ranges are loaded into nftables `direct` set (from RIPE database)
|
||||||
- For `.ru` and `.рф` domains → adds resolved IPs to `direct` ipset
|
4. nftables marks packets based on destination:
|
||||||
- For all other domains → adds resolved IPs to `proxy` ipset
|
- IPs in `direct` set → no mark (routes directly via RU VDS)
|
||||||
4. nftables routes packets based on ipset membership:
|
- All other IPs → marked with `0x1` (routes via DE VDS tunnel)
|
||||||
- IPs in `direct` → route via RU VDS default gateway (direct internet)
|
5. Policy routing sends marked packets through the DE tunnel
|
||||||
- IPs in `proxy` → route via DE VDS tunnel (10.20.0.2)
|
|
||||||
|
**Why IP-based routing (not DNS-based)?**
|
||||||
|
- More reliable: works even if DNS is bypassed or cached
|
||||||
|
- Simpler: no iptables/ipset mixing, pure nftables
|
||||||
|
- Predictable: based on authoritative RIPE data
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
@@ -72,9 +76,10 @@ A WireGuard-based VPN network with selective domain routing. Traffic to `.ru` an
|
|||||||
- **WireGuard**: Two interfaces
|
- **WireGuard**: Two interfaces
|
||||||
- `wg0` - User-facing (10.10.0.1/24)
|
- `wg0` - User-facing (10.10.0.1/24)
|
||||||
- `wg1` - DE VDS tunnel (10.20.0.1/30)
|
- `wg1` - DE VDS tunnel (10.20.0.1/30)
|
||||||
- **dnsmasq**: DNS server with ipset integration
|
- **dnsmasq**: DNS server for VPN clients
|
||||||
- **nftables**: Packet marking and routing
|
- **nftables**: Firewall, NAT, and packet marking (pure nftables, no iptables)
|
||||||
- **iproute2**: Policy-based routing tables
|
- **iproute2**: Policy-based routing tables
|
||||||
|
- **update-direct-routes.sh**: Loads Russian IP ranges from RIPE
|
||||||
|
|
||||||
### DE VDS (Exit Node)
|
### DE VDS (Exit Node)
|
||||||
|
|
||||||
@@ -99,10 +104,11 @@ vpn.git/
|
|||||||
│ │ ├── wg1.conf # DE tunnel config
|
│ │ ├── wg1.conf # DE tunnel config
|
||||||
│ │ ├── postup.sh # Routing setup script
|
│ │ ├── postup.sh # Routing setup script
|
||||||
│ │ ├── postdown.sh # Routing cleanup script
|
│ │ ├── postdown.sh # Routing cleanup script
|
||||||
│ │ ├── nftables.conf # Firewall rules
|
│ │ ├── nftables.conf # Firewall + packet marking
|
||||||
│ │ ├── 99-vpn.conf # Sysctl settings
|
│ │ ├── 99-vpn.conf # Sysctl settings
|
||||||
│ │ ├── rt_tables # Routing tables
|
│ │ ├── rt_tables # Routing tables
|
||||||
│ │ └── vpn-routing.conf # dnsmasq config
|
│ │ ├── vpn-routing.conf # dnsmasq config
|
||||||
|
│ │ └── update-direct-routes.sh # Russian IP loader
|
||||||
│ └── client-templates/ # Client config templates
|
│ └── client-templates/ # Client config templates
|
||||||
│ └── example-client.conf
|
│ └── example-client.conf
|
||||||
└── scripts/ # Management scripts
|
└── scripts/ # Management scripts
|
||||||
@@ -215,9 +221,15 @@ wg show wg0 latest-handshakes
|
|||||||
# DNS cache stats
|
# DNS cache stats
|
||||||
kill -USR1 $(pidof dnsmasq) && journalctl -u dnsmasq -n 20
|
kill -USR1 $(pidof dnsmasq) && journalctl -u dnsmasq -n 20
|
||||||
|
|
||||||
|
# View nftables rules and sets
|
||||||
|
nft list ruleset
|
||||||
|
|
||||||
|
# Check direct routes set (Russian IPs)
|
||||||
|
nft list set ip vpn-routing direct
|
||||||
|
|
||||||
# Routing tables
|
# Routing tables
|
||||||
ip route show table direct
|
|
||||||
ip route show table proxy
|
ip route show table proxy
|
||||||
|
ip rule show
|
||||||
```
|
```
|
||||||
|
|
||||||
### View logs
|
### View logs
|
||||||
|
|||||||
22
TESTING.md
22
TESTING.md
@@ -33,10 +33,11 @@ Use this checklist to verify your VPN network is working correctly.
|
|||||||
|
|
||||||
- [ ] WireGuard installed (`wg version`)
|
- [ ] WireGuard installed (`wg version`)
|
||||||
- [ ] dnsmasq installed (`dnsmasq -v`)
|
- [ ] dnsmasq installed (`dnsmasq -v`)
|
||||||
- [ ] ipset installed (`ipset -v`)
|
- [ ] nftables installed (`nft -v`)
|
||||||
- [ ] IP forwarding enabled (`cat /proc/sys/net/ipv4/ip_forward` = 1)
|
- [ ] IP forwarding enabled (`cat /proc/sys/net/ipv4/ip_forward` = 1)
|
||||||
- [ ] WireGuard keys generated (`ls /etc/wireguard/keys/`)
|
- [ ] WireGuard keys generated (`ls /etc/wireguard/keys/`)
|
||||||
- [ ] Routing table added (`grep proxy /etc/iproute2/rt_tables`)
|
- [ ] Routing table added (`grep proxy /etc/iproute2/rt_tables`)
|
||||||
|
- [ ] Update script exists (`ls /etc/wireguard/update-direct-routes.sh`)
|
||||||
- [ ] All configs in place
|
- [ ] All configs in place
|
||||||
- [ ] Services enabled (not yet started)
|
- [ ] Services enabled (not yet started)
|
||||||
|
|
||||||
@@ -82,8 +83,9 @@ Use this checklist to verify your VPN network is working correctly.
|
|||||||
- [ ] Proxy routing table exists: `ip route show table proxy`
|
- [ ] Proxy routing table exists: `ip route show table proxy`
|
||||||
- [ ] Default route via DE: `ip route show table proxy | grep "default via 10.20.0.2"`
|
- [ ] Default route via DE: `ip route show table proxy | grep "default via 10.20.0.2"`
|
||||||
- [ ] Policy routing rule exists: `ip rule show | grep proxy`
|
- [ ] Policy routing rule exists: `ip rule show | grep proxy`
|
||||||
- [ ] ipset 'direct' exists: `ipset list direct`
|
- [ ] nftables 'direct' set exists: `nft list set ip vpn-routing direct`
|
||||||
- [ ] iptables mangle rule exists: `iptables -t mangle -L PREROUTING | grep direct`
|
- [ ] nftables prerouting chain exists: `nft list chain ip vpn-routing prerouting`
|
||||||
|
- [ ] Russian IP ranges loaded: `nft list set ip vpn-routing direct | grep -c elements`
|
||||||
|
|
||||||
## Client Connection Tests
|
## Client Connection Tests
|
||||||
|
|
||||||
@@ -115,16 +117,14 @@ Use this checklist to verify your VPN network is working correctly.
|
|||||||
- [ ] Can access international sites: `curl -I https://google.com`
|
- [ ] Can access international sites: `curl -I https://google.com`
|
||||||
- [ ] Can access Russian sites: `curl -I https://yandex.ru`
|
- [ ] Can access Russian sites: `curl -I https://yandex.ru`
|
||||||
|
|
||||||
### DNS-Based Routing (From Client)
|
### IP-Based Routing (From Client)
|
||||||
|
|
||||||
Visit some Russian sites first, then check on RU VDS:
|
Russian IPs are pre-loaded from RIPE database:
|
||||||
|
|
||||||
- [ ] Visit `https://yandex.ru` from client
|
- [ ] Verify Russian IP ranges are loaded on RU VDS: `nft list set ip vpn-routing direct | wc -l`
|
||||||
- [ ] Visit `https://mail.ru` from client
|
- [ ] Visit `https://yandex.ru` from client (should be fast, direct route)
|
||||||
- [ ] Check ipset on RU VDS: `ipset list direct`
|
- [ ] Visit `https://mail.ru` from client (should be fast, direct route)
|
||||||
- [ ] ipset contains Russian IPs
|
- [ ] Visit `https://google.com` from client (should go through DE tunnel)
|
||||||
- [ ] Visit `https://google.com` from client
|
|
||||||
- [ ] Check that google IPs NOT in direct ipset
|
|
||||||
|
|
||||||
### Advanced Routing Tests
|
### Advanced Routing Tests
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
#!/usr/sbin/nft -f
|
#!/usr/sbin/nft -f
|
||||||
|
#
|
||||||
|
# RU VDS nftables configuration
|
||||||
|
#
|
||||||
|
# Routing approach:
|
||||||
|
# - dnsmasq populates the 'direct' nftables set via helper script
|
||||||
|
# - nftables marks packets for policy routing
|
||||||
|
# - No iptables dependency
|
||||||
|
#
|
||||||
|
|
||||||
flush ruleset
|
flush ruleset
|
||||||
|
|
||||||
@@ -44,12 +52,37 @@ table inet filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table ip vpn-routing {
|
||||||
|
# Set for Russian domain IPs (direct routing, no proxy)
|
||||||
|
# Populated by dnsmasq via /etc/wireguard/nft-set-add.sh
|
||||||
|
# Auto-expires entries after 6 hours
|
||||||
|
set direct {
|
||||||
|
type ipv4_addr
|
||||||
|
flags timeout
|
||||||
|
timeout 6h
|
||||||
|
}
|
||||||
|
|
||||||
|
# Packet marking chain for policy routing
|
||||||
|
chain prerouting {
|
||||||
|
type filter hook prerouting priority mangle; policy accept;
|
||||||
|
|
||||||
|
# Only process traffic from VPN clients
|
||||||
|
ip saddr != 10.10.0.0/24 return
|
||||||
|
|
||||||
|
# Destinations in 'direct' set: no mark (direct routing)
|
||||||
|
ip daddr @direct return
|
||||||
|
|
||||||
|
# Everything else: mark for proxy routing via DE tunnel
|
||||||
|
meta mark set 0x1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table inet nat {
|
table inet nat {
|
||||||
chain postrouting {
|
chain postrouting {
|
||||||
type nat hook postrouting priority 100;
|
type nat hook postrouting priority 100;
|
||||||
|
|
||||||
# NAT direct traffic going out main interface
|
# NAT traffic going out to internet directly (not via wg1 tunnel)
|
||||||
# Traffic going through wg1 doesn't need NAT (DE VDS will NAT it)
|
# Traffic via wg1 will be NATed by DE VDS
|
||||||
oifname != "wg0" oifname != "wg1" ip saddr 10.10.0.0/24 masquerade
|
oifname != "wg0" oifname != "wg1" ip saddr 10.10.0.0/24 masquerade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# PostDown script for WireGuard wg0 interface
|
||||||
|
# Pure nftables solution - no iptables/ipset dependencies
|
||||||
|
#
|
||||||
|
|
||||||
# Remove policy routing rule
|
# Remove policy routing rule
|
||||||
ip rule del from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
ip rule del from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
||||||
|
|
||||||
# Flush routing table
|
# Flush routing table
|
||||||
ip route flush table proxy 2>/dev/null || true
|
ip route flush table proxy 2>/dev/null || true
|
||||||
|
|
||||||
# Remove iptables mangle rule
|
# Flush nftables vpn-routing table (keeps filter and nat rules intact)
|
||||||
iptables -t mangle -F PREROUTING 2>/dev/null || true
|
nft flush table ip vpn-routing 2>/dev/null || true
|
||||||
|
|
||||||
# Destroy ipsets
|
|
||||||
ipset destroy direct 2>/dev/null || true
|
|
||||||
|
|
||||||
# Flush nftables (if not managed by other services)
|
|
||||||
# nft flush ruleset
|
|
||||||
|
|
||||||
echo "PostDown script completed"
|
echo "PostDown script completed"
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Create ipsets for routing decisions
|
#
|
||||||
ipset create direct hash:net -exist
|
# PostUp script for WireGuard wg0 interface
|
||||||
ipset flush direct
|
# Pure nftables solution - no iptables/ipset dependencies
|
||||||
|
#
|
||||||
|
|
||||||
|
# Load nftables rules (includes the 'direct' set and packet marking)
|
||||||
|
nft -f /etc/nftables.conf
|
||||||
|
|
||||||
# Add default route via DE tunnel for 'proxy' table
|
# Add default route via DE tunnel for 'proxy' table
|
||||||
ip route add default via 10.20.0.2 dev wg1 table proxy 2>/dev/null || true
|
ip route add default via 10.20.0.2 dev wg1 table proxy 2>/dev/null || true
|
||||||
@@ -11,11 +15,4 @@ ip route add default via 10.20.0.2 dev wg1 table proxy 2>/dev/null || true
|
|||||||
# Policy routing: packets with fwmark 0x1 use 'proxy' table
|
# Policy routing: packets with fwmark 0x1 use 'proxy' table
|
||||||
ip rule add from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
ip rule add from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
||||||
|
|
||||||
# Load nftables rules
|
|
||||||
nft -f /etc/nftables.conf
|
|
||||||
|
|
||||||
# Mark packets NOT going to 'direct' ipset with fwmark 0x1
|
|
||||||
# This is needed because nftables + ipset integration is complex
|
|
||||||
iptables -t mangle -I PREROUTING -m set ! --match-set direct dst -s 10.10.0.0/24 -j MARK --set-mark 0x1
|
|
||||||
|
|
||||||
echo "PostUp script completed successfully"
|
echo "PostUp script completed successfully"
|
||||||
|
|||||||
58
configs/ru-vds/update-direct-routes.sh
Normal file
58
configs/ru-vds/update-direct-routes.sh
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Downloads Russian IP ranges and adds them to the nftables 'direct' set
|
||||||
|
# These IPs will be routed directly instead of through the DE proxy
|
||||||
|
#
|
||||||
|
# Run this script:
|
||||||
|
# - Once during initial setup
|
||||||
|
# - Periodically via cron (weekly) to keep ranges updated
|
||||||
|
#
|
||||||
|
# Data source: RIPE NCC delegated statistics
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RIPE_URL="https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest"
|
||||||
|
TEMP_FILE="/tmp/ripe-delegated.txt"
|
||||||
|
NFT_TABLE="ip vpn-routing"
|
||||||
|
NFT_SET="direct"
|
||||||
|
|
||||||
|
echo "Downloading RIPE delegation data..."
|
||||||
|
curl -s "$RIPE_URL" -o "$TEMP_FILE"
|
||||||
|
|
||||||
|
echo "Parsing Russian IP allocations..."
|
||||||
|
|
||||||
|
# Extract Russian IPv4 allocations and convert to CIDR notation
|
||||||
|
# Format: ripencc|RU|ipv4|start_ip|count|date|status
|
||||||
|
RU_RANGES=$(grep '|RU|ipv4|' "$TEMP_FILE" | while IFS='|' read -r registry cc type start count date status rest; do
|
||||||
|
# Convert count to CIDR prefix length
|
||||||
|
# count is number of IPs, prefix = 32 - log2(count)
|
||||||
|
if [[ "$count" =~ ^[0-9]+$ ]]; then
|
||||||
|
prefix=$(echo "32 - l($count)/l(2)" | bc -l | cut -d. -f1)
|
||||||
|
echo "$start/$prefix"
|
||||||
|
fi
|
||||||
|
done)
|
||||||
|
|
||||||
|
# Count ranges
|
||||||
|
RANGE_COUNT=$(echo "$RU_RANGES" | wc -l)
|
||||||
|
echo "Found $RANGE_COUNT Russian IP ranges"
|
||||||
|
|
||||||
|
echo "Flushing existing 'direct' set..."
|
||||||
|
nft flush set $NFT_TABLE $NFT_SET 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Adding ranges to nftables set..."
|
||||||
|
# Add in batches for efficiency
|
||||||
|
echo "$RU_RANGES" | while read -r cidr; do
|
||||||
|
if [[ -n "$cidr" ]]; then
|
||||||
|
nft add element $NFT_TABLE $NFT_SET { "$cidr" } 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -f "$TEMP_FILE"
|
||||||
|
|
||||||
|
# Show stats
|
||||||
|
FINAL_COUNT=$(nft list set $NFT_TABLE $NFT_SET 2>/dev/null | grep -c "elements" || echo "0")
|
||||||
|
echo "Done. Set '$NFT_SET' populated with Russian IP ranges."
|
||||||
|
echo ""
|
||||||
|
echo "To verify: nft list set $NFT_TABLE $NFT_SET"
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
# dnsmasq configuration for VPN routing
|
||||||
|
#
|
||||||
|
# Note: Routing decisions are now based on destination IP ranges,
|
||||||
|
# not DNS responses. Russian IP ranges are loaded into nftables
|
||||||
|
# by the update-direct-routes.sh script.
|
||||||
|
|
||||||
# Listen only on VPN interface
|
# Listen only on VPN interface
|
||||||
interface=wg0
|
interface=wg0
|
||||||
bind-interfaces
|
bind-interfaces
|
||||||
@@ -13,18 +19,5 @@ no-resolv
|
|||||||
# Cache settings
|
# Cache settings
|
||||||
cache-size=10000
|
cache-size=10000
|
||||||
|
|
||||||
# Log queries (optional, comment out in production for performance)
|
# Log queries (optional, uncomment for debugging)
|
||||||
# log-queries
|
# log-queries
|
||||||
|
|
||||||
# Russian TLDs - add resolved IPs to 'direct' ipset
|
|
||||||
# These domains will be routed directly, not through DE VDS
|
|
||||||
ipset=/ru/direct
|
|
||||||
ipset=/рф/direct
|
|
||||||
ipset=/su/direct
|
|
||||||
|
|
||||||
# Additional Russian domains (optional, can be extended)
|
|
||||||
# ipset=/yandex.ru/direct
|
|
||||||
# ipset=/mail.ru/direct
|
|
||||||
# ipset=/vk.com/direct
|
|
||||||
|
|
||||||
# All other domains will go through proxy (default routing)
|
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ set -e
|
|||||||
|
|
||||||
# Setup script for RU VDS (Gateway)
|
# Setup script for RU VDS (Gateway)
|
||||||
# Run this script as root on the RU VDS server
|
# Run this script as root on the RU VDS server
|
||||||
|
#
|
||||||
|
# This script sets up:
|
||||||
|
# - WireGuard VPN (dual interface: users + DE tunnel)
|
||||||
|
# - dnsmasq for DNS resolution
|
||||||
|
# - nftables for firewall and routing
|
||||||
|
# - Policy routing for split-tunnel VPN
|
||||||
|
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
echo "RU VDS (Gateway) Setup"
|
echo "RU VDS (Gateway) Setup"
|
||||||
@@ -15,14 +21,14 @@ if [ "$EUID" -ne 0 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[1/10] Updating system packages..."
|
echo "[1/11] Updating system packages..."
|
||||||
apt update
|
apt update
|
||||||
apt upgrade -y
|
apt upgrade -y
|
||||||
|
|
||||||
echo "[2/10] Installing required packages..."
|
echo "[2/11] Installing required packages..."
|
||||||
apt install -y wireguard dnsmasq nftables iptables ipset qrencode
|
apt install -y wireguard dnsmasq nftables qrencode curl bc
|
||||||
|
|
||||||
echo "[3/10] Disabling systemd-resolved (conflicts with dnsmasq)..."
|
echo "[3/11] Disabling systemd-resolved (conflicts with dnsmasq)..."
|
||||||
systemctl disable --now systemd-resolved 2>/dev/null || true
|
systemctl disable --now systemd-resolved 2>/dev/null || true
|
||||||
rm -f /etc/resolv.conf
|
rm -f /etc/resolv.conf
|
||||||
cat > /etc/resolv.conf << 'EOF'
|
cat > /etc/resolv.conf << 'EOF'
|
||||||
@@ -30,14 +36,14 @@ nameserver 8.8.8.8
|
|||||||
nameserver 1.1.1.1
|
nameserver 1.1.1.1
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "[4/10] Enabling IP forwarding..."
|
echo "[4/11] Enabling IP forwarding..."
|
||||||
cat > /etc/sysctl.d/99-vpn.conf << 'EOF'
|
cat > /etc/sysctl.d/99-vpn.conf << 'EOF'
|
||||||
# Enable IP forwarding for VPN
|
# Enable IP forwarding for VPN
|
||||||
net.ipv4.ip_forward = 1
|
net.ipv4.ip_forward = 1
|
||||||
EOF
|
EOF
|
||||||
sysctl -p /etc/sysctl.d/99-vpn.conf
|
sysctl -p /etc/sysctl.d/99-vpn.conf
|
||||||
|
|
||||||
echo "[5/10] Generating WireGuard keys..."
|
echo "[5/11] Generating WireGuard keys..."
|
||||||
mkdir -p /etc/wireguard/keys
|
mkdir -p /etc/wireguard/keys
|
||||||
chmod 700 /etc/wireguard/keys
|
chmod 700 /etc/wireguard/keys
|
||||||
|
|
||||||
@@ -49,12 +55,12 @@ wg genkey | tee /etc/wireguard/keys/de-tunnel.key | wg pubkey > /etc/wireguard/k
|
|||||||
|
|
||||||
chmod 600 /etc/wireguard/keys/*
|
chmod 600 /etc/wireguard/keys/*
|
||||||
|
|
||||||
echo "[6/10] Adding custom routing table..."
|
echo "[6/11] Adding custom routing table..."
|
||||||
if ! grep -q "^200[[:space:]]*proxy" /etc/iproute2/rt_tables; then
|
if ! grep -q "^200[[:space:]]*proxy" /etc/iproute2/rt_tables; then
|
||||||
echo "200 proxy" >> /etc/iproute2/rt_tables
|
echo "200 proxy" >> /etc/iproute2/rt_tables
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[7/10] Creating WireGuard configurations..."
|
echo "[7/11] Creating WireGuard configurations..."
|
||||||
|
|
||||||
# wg0 - user-facing
|
# wg0 - user-facing
|
||||||
cat > /etc/wireguard/wg0.conf << 'EOF'
|
cat > /etc/wireguard/wg0.conf << 'EOF'
|
||||||
@@ -91,16 +97,20 @@ EOF
|
|||||||
DE_TUNNEL_KEY=$(cat /etc/wireguard/keys/de-tunnel.key)
|
DE_TUNNEL_KEY=$(cat /etc/wireguard/keys/de-tunnel.key)
|
||||||
sed -i "s|__RU_DE_TUNNEL_PRIVATE_KEY__|${DE_TUNNEL_KEY}|g" /etc/wireguard/wg1.conf
|
sed -i "s|__RU_DE_TUNNEL_PRIVATE_KEY__|${DE_TUNNEL_KEY}|g" /etc/wireguard/wg1.conf
|
||||||
|
|
||||||
echo "[8/10] Creating WireGuard helper scripts..."
|
echo "[8/11] Creating WireGuard helper scripts..."
|
||||||
|
|
||||||
# PostUp script
|
# PostUp script
|
||||||
cat > /etc/wireguard/postup.sh << 'EOF'
|
cat > /etc/wireguard/postup.sh << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Create ipsets for routing decisions
|
#
|
||||||
ipset create direct hash:net -exist
|
# PostUp script for WireGuard wg0 interface
|
||||||
ipset flush direct
|
# Pure nftables solution - no iptables/ipset dependencies
|
||||||
|
#
|
||||||
|
|
||||||
|
# Load nftables rules (includes the 'direct' set and packet marking)
|
||||||
|
nft -f /etc/nftables.conf
|
||||||
|
|
||||||
# Add default route via DE tunnel for 'proxy' table
|
# Add default route via DE tunnel for 'proxy' table
|
||||||
ip route add default via 10.20.0.2 dev wg1 table proxy 2>/dev/null || true
|
ip route add default via 10.20.0.2 dev wg1 table proxy 2>/dev/null || true
|
||||||
@@ -108,12 +118,6 @@ ip route add default via 10.20.0.2 dev wg1 table proxy 2>/dev/null || true
|
|||||||
# Policy routing: packets with fwmark 0x1 use 'proxy' table
|
# Policy routing: packets with fwmark 0x1 use 'proxy' table
|
||||||
ip rule add from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
ip rule add from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
||||||
|
|
||||||
# Load nftables rules
|
|
||||||
nft -f /etc/nftables.conf
|
|
||||||
|
|
||||||
# Mark packets NOT going to 'direct' ipset with fwmark 0x1
|
|
||||||
iptables -t mangle -I PREROUTING -m set ! --match-set direct dst -s 10.10.0.0/24 -j MARK --set-mark 0x1
|
|
||||||
|
|
||||||
echo "PostUp script completed successfully"
|
echo "PostUp script completed successfully"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -121,17 +125,19 @@ EOF
|
|||||||
cat > /etc/wireguard/postdown.sh << 'EOF'
|
cat > /etc/wireguard/postdown.sh << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# PostDown script for WireGuard wg0 interface
|
||||||
|
# Pure nftables solution - no iptables/ipset dependencies
|
||||||
|
#
|
||||||
|
|
||||||
# Remove policy routing rule
|
# Remove policy routing rule
|
||||||
ip rule del from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
ip rule del from 10.10.0.0/24 fwmark 0x1 table proxy priority 100 2>/dev/null || true
|
||||||
|
|
||||||
# Flush routing table
|
# Flush routing table
|
||||||
ip route flush table proxy 2>/dev/null || true
|
ip route flush table proxy 2>/dev/null || true
|
||||||
|
|
||||||
# Remove iptables mangle rule
|
# Flush nftables vpn-routing table (keeps filter and nat rules intact)
|
||||||
iptables -t mangle -F PREROUTING 2>/dev/null || true
|
nft flush table ip vpn-routing 2>/dev/null || true
|
||||||
|
|
||||||
# Destroy ipsets
|
|
||||||
ipset destroy direct 2>/dev/null || true
|
|
||||||
|
|
||||||
echo "PostDown script completed"
|
echo "PostDown script completed"
|
||||||
EOF
|
EOF
|
||||||
@@ -139,9 +145,17 @@ EOF
|
|||||||
chmod +x /etc/wireguard/postup.sh
|
chmod +x /etc/wireguard/postup.sh
|
||||||
chmod +x /etc/wireguard/postdown.sh
|
chmod +x /etc/wireguard/postdown.sh
|
||||||
|
|
||||||
echo "[9/10] Creating nftables configuration..."
|
echo "[9/11] Creating nftables configuration..."
|
||||||
cat > /etc/nftables.conf << 'EOF'
|
cat > /etc/nftables.conf << 'EOF'
|
||||||
#!/usr/sbin/nft -f
|
#!/usr/sbin/nft -f
|
||||||
|
#
|
||||||
|
# RU VDS nftables configuration
|
||||||
|
#
|
||||||
|
# Routing approach:
|
||||||
|
# - Russian IP ranges loaded into 'direct' set by update-direct-routes.sh
|
||||||
|
# - nftables marks packets for policy routing
|
||||||
|
# - No iptables dependency
|
||||||
|
#
|
||||||
|
|
||||||
flush ruleset
|
flush ruleset
|
||||||
|
|
||||||
@@ -187,11 +201,36 @@ table inet filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table ip vpn-routing {
|
||||||
|
# Set for Russian IPs (direct routing, no proxy)
|
||||||
|
# Populated by /etc/wireguard/update-direct-routes.sh
|
||||||
|
# Auto-expires entries after 6 hours
|
||||||
|
set direct {
|
||||||
|
type ipv4_addr
|
||||||
|
flags interval, timeout
|
||||||
|
timeout 6h
|
||||||
|
}
|
||||||
|
|
||||||
|
# Packet marking chain for policy routing
|
||||||
|
chain prerouting {
|
||||||
|
type filter hook prerouting priority mangle; policy accept;
|
||||||
|
|
||||||
|
# Only process traffic from VPN clients
|
||||||
|
ip saddr != 10.10.0.0/24 return
|
||||||
|
|
||||||
|
# Destinations in 'direct' set: no mark (direct routing)
|
||||||
|
ip daddr @direct return
|
||||||
|
|
||||||
|
# Everything else: mark for proxy routing via DE tunnel
|
||||||
|
meta mark set 0x1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
table inet nat {
|
table inet nat {
|
||||||
chain postrouting {
|
chain postrouting {
|
||||||
type nat hook postrouting priority 100;
|
type nat hook postrouting priority 100;
|
||||||
|
|
||||||
# NAT direct traffic going out main interface
|
# NAT traffic going out to internet directly (not via wg1 tunnel)
|
||||||
oifname != "wg0" oifname != "wg1" ip saddr 10.10.0.0/24 masquerade
|
oifname != "wg0" oifname != "wg1" ip saddr 10.10.0.0/24 masquerade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,8 +238,14 @@ EOF
|
|||||||
|
|
||||||
chmod +x /etc/nftables.conf
|
chmod +x /etc/nftables.conf
|
||||||
|
|
||||||
echo "[10/10] Configuring dnsmasq..."
|
echo "[10/11] Configuring dnsmasq..."
|
||||||
cat > /etc/dnsmasq.d/vpn-routing.conf << 'EOF'
|
cat > /etc/dnsmasq.d/vpn-routing.conf << 'EOF'
|
||||||
|
# dnsmasq configuration for VPN routing
|
||||||
|
#
|
||||||
|
# Note: Routing decisions are based on destination IP ranges,
|
||||||
|
# not DNS responses. Russian IP ranges are loaded into nftables
|
||||||
|
# by the update-direct-routes.sh script.
|
||||||
|
|
||||||
# Listen only on VPN interface
|
# Listen only on VPN interface
|
||||||
interface=wg0
|
interface=wg0
|
||||||
bind-interfaces
|
bind-interfaces
|
||||||
@@ -216,17 +261,85 @@ no-resolv
|
|||||||
# Cache settings
|
# Cache settings
|
||||||
cache-size=10000
|
cache-size=10000
|
||||||
|
|
||||||
# Russian TLDs - add resolved IPs to 'direct' ipset
|
# Log queries (optional, uncomment for debugging)
|
||||||
ipset=/ru/direct
|
# log-queries
|
||||||
ipset=/рф/direct
|
|
||||||
ipset=/su/direct
|
|
||||||
|
|
||||||
# All other domains will go through proxy (default routing)
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
echo "[11/11] Creating Russian IP routes update script..."
|
||||||
|
cat > /etc/wireguard/update-direct-routes.sh << 'SCRIPT'
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Downloads Russian IP ranges and adds them to the nftables 'direct' set
|
||||||
|
# These IPs will be routed directly instead of through the DE proxy
|
||||||
|
#
|
||||||
|
# Run this script:
|
||||||
|
# - Once during initial setup (after services are started)
|
||||||
|
# - Periodically via cron (weekly) to keep ranges updated
|
||||||
|
#
|
||||||
|
# Data source: RIPE NCC delegated statistics
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RIPE_URL="https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest"
|
||||||
|
TEMP_FILE="/tmp/ripe-delegated.txt"
|
||||||
|
NFT_TABLE="ip vpn-routing"
|
||||||
|
NFT_SET="direct"
|
||||||
|
|
||||||
|
echo "Downloading RIPE delegation data..."
|
||||||
|
curl -s "$RIPE_URL" -o "$TEMP_FILE"
|
||||||
|
|
||||||
|
echo "Parsing Russian IP allocations..."
|
||||||
|
|
||||||
|
# Extract Russian IPv4 allocations and convert to CIDR notation
|
||||||
|
# Format: ripencc|RU|ipv4|start_ip|count|date|status
|
||||||
|
RU_RANGES=$(grep '|RU|ipv4|' "$TEMP_FILE" | while IFS='|' read -r registry cc type start count date status rest; do
|
||||||
|
# Convert count to CIDR prefix length
|
||||||
|
# count is number of IPs, prefix = 32 - log2(count)
|
||||||
|
if [[ "$count" =~ ^[0-9]+$ ]] && [ "$count" -gt 0 ]; then
|
||||||
|
prefix=$(echo "32 - l($count)/l(2)" | bc -l 2>/dev/null | cut -d. -f1)
|
||||||
|
if [[ "$prefix" =~ ^[0-9]+$ ]] && [ "$prefix" -ge 8 ] && [ "$prefix" -le 32 ]; then
|
||||||
|
echo "$start/$prefix"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done)
|
||||||
|
|
||||||
|
# Count ranges
|
||||||
|
RANGE_COUNT=$(echo "$RU_RANGES" | grep -c . || echo "0")
|
||||||
|
echo "Found $RANGE_COUNT Russian IP ranges"
|
||||||
|
|
||||||
|
echo "Flushing existing 'direct' set..."
|
||||||
|
nft flush set $NFT_TABLE $NFT_SET 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Adding ranges to nftables set (this may take a moment)..."
|
||||||
|
# Add in batches for efficiency
|
||||||
|
echo "$RU_RANGES" | while read -r cidr; do
|
||||||
|
if [[ -n "$cidr" ]] && [[ "$cidr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]]; then
|
||||||
|
nft add element $NFT_TABLE $NFT_SET { "$cidr" } 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -f "$TEMP_FILE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done. Russian IP ranges loaded into nftables."
|
||||||
|
echo "Traffic to these IPs will be routed directly (not through DE proxy)."
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
chmod +x /etc/wireguard/update-direct-routes.sh
|
||||||
|
|
||||||
# Create clients directory
|
# Create clients directory
|
||||||
mkdir -p /etc/wireguard/clients
|
mkdir -p /etc/wireguard/clients
|
||||||
|
|
||||||
|
# Add cron job for weekly updates
|
||||||
|
echo "Setting up weekly cron job for IP range updates..."
|
||||||
|
cat > /etc/cron.weekly/update-vpn-routes << 'CRON'
|
||||||
|
#!/bin/bash
|
||||||
|
/etc/wireguard/update-direct-routes.sh >> /var/log/vpn-routes-update.log 2>&1
|
||||||
|
CRON
|
||||||
|
chmod +x /etc/cron.weekly/update-vpn-routes
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "========================================="
|
echo "========================================="
|
||||||
echo "Setup completed!"
|
echo "Setup completed!"
|
||||||
@@ -248,14 +361,17 @@ echo "3. Edit /etc/wireguard/wg1.conf and replace:"
|
|||||||
echo " __DE_SERVER_PUBLIC_KEY__ with the actual DE VDS public key"
|
echo " __DE_SERVER_PUBLIC_KEY__ with the actual DE VDS public key"
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Enable and start services:"
|
echo "4. Enable and start services:"
|
||||||
echo " systemctl enable nftables dnsmasq"
|
echo " systemctl enable nftables dnsmasq wg-quick@wg0 wg-quick@wg1"
|
||||||
echo " systemctl start dnsmasq"
|
echo " systemctl start dnsmasq"
|
||||||
echo " systemctl start wg-quick@wg1"
|
echo " systemctl start wg-quick@wg1"
|
||||||
echo " systemctl start wg-quick@wg0"
|
echo " systemctl start wg-quick@wg0"
|
||||||
echo ""
|
echo ""
|
||||||
echo "5. Verify the tunnel:"
|
echo "5. Load Russian IP ranges (after tunnel is up):"
|
||||||
|
echo " /etc/wireguard/update-direct-routes.sh"
|
||||||
|
echo ""
|
||||||
|
echo "6. Verify the tunnel:"
|
||||||
echo " wg show"
|
echo " wg show"
|
||||||
echo " ping 10.20.0.2"
|
echo " ping 10.20.0.2"
|
||||||
echo ""
|
echo ""
|
||||||
echo "6. Add clients using: /root/add-client.sh <client_name>"
|
echo "7. Add clients using: /root/add-client.sh <client_name>"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user