Setting up WireGuard IPv6

I’ve been having a quick play with setting up another VPN solution for getting an IPv6 address on my mobile devices this time using WireGuard.

WireGuard is a relatively new VPN tunnel implementation that has been written to be as stripped back as possible to keep the codebase as small as possible to help make it easier to audit.

Setup

A lot of the instructions for running WireGuard on RaspberryPi OS talk about adding debian testing repos or building the code from scratch, but it looks like recent updates have included the packages needed in the core repositories.

# apt-get install wireguard

I set up UDP port forwarding on my router for port 53145 and got my ISP to route another /64 IPv6 subnet to my line, both of these are forwarded on to the Raspberry Pi that is running that is also running my OpenVPN setup. This is useful as it’s already setup to do NAT for the 10.8.0.0/24 range I’m issuing to OpenVPN clients so having it do itfor the 10.9.0.0/24 range for WireGuard is easy enough.

WireGuard on Linux is implemented as a network device driver so can be configured on the command line with the ip command e.g.

# ip link add dev wg0 type wireguard
# ip address add dev wg0 10.9.0.1/24

Which brings the device up and sets the IP addresses but you still need to add the Private Key and remote address and Public Key which can be done with the wg command

# wg set wg0 listen-port 53145 private-key /path/to/private-key peer ABCDEF... allowed-ips 0.0.0.0/0 endpoint 209.202.254.14:8172

Or more easily it can read from a config file

# wg setconf wg0 myconfig.conf

Or the whole setup and configured with wg-quick

# wg-quick up /path/to/wg.conf

Server Config

[Interface]
Address = 10.9.0.1/24, 2001:8b0:2c1:xxx::1/64
ListenPort = 53145
PrivateKey = oP3TAHBctNVcnPTxxxxxxxxzNRLSF5CwII4s8gVAXg=

#nexus
[Peer]
PublicKey = 4XcNbctkGy0s73Dvxxxxxxxxx++rs5BAzCGjYmq21UM=
AllowedIPs = 10.9.0.2/32, 2001:8b0:2c1:xxx::2/128

The Server config includes:

  • Address is the local address on the VPN tunnel, here has both IPv4 and IPv6.
  • ListenPort is which port to listen for client connections on. WireGuard doesn’t have a assigned port.
  • PrivateKey to identify the host.
  • There can be multiple Peers which represent which clients can connect and the AllowedIPs is the IP addresses for each client.

Client Config

[Interface]
Address = 10.9.0.2/32, 2001:8b0:2c1:xxx::2/128
PrivateKey = UFIJGgtKsor6xxxxxxxxxxxbWeKmw+Bb5ODpyNblEA=
DNS = 8.8.8.8

[Peer]
PublicKey = jMB2oMu+YTKigGxxxxxxxxxxSYcTde/7HT+QlQoZFm0=
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = hardill.me.uk:53145

The differences from the Server config are:

  • Interface has a DNS entry for the client to use while the tunnel is running.
  • Peer has an Endpoint which is the public address and port to connect to
  • AllowedIPs are which IPs to route over the tunnel, in this case it’s everything

Key Generation

Both ends of the connection need a PublicKey and a PrivateKey so they can mutually authenticate each other. These are generated with the wg command

# wg keygen > privateKey
# wg pubkey < privateKey > publicKey

Sharing Config

The WireGuard Android app that you can manually add all the details in the config file or it supports reading config files from QR codes. This makes it really easy to setup and removes the chance of getting a typo in the Keys and IP addresses.

You can generate QR codes from the config file as follows:

# qrencode -t png -o nexus.png < nexus.conf
# qrencode -t ansiutf8 < nexus.conf

The first generates a PNG file with the QR code, the second prints the code out as ASCII art.

Conclusion

It all looks to be working smoothly. I can see the advantages over OpenVPN being that you don’t need to worry about certificate maintenance and distribution.

I’ll give it a proper work out and see how it holds up running things like SIP connections along with general access to my home network.

As well as running it on the phone, I’ll set up a client config for my laptop to use when out and about. The only issues is that the Gnome Network Manager integration for WireGuard isn’t available in the standard repos for Ubuntu 20.04 so it needs to be started/stopped from the command line.

2 thoughts on “Setting up WireGuard IPv6”

  1. I was trying to do the same but found it to be quite impossible – flicking through guides online, tweaking Wireguard config files… At one point I was able to ping6 the IPv6 address of google.com from a client, but couldn’t resolve DNS via IPv6 (using an IPv6 DNS server).

    Please could you post what your output of:
    $ ip a
    Looks like on the server?

    I was trying to do the IPv6 tunnel with Linode, using a SLAAC address and a /64 block from them. I noticed you aren’t using a local IPv6 address in your Wireguard configs, which is the same as what I was trying to do – so that the client has a fully public IPv6 address. But it was so confusing as it is possible to assign the /64 block to either wg0, or eth0, or assign nothing to wg0, etc. Then some guides say use ‘ip neigh …proxy’ and others say ‘ip6tables -A FORWARD’ etc…

    1. Probably worth pointing out I have a /48 so `ip a` would look something like:


      ...
      2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000
      link/ether dc:a6:32:14:a1:b2 brd ff:ff:ff:ff:ff:ff
      inet 192.168.1.x/24 brd 192.168.1.255 scope global dynamic noprefixroute eth0
      valid_lft 62603sec preferred_lft 51803sec
      inet6 2001:8b0:2c1:xxx1:77c4:c395:19c0:242/64 scope global dynamic mngtmpaddr noprefixroute
      valid_lft 2591972sec preferred_lft 604772sec
      inet6 fe80::56df:fe7:b07a:f893/64 scope link
      valid_lft forever preferred_lft forever
      ...
      5: wg0: mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
      link/none
      inet 10.9.0.1/27 scope global wg0
      valid_lft forever preferred_lft forever
      inet6 2001:8b0:2c1:xxx2::1/64 scope global
      valid_lft forever preferred_lft forever

      My router is set to forward 2001:8b0:2c1:xxx2::/64 to the device running WireGuard, which picks up it’s eth0 address via SLAAC. This means I’m not trying to share the /64 that the host is not trying to share the same /64 as it’s own back haul.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.