Page 1 of 1

Wireguard on Openwrt - an obscurely specific HowTo

Posted: Wed Feb 04, 2026 1:37 am
by spot
WireGuard VPN Tunnel: OpenWrt Router to Debian Trixie Server

Technical reference for establishing a full-tunnel VPN exit using an OpenWrt router as client and a Debian Trixie VPS as server.

Overview

• Router: OpenWrt (tested on 25.12.0-rc4, ramips/mt7621)
• Server: Debian 13 (Trixie) VPS
• Tunnel network: 10.0.100.0/24
• Router tunnel IP: 10.0.100.2/32
• Server tunnel IP: 10.0.100.1/24
• WireGuard port: 51820/udp

Server Setup (Trixie)

Install WireGuard

Code: Select all

sudo apt update && sudo apt install wireguard
Enable IP Forwarding

Code: Select all

echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.conf
sudo /sbin/sysctl -p
Verify:

Code: Select all

cat /proc/sys/net/ipv4/ip_forward
# Should return: 1
Generate Server Keys

Code: Select all

wg genkey | tee /tmp/server_private | wg pubkey > /tmp/server_public
cat /tmp/server_private  # <SERVER_PRIVATE_KEY> - keep secret
cat /tmp/server_public   # <SERVER_PUBLIC_KEY> - give to router config
Generate Router Keys

On the router (or any machine with wg installed):

Code: Select all

wg genkey | tee /tmp/router_private | wg pubkey > /tmp/router_public
cat /tmp/router_private  # <ROUTER_PRIVATE_KEY> - keep secret
cat /tmp/router_public   # <ROUTER_PUBLIC_KEY> - give to server config
You now have four values:
• <SERVER_PRIVATE_KEY> - used only in server's wg0.conf
• <SERVER_PUBLIC_KEY> - used in router's peer config
• <ROUTER_PRIVATE_KEY> - used only in router's interface config
• <ROUTER_PUBLIC_KEY> - used in server's peer config

Create Server Configuration

Replace <SERVER_PRIVATE_KEY> and <ROUTER_PUBLIC_KEY> with actual values:

Code: Select all

sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.0.100.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
PostUp = iptables -t nat -A POSTROUTING -s 10.0.100.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.0.100.0/24 -o eth0 -j MASQUERADE

[Peer]
PublicKey = <ROUTER_PUBLIC_KEY>
AllowedIPs = 10.0.100.2/32
EOF
Note: Adjust -o eth0 if the server's public interface has a different name.

Adding Multiple Peers

Each device connecting to the server needs its own keypair and tunnel IP. Generate keys on each device as above, then add a [Peer] block for each in the server's wg0.conf:

Code: Select all

[Peer]
# Second router or device
PublicKey = <SECOND_DEVICE_PUBLIC_KEY>
AllowedIPs = 10.0.100.3/32

[Peer]
# Phone, laptop, etc.
PublicKey = <THIRD_DEVICE_PUBLIC_KEY>
AllowedIPs = 10.0.100.4/32
Assign each device a unique IP within the 10.0.100.0/24 range. All peers can be active simultaneously without conflict.

To add a peer without restarting the tunnel (preserving existing connections):

Code: Select all

sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'
Verify all peers are registered:

Code: Select all

sudo wg show
Configure UFW Firewall

Code: Select all

sudo ufw allow 51820/udp
sudo ufw route allow in on wg0 out on eth0
sudo ufw route allow in on eth0 out on wg0
Note: If using nftables or iptables directly instead of UFW, the equivalent rules are:

Code: Select all

# Allow WireGuard port
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
# Allow forwarding between wg0 and eth0
iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT
Start WireGuard

Code: Select all

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg show
Router Setup (OpenWrt)

Configure WireGuard Interface via UCI

Replace placeholders with actual values:

Code: Select all

# Create interface
uci set network.wgserver=interface
uci set network.wgserver.proto='wireguard'
uci set network.wgserver.private_key='<ROUTER_PRIVATE_KEY>'
uci set network.wgserver.addresses='10.0.100.2/32'

# Add peer (the server)
uci add network wireguard_wgserver
uci set network.@wireguard_wgserver[-1].public_key='<SERVER_PUBLIC_KEY>'
uci set network.@wireguard_wgserver[-1].endpoint='<SERVER_IP>:51820'
uci set network.@wireguard_wgserver[-1].allowed_ips='0.0.0.0/0'
uci set network.@wireguard_wgserver[-1].persistent_keepalive='25'
uci commit network
Configure Firewall

Add the WireGuard interface to the wan zone for masquerading and forwarding:

Code: Select all

uci add_list firewall.@zone[1].network='wgserver'
uci commit firewall
/etc/init.d/firewall restart
Bring Up Interface

Code: Select all

ifdown wgserver && ifup wgserver
wg show wgserver
VPN Toggle Script

Create /root/vpn-toggle.sh:

Code: Select all

#!/bin/sh

WG_INTERFACE="wgserver"
WG_ENDPOINT="<SERVER_IP>"
WG_PUBKEY="<SERVER_PUBLIC_KEY>"
WAN_GATEWAY="192.168.1.254"  # Adjust to your upstream gateway

case "$1" in
  on)
    # Ensure endpoint route exists (critical!)
    ip route add $WG_ENDPOINT via $WAN_GATEWAY dev wan 2>/dev/null
    # Set endpoint if missing (workaround for netifd bug)
    wg set $WG_INTERFACE peer $WG_PUBKEY endpoint $WG_ENDPOINT:51820 2>/dev/null
    ip route replace default dev $WG_INTERFACE
    logger -t vpn-toggle "VPN enabled"
    ;;
  off)
    ip route replace default via $WAN_GATEWAY dev wan
    logger -t vpn-toggle "VPN disabled"
    ;;
  status)
    if ip route | grep -q "default dev $WG_INTERFACE"; then
      echo "VPN: active"
      curl -s ipinfo.io | grep -E '"(ip|city|country)"'
    else
      echo "VPN: inactive"
    fi
    ;;
  *)
    echo "Usage: $0 {on|off|status}"
    ;;
esac

Code: Select all

chmod +x /root/vpn-toggle.sh
Critical Notes

Endpoint Route

When replacing the default route to go through the WireGuard tunnel, the router must still be able to reach the VPN server's public IP via the normal WAN gateway. Without this, encapsulated packets have nowhere to go:

Code: Select all

ip route add <SERVER_IP> via <WAN_GATEWAY> dev wan
The toggle script handles this automatically.

Testing

From router:

Code: Select all

./vpn-toggle.sh on
curl -s ipinfo.io          # Should show server location
./vpn-toggle.sh off
From LAN client:

Code: Select all

curl -s ipinfo.io          # Should match router's exit
Verify Tunnel Health

Code: Select all

wg show wgserver
Look for:
• endpoint: showing server IP and port
• latest handshake: within last 2 minutes
• transfer: showing bytes in both directions

Troubleshooting

No handshake
• Check server firewall allows 51820/udp
• Check endpoint is set: wg show wgserver
• Manually set endpoint if missing (see bug below)

Handshake but no traffic
• Check server IP forwarding: cat /proc/sys/net/ipv4/ip_forward
• Check server NAT rule: sudo iptables -t nat -L POSTROUTING -v
• Check UFW forwarding rules

Traffic sent but nothing received
• Missing endpoint route - packets can't reach server
• Server not masquerading - check iptables rule

Preserve Across Sysupgrade

Add to /etc/sysupgrade.conf:

Code: Select all

/root/