🌐 VI | EN

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

Nghia Phan
Nghia Phan
🌐 Bản tiếng Việt Technical Guide Views

A WireGuard server on OpenWrt has three boundaries: server/client keys, the firewall rule for the UDP listen port, and the VPN interface’s firewall zone. Generate private keys, PSKs, and passwords locally; never store them 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

# Install packagesopkg updateopkg install wireguard-tools# Configuration parametersVPN_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.

# Generate keysumask go=wg genkey | tee wgserver.key | wg pubkey > wgserver.pubwg genkey | tee wgclient.key | wg pubkey > wgclient.pubwg genpsk > wgclient.psk# Server private keyVPN_KEY="$(cat wgserver.key)"# Pre-shared keyVPN_PSK="$(cat wgclient.psk)"# Client public keyVPN_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.

{{< ads >}}

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:

# Configure firewalluci 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.wguci 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 firewallservice 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

# Configure networkuci -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 peersuci -q delete network.wgclientuci 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 networkservice 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:

traceroute openwrt.orgtraceroute6 openwrt.org

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

# Restart servicesservice log restart; service network restart; sleep 10# Log and statuslogread -e vpn; netstat -l -n -p | grep -e "^udp\s.*\s-$"# Runtime configurationpgrep -f -a wg; wg show; wg showconf vpnip address show; ip route show table allip rule show; ip -6 rule show; nft list ruleset# Persistent configurationuci 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

Footnotes

  1. https://openwrt.org/docs/guide-user/services/vpn/wireguard/server — OpenWrt Wiki – WireGuard server. 2 3

Comments & Discussion

Share your thoughts, ask questions and feedback

Markdown & QQ Emoji
Loading comments...