| name | vpn-deployment |
| description | End-to-end WireGuard VPN deployment guide — VPS server setup, router client configuration, and traffic routing via apfree-wifidog tools. |
| user-invocable | true |
WireGuard VPN Deployment Guide
This skill guides you through the complete WireGuard VPN deployment workflow: installing and configuring a WireGuard server on a VPS, connecting an OpenWrt router as a client via apfree_wifidog_set_wireguard_vpn, and managing traffic routing via apfree_wifidog_set_vpn_routes.
Overview
VPS (WireGuard Server) Router (WireGuard Client)
┌──────────────────────┐ ┌──────────────────────┐
│ wg0: 10.0.0.1/24 │◄── WG tunnel ──► │ wg0: 10.0.0.2/24 │
│ Public IP: X.X.X.X │ │ LAN: 192.168.1.0/24 │
│ NAT masquerade │ │ ip route proto static│
└──────────────────────┘ └──────────────────────┘
│ │
Internet ◄─── selected/all traffic ────── LAN clients
Phase 1: VPS WireGuard Server Setup
These are local shell commands to run on the VPS directly (not through apfree-wifidog tools).
1.1 Install WireGuard
Detect the distro and use the appropriate package manager:
apt update && apt install -y wireguard
dnf install -y epel-release elrepo-release
dnf install -y kmod-wireguard wireguard-tools
yum install -y epel-release
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum install -y kmod-wireguard wireguard-tools
dnf install -y wireguard-tools
pacman -S --noconfirm wireguard-tools
which wg && which wg-quick
modprobe wireguard && echo "wireguard module loaded"
Note: On CentOS/RHEL with kernel < 5.6, the kmod-wireguard package provides the kernel module. On kernel >= 5.6 (including most CentOS 8 Stream / RHEL 9), WireGuard is built-in and only wireguard-tools is needed.
1.2 Generate Server Keys
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key
chmod 600 /etc/wireguard/server_private.key
1.3 Generate Router Public Key (Secure — keys generated on router)
Instead of generating router keys on the VPS, use apfree_wifidog_generate_wireguard_keys to generate the key pair on the router itself. The private key is written directly to UCI and never leaves the device. Only the public key is returned.
Tool: apfree_wifidog_generate_wireguard_keys
Params:
deviceId: "<router_device_id>"
Response will contain data.public_key — save this for the VPS [Peer] section below.
Security: The router's private key never traverses the network (MQTT/WebSocket). It is generated locally by wg genkey and stored in UCI network.wg0.private_key.
1.4 Create Server Configuration
cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <server_private_key>
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT
[Peer]
PublicKey = <router1_public_key>
AllowedIPs = 10.0.0.2/32, 192.168.1.0/24
EOF
Important: Replace eth0 with the actual public-facing interface name (ip route get 1.1.1.1 | awk '{print $5}').
1.5 Enable IP Forwarding
sysctl net.ipv4.ip_forward
sed -i '/^net.ipv4.ip_forward/d' /etc/sysctl.conf
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
sysctl -p
1.6 Firewall Configuration
Open the WireGuard listen port. Detect which firewall is active:
if systemctl is-active --quiet firewalld; then
firewall-cmd --permanent --add-port=51820/udp
firewall-cmd --permanent --add-masquerade
firewall-cmd --reload
elif command -v ufw &>/dev/null && ufw status | grep -q "active"; then
ufw allow 51820/udp
else
echo "No firewalld or ufw detected; relying on wg0.conf PostUp/PostDown iptables rules."
fi
1.7 Start and Enable
systemctl enable --now wg-quick@wg0
wg show wg0
Phase 2: Router WireGuard Client Configuration
Use apfree-wifidog tools to configure the router. Run these via the OpenClaw agent.
2.1 Configure WireGuard Tunnel
Prerequisite: Run apfree_wifidog_generate_wireguard_keys first (Phase 1.3). The private key is already stored in UCI.
Use apfree_wifidog_set_wireguard_vpn with:
- interface: tunnel address only (private key is already in UCI from key generation)
- peers: VPS public key + endpoint +
allowedIps: ["0.0.0.0/0"] + routeAllowedIps: false
Setting routeAllowedIps: false prevents netifd from auto-creating kernel routes, letting us manage routes explicitly.
Tool: apfree_wifidog_set_wireguard_vpn
Params:
deviceId: "<router_device_id>"
interface:
addresses: ["10.0.0.2/24"]
peers:
- publicKey: "<server_public_key>"
endpointHost: "<vps_public_ip>"
endpointPort: 51820
allowedIps: ["0.0.0.0/0"]
persistentKeepalive: 25
routeAllowedIps: false
Note: Do NOT pass privateKey here — it is already set by generate_wireguard_keys. The set_wireguard_vpn handler preserves existing UCI options that are not overwritten.
2.2 Verify Tunnel Status
Use apfree_wifidog_get_wireguard_vpn_status to confirm the tunnel is up and has a handshake.
Phase 3: Traffic Routing
3.1 Selective Routing (specific destinations through VPN)
Use apfree_wifidog_set_vpn_routes with mode: "selective":
Tool: apfree_wifidog_set_vpn_routes
Params:
deviceId: "<router_device_id>"
mode: "selective"
routes: ["1.2.3.0/24", "4.5.6.0/24"]
3.2 Full Tunnel (all traffic through VPN)
Use apfree_wifidog_set_vpn_routes with mode: "full_tunnel" and excludeIps containing the VPS public IP to prevent routing loop:
Tool: apfree_wifidog_set_vpn_routes
Params:
deviceId: "<router_device_id>"
mode: "full_tunnel"
excludeIps: ["<vps_public_ip>"]
3.3 Verify Routes
Use apfree_wifidog_get_vpn_routes to confirm routes are applied.
3.4 Remove Routes
Use apfree_wifidog_delete_vpn_routes with flushAll: true to clear all VPN routes.
Phase 3B: Domain-Based Routing
When a user wants to route traffic for specific domain names (e.g. "youtube.com", "netflix.com") through the VPN tunnel, the agent must resolve domains to IP addresses first because set_vpn_routes only accepts CIDR blocks.
Important: set_vpn_routes does NOT accept domain names. Domains must be resolved to IPs before calling the tool.
3B.1 Resolve Domains to IPs
Run DNS resolution on the VPS (or any machine with internet access) using shell commands:
dig +short youtube.com | grep -E '^[0-9]+\.' | sort -u
for domain in youtube.com netflix.com; do
dig +short "$domain" | grep -E '^[0-9]+\.'
done | sort -u
whois $(dig +short youtube.com | head -1) | grep -i 'cidr\|route:' | head -3
3B.2 Convert IPs to CIDR Routes
Use /32 suffix for each individual IP address to ensure precise routing through the tunnel.
-
Individual IPs: Use /32 suffix for each IP — most reliable for domain-based routing.
routes: ["142.250.80.46/32", "142.250.80.78/32"]
-
CIDR aggregation: Use the subnet that covers the IPs — fewer routes, slightly broader (use with caution).
whois 142.250.80.46 | grep -i cidr
routes: ["142.250.0.0/15"]
-
Known service CIDR blocks: For major services, use published IP ranges
curl -s https://www.gstatic.com/ipranges/goog.json | jq -r '.prefixes[].ipv4Prefix' | head -20
curl -s https://www.cloudflare.com/ips-v4
3B.3 Push Routes to Router
After resolving domains to IPs (using /32), call apfree_wifidog_set_vpn_routes:
Tool: apfree_wifidog_set_vpn_routes
Params:
deviceId: "<router_device_id>"
mode: "selective"
routes: ["142.250.80.46/32", "142.250.80.78/32"]
3B.4 Domain Routing Workflow Summary
The complete agent workflow when a user says "route youtube.com through VPN":
- Resolve: Run
dig +short youtube.com on VPS via shell → get IP list
- Aggregate: Use
whois or known ranges to find covering CIDRs
- Push: Call
apfree_wifidog_set_vpn_routes with the CIDRs
- Verify: Call
apfree_wifidog_get_vpn_routes to confirm
Caveats:
- DNS results change over time (CDN rotation). Routes may need periodic refresh.
- Large services (Google, AWS, Cloudflare) may have hundreds of CIDRs. Use aggregated ranges.
- Routes are kernel-only and lost on router reboot. Re-push after tunnel re-establishment.
Phase 4: Adding More Routers
For each additional router:
- Generate keys on the new router via
apfree_wifidog_generate_wireguard_keys — note the returned public key
- Add a
[Peer] section to VPS /etc/wireguard/wg0.conf with the new router's public key and a unique tunnel IP (10.0.0.3/32, 10.0.0.4/32, etc.)
- Reload VPS config:
wg syncconf wg0 <(wg-quick strip wg0)
- Configure the new router via
apfree_wifidog_set_wireguard_vpn (Phase 2) — omit privateKey (already set)
- Apply routes via
apfree_wifidog_set_vpn_routes (Phase 3)
Troubleshooting
| Symptom | Check | Fix |
|---|
| No handshake | wg show wg0 on VPS — peer should show latest handshake | Verify endpoint host/port, check VPS firewall (see Phase 1.6) |
| WireGuard module not loaded | modprobe wireguard fails | CentOS/RHEL: install kmod-wireguard from ELRepo; kernel >= 5.6 has it built-in |
| Tunnel up but no traffic | apfree_wifidog_get_vpn_routes — routes should exist | Re-apply routes with set_vpn_routes |
| Full tunnel breaks VPS connection | exclude_ips missing VPS IP | Add VPS public IP to excludeIps array |
| Routes lost after reboot | Routes are not persisted in UCI | Re-push via set_vpn_routes after tunnel re-establishment |
| DNS not resolving through VPN | Router using local DNS | Configure DNS to use tunnel: ip route add <dns_ip>/32 dev wg0 proto static |
Security Notes
- Private keys never leave the device. Use
apfree_wifidog_generate_wireguard_keys to generate keys on the router; only the public key is returned. The private key goes directly to UCI.
- Never expose private keys in logs or chat. Use placeholders when discussing.
- VPS firewall: open only the WireGuard listen port (default 51820/udp).
- Use
PresharedKey for post-quantum protection when required.
- Rotate keys by calling
apfree_wifidog_generate_wireguard_keys again and updating the VPS peer config with the new public key.