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:
mguschin
2026-02-19 18:02:28 +03:00
parent b117efc604
commit f14d4f8f33
13 changed files with 461 additions and 264 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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"

View File

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

View File

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