Bookmark

Build a WireGuard server on OpenWrt with UCI and verify the tunnel

A WireGuard server on OpenWrt has three boundaries I keep separate: server/client keys, the firewall rule for the UDP listen port, and the VPN interface's firewall zone. The examples preserve the source names and addresses; private keys, PSKs, and passwords must be generated locally and never stored in Markdown.1

Goals and assumptions

The source targets encrypted VPN access, remote LAN services, split/full tunnel operation, and client-side leak prevention. I assume a working WAN, a client that can generate keys, and a UDP port forward to VPN_PORT if the router is behind another modem. This is not a substitute for the WireGuard client/extras pages.

1. Preparation

Install the package and set the parameters. VPN_IF, VPN_PORT, the IPv4 /24, and the IPv6 ULA /64 are preserved from the source; if I change them, I change them consistently in firewall, network, and client configuration:1

1
2
3
4
5
6
7
8
9
# Install packages
opkg update
opkg install wireguard-tools

# Configuration parameters
VPN_IF="vpn"
VPN_PORT="51820"
VPN_ADDR="192.168.9.1/24"
VPN_ADDR6="fd00:9::1/64"

2. Key management

Generate the server and client key pairs plus a pre-shared key. Keep wgserver.key, wgserver.pub, wgclient.key, wgclient.pub, and wgclient.psk outside the repository with restrictive permissions; only the public key is exchanged with the other peer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Generate keys
umask go=
wg genkey | tee wgserver.key | wg pubkey > wgserver.pub
wg genkey | tee wgclient.key | wg pubkey > wgclient.pub
wg genpsk > wgclient.psk

# Server private key
VPN_KEY="$(cat wgserver.key)"

# Pre-shared key
VPN_PSK="$(cat wgclient.psk)"

# Client public key
VPN_PUB="$(cat wgclient.pub)"

umask go= is retained exactly from the source; I check whether the running shell/release accepts it before execution. I never paste a private key or PSK into logs, chat, or Git.

3. Firewall

The source adds the VPN interface to the LAN zone to reduce internal firewall setup and opens the UDP VPN_PORT from WAN:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Configure firewall
uci rename firewall.@zone[0]="lan"
uci rename firewall.@zone[1]="wan"
uci del_list firewall.lan.network="${VPN_IF}"
uci add_list firewall.lan.network="${VPN_IF}"
uci -q delete firewall.wg
uci set firewall.wg="rule"
uci set firewall.wg.name="Allow-WireGuard"
uci set firewall.wg.src="wan"
uci set firewall.wg.dest_port="${VPN_PORT}"
uci set firewall.wg.proto="udp"
uci set firewall.wg.target="ACCEPT"
uci commit firewall
service firewall restart

That is a policy choice, not a universal safe default. If a client only needs one VLAN or service, I use a dedicated zone and minimum forwarding rather than giving the entire VPN access to LAN.

4. Network and peer

Create the WireGuard interface with its private key, listen port, and addresses, then add the client peer with its public key, PSK, and client /32 + /128 allowed IPs:1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Configure network
uci -q delete network.${VPN_IF}
uci set network.${VPN_IF}="interface"
uci set network.${VPN_IF}.proto="wireguard"
uci set network.${VPN_IF}.private_key="${VPN_KEY}"
uci set network.${VPN_IF}.listen_port="${VPN_PORT}"
uci add_list network.${VPN_IF}.addresses="${VPN_ADDR}"
uci add_list network.${VPN_IF}.addresses="${VPN_ADDR6}"

# Add VPN peers
uci -q delete network.wgclient
uci set network.wgclient="wireguard_${VPN_IF}"
uci set network.wgclient.public_key="${VPN_PUB}"
uci set network.wgclient.preshared_key="${VPN_PSK}"
uci add_list network.wgclient.allowed_ips="${VPN_ADDR%.*}.2/32"
uci add_list network.wgclient.allowed_ips="${VPN_ADDR6%:*}:2/128"
uci commit network
service network restart

In this block, ${VPN_ADDR%.*}.2/32 and ${VPN_ADDR6%:*}:2/128 derive the client address from the server variables. I confirm the resulting values with uci show network before restarting.

Testing

Test both address families with traceroute:

1
2
traceroute openwrt.org
traceroute6 openwrt.org

After the client handshakes, inspect logs, the UDP listener, WireGuard runtime state, route/rule tables, nftables, and persistent UCI configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Restart services
service log restart; service network restart; sleep 10

# Log and status
logread -e vpn; netstat -l -n -p | grep -e "^udp\s.*\s-$"

# Runtime configuration
pgrep -f -a wg; wg show; wg showconf vpn
ip address show; ip route show table all
ip rule show; ip -6 rule show; nft list ruleset

# Persistent configuration
uci show network; uci show firewall; crontab -l

Troubleshooting

For a failed handshake, check the peer public key, PSK, endpoint/UDP port-forward, listen port, clock, allowed_ips, and firewall. If the handshake exists but LAN traffic fails, inspect zone forwarding, 192.168.9.0/24, IPv6 routes, and nft list ruleset. If full-tunnel policy removes access to the endpoint, create an endpoint exception before enforcing a default route.

Safety and rollback

Save uci show network and uci show firewall before editing. For rollback, remove only the network.vpn, network.wgclient, and firewall rule created by this article, commit, and restart; do not delete the WAN/LAN zones by mistake. A leaked private key requires new keys and an update on every peer.

Sources


  1. https://openwrt.org/docs/guide-user/services/vpn/wireguard/server — OpenWrt Wiki – WireGuard server. Independently adapted from the 2023-11-05 snapshot; keys and PSKs are generated locally during deployment. ↩︎ ↩︎ ↩︎


0 Bình luận

Góp Ý / Bình Luận / Đánh giá