WrapGuard supports policy-based routing, allowing you to route traffic through specific WireGuard peers based on destination IP addresses, protocols, and port ranges.
In addition to the standard WireGuard configuration, you can add Route directives to each peer:
[Peer]
PublicKey = ...
Endpoint = ...
AllowedIPs = ...
# Route directives for policy-based routing
Route = <CIDR>
Route = <CIDR>:<protocol>:<ports><CIDR>: Destination network in CIDR notation (e.g.,192.168.1.0/24,0.0.0.0/0)<protocol>:tcp,udp, orany(optional, defaults toany)<ports>: Port or port range (optional, defaults to all ports)- Single port:
80 - Port range:
8080-9000 - Multiple ports:
80,443(comma-separated)
- Single port:
[Peer]
PublicKey = peer1_public_key
Endpoint = vpn1.example.com:51820
AllowedIPs = 0.0.0.0/0
# Route all traffic through this peer by default
Route = 0.0.0.0/0
[Peer]
PublicKey = peer2_public_key
Endpoint = vpn2.example.com:51820
AllowedIPs = 192.168.0.0/16
# Route specific subnet through this peer
Route = 192.168.1.0/24[Peer]
PublicKey = web_peer_public_key
Endpoint = web-vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
# Route web traffic through this peer
Route = 0.0.0.0/0:tcp:80,443
[Peer]
PublicKey = dev_peer_public_key
Endpoint = dev-vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
# Route development services through this peer
Route = 0.0.0.0/0:tcp:3000-4000
Route = 0.0.0.0/0:tcp:8080-9000[Interface]
PrivateKey = your_private_key
Address = 10.150.0.2/24
# Peer 1: General purpose VPN
[Peer]
PublicKey = general_vpn_public_key
Endpoint = general-vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
Route = 0.0.0.0/0 # Default route for all traffic
# Peer 2: Corporate network access
[Peer]
PublicKey = corp_vpn_public_key
Endpoint = corp-vpn.example.com:51820
AllowedIPs = 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
# Route corporate networks
Route = 10.0.0.0/8
Route = 172.16.0.0/12
Route = 192.168.0.0/16
# Route specific services
Route = 0.0.0.0/0:tcp:22 # SSH through corporate VPN
Route = 0.0.0.0/0:tcp:3389 # RDP through corporate VPN
# Peer 3: Streaming and gaming
[Peer]
PublicKey = gaming_vpn_public_key
Endpoint = gaming-vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
# Route gaming and streaming ports
Route = 0.0.0.0/0:udp:5000-6000 # Gaming ports
Route = 0.0.0.0/0:tcp:1935 # RTMP streaming- Most specific CIDR wins:
/32routes take precedence over/24, which take precedence over/0 - Order matters: For same CIDR specificity, routes listed first have higher priority
- Protocol matching: Protocol-specific routes only match their protocol
- Port matching: Port-specific routes only match connections to those ports
- When a connection is initiated, WrapGuard checks the destination IP, protocol, and port
- It searches through all configured routing policies to find the best match
- Traffic is routed through the WireGuard peer with the matching policy
- If no policy matches, it falls back to checking AllowedIPs
- If still no match, the default peer (first one with
0.0.0.0/0) is used
To test your routing configuration:
# Check which peer would handle specific traffic
wrapguard --config=policy-routing.conf -- curl https://example.com
wrapguard --config=policy-routing.conf -- ssh user@192.168.1.100
wrapguard --config=policy-routing.conf -- nc -v 10.0.0.5 3000Enable debug logging to see routing decisions:
wrapguard --config=policy-routing.conf --log-level=debug -- your_command- Currently only supports IPv4 routing
- Maximum of one route per line (no comma-separated CIDRs)
- Port ranges in route specifications don't support comma-separated values