Tip

This is the documentation for the 23.02 version. Looking for the documentation of the latest version? Have a look here.

WireGuard VPN for Remote Access

Current versions of TNSR support using WireGuard as a means of providing a remote access VPN for clients. This can also be referred to as a “mobile VPN” or a “road warrior VPN”.

Note

Though WireGuard is a true peer-to-peer VPN and has no internal concept of “client” or “server”, it can behave in ways similar to those roles, so for the sake of making this easy to follow, this document will still use those terms. See Design Style for details.

This is different from a site-to-site VPN in several ways, notably:

  • The remote peer endpoints are unknown and dynamic, because clients connect from any location with Internet access, such as mobile phones, hotels, coffee shops, libraries, and so on.

  • There are no networks behind the remote peers, the remote clients are all accessing resources on the server side or beyond (e.g. routed networks, Internet hosts)

Required Information

Determine Addresses

This TNSR instance uses addresses inside the larger subnet 10.31.0.0/16 which makes it easier for clients to route traffic to any network located behind the server. Thus, when a client wishes to only reach resources routed by the server, they can use that network instead of listing server networks individually.

A remote access WireGuard VPN requires a dedicated subnet used to communicate between the peers inside the VPN. In this case, the server will use 10.31.111.0/24.

Addresses inside this subnet must be allocated manually to peers as WireGuard does not have a mechanism by which it can allocate these addresses automatically.

In this example, the server will use the first address in the subnet, 10.31.111.1. As peers are added, they will receive the next available address in the subnet.

Tip

Use IP address management software or a spreadsheet to track allocated addresses.

Generate Keys

Before starting, generate a set of keys for the WireGuard server at a shell prompt:

$ wg genkey | tee ra.prv.key | wg pubkey > ra.pub.key
$ cat ra.prv.key
iK24AJ251et8cBe3+QsmE6BV0X8jRcf8Wzy5ukkEXFY=
$ cat ra.pub.key
LQZRVWTPMSNfB+T7OjgLmWwK8nE4kVw+Xme0gwinFXQ=

Each client should generate their own keys and give the public key to the TNSR administrator configuring the VPN. The server does not need to know the client private keys, only the public keys.

Settings Summary

The table Example WireGuard Remote Access Configuration contains the required information and other settings which form the WireGuard Remote Access VPN for this example.

Example WireGuard Remote Access Configuration

Item

Value

TNSR WG Private Key

iK24AJ251et8cBe3+QsmE6BV0X8jRcf8Wzy5ukkEXFY=

TNSR WG Public Key

LQZRVWTPMSNfB+T7OjgLmWwK8nE4kVw+Xme0gwinFXQ=

TNSR Local Network

10.31.0.0/16

TNSR Server Address

203.0.113.31

TNSR Local WG Port

51820

TNSR WG Interface

10.31.111.1/24

Alice WG Interface

10.31.111.2/32

Alice WG Public Key

WTayq0ZP4pWUV4eAk3FnRAPskw+Qcvf86S3mCiH0rAA=

Bob WG Interface

10.31.111.3/32

Bob WG Public Key

FzyY/KFTL1FSz8jJJ7fyscIvGchqxSU5izxWWgD/SmA=

For each peer, the remote access VPN server only requires its public key and an interface address the client will use to access the VPN.

Configure WireGuard Instance

The next step is to start configuring the WireGuard instance, filling in the values from the table above:

tnsr(config)# interface wireguard 1
tnsr(config-wireguard)# description WireGuard Remote Access VPN
tnsr(config-wireguard)# source-address 203.0.113.31
tnsr(config-wireguard)# port 51820
tnsr(config-wireguard)# private-key base64 iK24AJ251et8cBe3+QsmE6BV0X8jRcf8Wzy5ukkEXFY=

Do not exit at the end, continue on to setup the peers.

Configure WireGuard Peers

Now enter the configuration for the two peers using the values from the table. Set the peer allowed-prefix to the single address in the client subnet allocated to this client. Ensure this value uses a /32 prefix length.

tnsr(config-wireguard)# peer 1
tnsr(config-wireguard-peer)# description Alice
tnsr(config-wireguard-peer)# allowed-prefix 10.31.111.2/32
tnsr(config-wireguard-peer)# public-key base64 WTayq0ZP4pWUV4eAk3FnRAPskw+Qcvf86S3mCiH0rAA=
tnsr(config-wireguard-peer)# exit
tnsr(config-wireguard)# peer 2
tnsr(config-wireguard-peer)# description Bob
tnsr(config-wireguard-peer)# allowed-prefix 10.31.111.3/32
tnsr(config-wireguard-peer)# public-key base64 FzyY/KFTL1FSz8jJJ7fyscIvGchqxSU5izxWWgD/SmA=
tnsr(config-wireguard-peer)# exit

Repeat as needed for more peers, then exit the WireGuard instance configuration:

tnsr(config-wireguard)# exit

Configure WireGuard Interface

Next is the wg1 interface which the server will use to communicate with peers inside the tunnel. Use the chosen server address with a prefix length reflective of the entire subnet here, in this case /24.

tnsr(config)# interface wg1
tnsr(config-interface)# enable
tnsr(config-interface)# description WireGuard Remote Access VPN
tnsr(config-interface)# ip address 10.31.111.1/24
tnsr(config-interface)# exit
tnsr(config)#

Configure ACLs

If the external-facing interface from which the WireGuard clients will connect has an input ACL limiting inbound traffic, then it must be adjusted to allow the incoming WireGuard clients.

Note

If the external interface does not have an input ACL, this can be skipped.

For a brief look at a basic ACL configuration, see the ACL section of the ZTP guide.

The ACL must allow traffic to the IP address and port configured on the WireGuard instance. Since remote access client addresses are unknown, the ACL rule must allow WireGuard traffic from any source.

tnsr(config)# acl internet-in
tnsr(config-acl)# rule 50
tnsr(config-acl-rule)# description Allow WireGuard
tnsr(config-acl-rule)# action permit
tnsr(config-acl-rule)# ip-version ipv4
tnsr(config-acl-rule)# source address 0.0.0.0/0
tnsr(config-acl-rule)# destination address 203.0.113.31/32
tnsr(config-acl-rule)# destination port 51820 51820
tnsr(config-acl-rule)# protocol udp
tnsr(config-acl-rule)# exit
tnsr(config-acl)# exit
tnsr(config)#

If the same interface has a restrictive output ACL it may need a similar rule there but with the source and destination swapped.

Additionally, if there are ACLs on other internal interfaces, these may also require adjustment.

Tip

To restrict traffic to or from WireGuard clients, output and input ACLs can be added to the wgX interface.

Configure Peer Routes

Each peer must have an entry in the routing table informing TNSR that the individual address is reachable through the wg1 interface.

Warning

This cannot be summarized by using a larger prefix route, each client address must be listed separately.

The routes in this case are added to the default route table:

tnsr(config)# route table ipv4-VRF:0

This example adds a separate route for each of the peers, Alice and Bob:

tnsr(config-route-table)# route 10.31.111.2/32
tnsr(config-rttbl4-next-hop)# description WG RA Peer Route for Alice
tnsr(config-rttbl4-next-hop)# next-hop 0 via 0.0.0.0 wg1
tnsr(config-rttbl4-next-hop)# exit
tnsr(config-route-table)# route 10.31.111.3/32
tnsr(config-rttbl4-next-hop)# description WG RA Peer Route for Bob
tnsr(config-rttbl4-next-hop)# next-hop 0 via 0.0.0.0 wg1
tnsr(config-rttbl4-next-hop)# exit

Repeat for each additional peer, then exit the route table configuration:

tnsr(config-route-table)# exit

That completes the server-side configuration on TNSR.

Configure Clients

There are WireGuard clients for a variety of platforms. So many that it is difficult to list and describe them all and how they operate. There are clients for major desktop and mobile operating system and other less common devices as well. Some WireGuard clients have a graphical interface (GUI) and others use configuration files. Thankfully, in most cases these clients use identical, or at least similar, terminology.

This is the general form of a basic WireGuard client configuration:

[Interface]
PrivateKey = < private to the client >
Address = < allocated VPN interface address >
[Peer]
PublicKey = < server public key >
Endpoint = < server address>:<server port >
AllowedIPs = < server-side network(s) >

For clients with a GUI, it should be relatively intuitive to map those fields to similar fields in the GUI. Often clients have a means to import configuration files as well, which means a crafted configuration could be imported to bypass any differences in the GUI.

This example configuration is for the client peer Alice:

[Interface]
PrivateKey = < not shown >
Address = 10.31.111.2/24
[Peer]
PublicKey = LQZRVWTPMSNfB+T7OjgLmWwK8nE4kVw+Xme0gwinFXQ=
Endpoint = 203.0.113.31:51820
AllowedIPs = 10.31.0.0/16

This example configuration is for the client peer Bob:

[Interface]
PrivateKey = < not shown >
Address = 10.31.111.3/24
[Peer]
PublicKey = LQZRVWTPMSNfB+T7OjgLmWwK8nE4kVw+Xme0gwinFXQ=
Endpoint = 203.0.113.31:51820
AllowedIPs = 10.31.0.0/16

Note that in both cases the server side configuration in the [Peer] section is identical. Only the private key and client address are different.

With a client configuration in place, a client should be able to activate the VPN and transmit traffic to hosts behind the server.

Allowing Internet Access

Another common use case for remote access VPNs is for the server to act as an Internet gateway for clients. This can be leveraged for private browsing across an untrusted local network, geographic relocation of traffic, or other uses where such traffic is centrally routed.

The configuration thus far is nearly complete to allow this except for a few simple items.

NAT

For clients on the WireGuard VPN to reach the Internet, TNSR must apply NAT to their traffic to mask the true source address. This example assumes NAT is already configured on TNSR for other local hosts.

See also

For a brief look at a basic NAT configuration, see the NAT section of the ZTP guide.

To perform NAT on client traffic originating from the wg1 interface, set it as an inside NAT interface:

tnsr(config)# interface wg1
tnsr(config-interface)# ip nat inside
tnsr(config-interface)# exit

Allowed IP Address Change

To make the client route all of its traffic through the WireGuard VPN, the client configuration must be changed to reflect this behavior.

Note that in the previous examples the AllowedIPs entry in the [Peer] section were set to the subnet reachable through the VPN:

[Peer]
AllowedIPs = 10.31.0.0/16

To route all traffic through, set this value to 0.0.0.0/0 instead:

[Peer]
AllowedIPs = 0.0.0.0/0

Deactivate and activate the VPN client so it will use the new settings.

DNS

Part of private and/or secure browsing is controlling the DNS servers used by clients to resolve hostnames to IP addresses.

If clients send their DNS requests to a third party public DNS server on the Internet, such as CloudFlare or Google, this configuration may not be necessary.

For VPN clients to use the Unbound DNS resolver on TNSR for DNS, first allow the VPN client subnet to access the resolver:

tnsr(config)# unbound server
tnsr(config-unbound)# access-control 10.31.111.0/24 allow
tnsr(config-unbound)# exit

Next, set the VPN client to use an address on TNSR for DNS. The address to use varies based on how Unbound was configured, but is likely the LAN interface address, such as 10.31.0.1 in this example.

See also

For a brief look at a basic Unbound configuration, see the DNS section of the ZTP guide.

Now add the DNS server to the [Interface] section of the client configuration:

[Interface]
DNS = 10.31.0.1

Deactivate and activate the VPN client so it will use the new settings.