Setting up WireGuard on OpenWrt


Updated: 2020-01-05

WireGuard is a modern VPN solution that has been getting a lot of attention recently, even earning accolades from Linus Torvalds himself. The developer's website states the following about WireGuard:

[...] an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances.

WireGuard is available for Windows, all major Linux distributions, MacOS, Android and iOS.

Use case

So when I needed to implement a VPN solution, WireGuard was the first thing I looked into, having watched the horror OpenVPN configuration can be from the sidelines. My use case was a simple and common one: a remote, mobile client that needed to maintain access to the LAN it is part of most of the time.

Required packages

Install the following. The qrencode and the LuCI packages are optional; however, together they will allow you to generate a QR code you can scan from your peer to transfer the public key and some other settings. If your flash is limited, consider using the image builder to build an image with the necessary packages included.

# opkg update
# opkg install wireguard luci-app-wireguard qrencode

Configuration

For reference, the setup below was performed on a pre-release OpenWrt 19.07 build with WireGuard 20190601, but still functional on WireGuard 20191205. WireGuard is still under development, so there might be changes that break compatibility, but I haven't found them yet - granted, my use case is a simple one.

Key generation

Before you can start setting up your WireGuard connection, you need to generate a keypair. If you just booted up your router, you might not have enough entropy to generate them, which means it can take a while. In that case, just generate the key pair on your computer and copy them over.

On your OpenWrt device, enter the following. This will create an /etc/wireguard/ directory, set the correct permissions for the files to be created, and finally generate both your public and private key in one go.

# mkdir /etc/wireguard/ && cd /etc/wireguard/
# umask 077 
# wg genkey | tee private.key | wg pubkey > public.key

You can also add a pre-shared key (PSK) for additional security, but this is completely optional:

# wg genpsk > preshared.key

Creating the local WireGuard 'server' interface

WireGuard interfaces are named wgX, where X is a number starting from zero. Best practices are the following:

  • Although you might want to just add your WireGuard interface to your LAN, it's better to assign it to a separate subnet, since using your LAN subnet might cause issues.
  • You also need to define the subnet available to the remote peers; this can be a traditional /24, but it goes without saying you can narrow or widen the pool of available addresses to your liking (e.g. /28 or /16). Your remote peers can then pick an address from this pool.
  • As for the listening port, UDP port 500 (IPsec) should be a safe fallback if your WireGuard peers are on locked down networks often.

The following commands will add the WireGuard 'server' interface to your OpenWrt setup, including your private key. Note you can specify multiple address ranges (hence the uci add_list).

# uci set network.wg0="interface"
# uci set network.wg0.proto="wireguard"
# uci set network.wg0.private_key="$(cat /etc/wireguard/private.key)"
# uci set network.wg0.listen_port="8192"
# uci add_list network.wg0.addresses='10.0.10.0/24'
# uci commit network

Adding the remote WireGuard peer on OpenWrt

We'll be adding an Android peer here. Generate a key pair in the Android WireGuard app and copy your public key to the OpenWrt device (if you want to store it, store it to e.g. /tmp/remote_peer_pubkey). UCI's numbering scheme will automatically increment peer instances if you use [-1], so that's what we're using here. Things to keep in mind:

  • allowed_ips in the peer configuration section are to be specified with a /32 mask if they're a single peer; this is the IP your remote peer will use to connect to the Wireguard 'server'. As such, the address(es) specified should be unique and in the same range as the one(s) you have defined in network.wg0.addresses in the 'server' stanza we configured earlier.
  • The description is optional, but it sure helps to keep track of which peer you're looking at.

To add your first peer to /etc/config/network, issue the following commands:

# uci add network wireguard_wg0
# uci set network.@wireguard_wg0[-1]=wireguard_wg0
# uci set network.@wireguard_wg0[-1].public_key="$(cat /tmp/remote_peer_pubkey)"
# uci set network.@wireguard_wg0[-1].persistent_keepalive='25'
# uci add_list network.@wireguard_wg0[-1].allowed_ips='10.0.10.250/32'
# uci set network.@wireguard_wg0[-1].description='Android 10 smartphone'
# uci commit network

For subsequent peers, keep the [-1] numbering; UCI will take care of it.

Once this is done, an /etc/init.d/network reload should be sufficient. If not, make it a 'restart' instead. Keep in mind, to reload the OpenWrt WireGuard interface, you need to bring down the wg0 (or equivalent) interface and bring it up again:

# ifdown wg0 && ifup wg0

To show your configuration (and see if any of your peers are connecting, last handshake, etc.) use the following command:

# wg showconf wg0

Configuring your firewall

If you would like fine-grained control over outgoing WireGuard traffic, you can create a separate zone; in a lot of cases though, you can just add the WireGuard interface to the existing LAN zone.

Add the firewall zone.

# uci add firewall zone
# uci set firewall.@zone[-1].name='wg'
# uci set firewall.@zone[-1].input='ACCEPT'
# uci set firewall.@zone[-1].forward='ACCEPT'
# uci set firewall.@zone[-1].output='ACCEPT'
# uci set firewall.@zone[-1].network='wg0'
# uci set firewall.@zone[-1].masq='1'

Add the forwarding rule so the WireGuard peers can communicate with the LAN. Keep in mind, since you are using a different subnet from your LAN, you'll still need to adjust any firewalls on your LAN clients to accept connections from your WireGuard peers, in case you have any LAN clients set to just accepting traffic from the LAN itself (which in my case is 10.0.0.0/24).

# uci add firewall forwarding
# uci set firewall.@forwarding[-1].src='wg'
# uci set firewall.@forwarding[-1].dest='lan'
# uci add firewall forwarding
# uci set firewall.@forwarding[-1].src='lan'
# uci set firewall.@forwarding[-1].dest='wg'

Open up the WireGuard port, save and restart the firewall:

# uci add firewall rule
# uci set firewall.@rule[-1].src="*"
# uci set firewall.@rule[-1].target="ACCEPT"
# uci set firewall.@rule[-1].proto="udp"
# uci set firewall.@rule[-1].dest_port="8192"
# uci set firewall.@rule[-1].name="Allow-Wireguard-Inbound"
# uci commit firewall
# /etc/init.d/firewall restart

Keeping your keys between OpenWrt upgrades

OpenWrt will keep your WireGuard configuration (since it's part of /etc/config/network) by default, but /etc/wireguard/ contents are not part of a default OpenWrt configuration; in order to preserve the keys, add them to /etc/sysupgrade.conf:

# ls /etc/wireguard/*key >> /etc/sysupgrade.conf

Configuring the remote WireGuard peer

Last but not least we need to set up the peer itself. Your configuration should look like this. The Address value is the IP your peer will use to communicate with the WireGuard instance on OpenWrt.

[Interface]
Address = 10.0.10.250/32
PrivateKey = $private_key_peer

[Peer]
AllowedIPs = 10.0.10.0/24,10.0.0.0/24
Endpoint = $your_WAN_IP_or_(dynamic)_DNS_hostname_here:8192
PersistentKeepalive = 25
PublicKey = $public_key_openwrt_wireguard_instance
  • Add all the allowed (and used) ranges to the AllowedIPs field; if you have e.g. a 10.0.0.0/24 subnet for your LAN, and are using a 10.0.10.0/24 subnet for your WireGuard peers, make sure to add both to the list (comma separated).
  • Leaving the DNS server blank (or entering my LAN's DNS server) breaks routing for me, but the latter might just be because the DNS server is only allowed to resolve queries from 10.0.0.x addresses. Entering e.g. one of Quad9's DNS servers worked.