2022-02-20
md
WireGuard on Raspberry Pi OS (Bullseye)
<-WireGuard on Raspberry Pi OS (Buster)
<-Installing and Configuring WireGuard - All Posts

The newest version of the Raspberry Pi OS replaced iptables with nftables. Both packages are the product of the netfilter project and the replacement has been in the works for a long time; nftables has been available since version 3.13 of the Linux kernel. Nevertheless, the change seems to have caught some off-guard as a search in the Raspberry Pi Forums will quickly prove. In particular, my previous guides to installing a WireGuard VPN on the Raspberry Pi are no longer valid, because iptables commands were used to establish routing of the IP data packets transiting the VPN tunnel.

Please refresh this page whenever you come back to this topic. It was probably an error but the https://sigmdel.ca/michel/ha/wireguard/wireguard_02_en.html URL is reused for each new version of the guide. I was hoping that search engines would serve up the most recent version, but browsers may display old cached content which defeats my goal altogether.


Table of Contents

  1. An Overview
  2. Installing WireGuard
  3. Enabling Remote Access to the Local Network
    1. Static Host IP Address
    2. Public IP or Dynanic Host Name
    3. Port Forwarding
    4. Enabling IP Forwarding
  4. Configuring WireGuard
    1. Enabling and Configuring nftables
    2. Installing the faicker/Mihalko User Management Script
    3. Generating the Private and Public Server Keys
    4. Creating and Editing the Server Definition File
    5. Editing the Client Configuration Template
    6. Editing the Server Configuration Template
    7. Creating an Empty WireGuard Server Configuration File
    8. Managing the WireGuard Service
  5. Managing Users
    1. Adding Users
    2. Installing a WireGuard Client in an Android Device
    3. Installing a WireGuard Client in Windows 10
    4. Installing a WireGuard Client in Linux
    5. Removing Users
  6. Using the WireGuard VPN Clients
  7. Concluding Remarks

An Overview toc

Presumably, if you are reading this post on an obscure personal Web site, it is because you have run into difficulty when installing or more likely when configuring a WireGuard server or a client. That's quite understandable because there are numerous moving parts especially when it comes to servers. A VPN tunnel can be seen as the glue between two physically separated networks combining them into a single local area network (LAN) from the user’s point of view. A WireGuard VPN is really a peer-to-peer connection, but I am a one-person outfit without powers of ubiquity, so I use WireGuard in a server-client configuration. I suspect most readers of this post will do the same. Let me describe the two scenarios in which I use WireGuard to explain what I mean when talking about a WireGuard "server" and "client" (or "user").

There is a WireGuard "server" on a machine about 1,000 km away in Montréal which I use for remote backups. The "client" is my desktop Linux computer. When I want to push a commit to a repository on the remote machine, I start WireGuard on the desktop using the configuration file that creates the VPN tunnel with the server, I commit changes to the remote repository with version control software in exactly the same way as I do it when committing changes to repositories on the NAS on the home LAN. Once the task is finished, I shut down the WireGuard service on my desktop. I may do this several times in a day or it may be days, maybe weeks, between commits but as long as the WireGuard server is running on the remote machine and the latter is connected to the Internet a single command reestablishes the tunnel very quickly.

I have another WireGuard "server" on a Raspberry Pi which is also hosting my home automation system. This small computer is always on, so that it is always possible to create a VPN tunnel at any time. I rarely do that. Sometimes when I am in town and want to check my e-mails and while feeling particularly paranoid, I'll start the WireGuard Android client and create a tunnel with the WireGuard server on the Pi before recovering my mail. I could connect to the WireGuard server in Montréal and obtain the same comforting feeling of security although I will probably get a warning from Google Mail that someone else is accessing my e-mail account with my password.

As can be seen, configuring a WireGuard server is not quite the same as configuring a client. The server configuration specifies which clients can connect to it, but a server never initiates a tunnel itself so it does not need much information about its clients. The clients, on the other hand, never accept a remote connection but they must be able to create a VPN tunnel. Consequently they need to know where to find either of the two servers. There are other differences in the configurations.

Presumably, a VPN server is set up to provide secure remote access to the computer on which WireGuard is installed if not to the complete local area network to which the server is connected. For most of us that is complicated by the fact that the public IP address of our LAN is dynamically allocated by our Internet service provider who may assign a different IP address at any time. The solution is to obtain a host name that is associated with the public IP address of the LAN and to make sure that the domain name system, which resolves the host name to the IP address, is updated whenever the ISP changes the public address of your LAN. It is also necessary to take care of "port forwarding" that ensures that the VPN server gets its IP data packets because the server shares the public IP address with all other computers on the LAN that access resources outside of the local network. None of this is specific to WireGuard. Nevertheless section 3 is dedicated to this topic. I'll add two comments. First, don't forget section 3.4 Enabling IP Forwarding or you may be disappointed to find that you cannot remotely access an IP camera or a home automation server or some other resource on the LAN even though the VPN service is working perfectly fine. Second, when setting up an WireGuard instance that will be used as a user or client only, none of this matters.

But what is a configuration? Basically, it is just an .INI file. Let's start with the configuration for a client.

[Interface] Address = 192.168.99.2/24 PrivateKey = gH5xInhP2NZw0t8hVgJPhTRDUh3Bir7FEynRcW8IHlg= [Peer] PublicKey = 5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= AllowedIPs = 192.168.99.1/32, 192.168.1.0/24 Endpoint = modomo.twilightparadox.com:53133

The WireGuard service needs some information about itself which is in the [Interface] section. Interface is an apt name because it hooks into the network by creating a network interface, which here as IP address 192.168.99.2. The secret PrivateKey is part of the authorization mechanism use by the VPN to ensure secure connections. Remember, the client must initiate the VPN tunnel so it obviously needs to know the public IP address (and UDP port) of the remote WireGuard server. This is called the VPN tunnel Endpoint. Because WireGuard uses public/private key authentification in the style of OpenSSH, the client must know the server's public key. Finally, it needs to know which IP packets to send through the tunnel. These are listed in CIDR notation in the AllowedIPs field. Each of my WireGuard clients needs a configuration file for each WireGuard server that it may be connected to. Actually, I have two configuration files for each WireGuard server. One of the configuration file sets AllowedIPs to 0.0.0.0/0 which means that all IP traffic sent out by the client machine will go through the VPN tunnel. The other configuration file sets AllowedIPs so that only IP packets destined to the WireGuard server or other machine on the same remote subnet are sent through the tunnel. In that case, e-mails will not to transit through the VPN (I do not run any mail servers... yet). Incidentally, when first testing a VPN connection, use AllowIPs=0.0.0.0/0, it will make things easier.

Each of the WireGuard servers that I run has only one configuration file. This file is rather long because it must hold the public key for each client (called Peer in the configuration file) that is allowed to connect to the server and initiate a VPN tunnel.

[Interface] Address = 192.168.99.1/24 ListenPort = 53133 PrivateKey = aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY= [Peer] PublicKey = BEnqBZ6rWcDO6lKhb6oXM7aRvE7fuIWCZw1PxgyMMyE= AllowedIPs = 192.168.99.2/32 [Peer] PublicKey = KwIurGAuy9BZXmQmHYNs63Ogp1+ukwfovZvCpFrkqQQ= AllowedIPs = 192.168.99.3/32 ...

There is also an AllowedIPs for each client which identifies the IP address of the client on the WireGuard virtual subnet. I won't elaborate further on that for fear of getting lost in the weeds. Note that there is no EndPoint for the peers/clients because the server will never be used to initiate a VPN tunnel. If you look at the Quick Start guide on the WireGuard site, it shows how to set up the VPN tunnel between symmetrical peers who each connect to the other machine. The animations also show how to create and bring up the virtual network interfaces, but this is now taken care of by a utility called wg-quick.

Don't worry, we no longer have to use ip commands to bring up network interfaces and we do not have to create those configuration files shown above. Clever people have created bash scripts that take care of all the nasty details. I have already mentioned wg-quick which sets up the virtual network interfaces. There's also a user management script, users.sh that create all user configuration files and updates the server configuration file. It's all very simple now for those among us such as me that don't really understand the ins and outs of networking on Linux, Windows etc. However, the management bash script uses templates and these need to be adjusted to the particulars of each server. But these adjustments are done once and do not normally need to be changed ever after. Consequently, section 4 on configuring WireGuard is really about setting the parameters in the various templates and data files used by the user management script. Be careful and methodical, don't skip any step, don't mix up the private and public keys of the server when editing its template (something I have often done much to my chagrin), and everything should work. At least it has for me in the last couple of years during which I have set up numerous WireGuard servers and clients.

There is, however, another important aspect of setting up a server. Routing of packets transiting the VPN tunnel has to be established. This is where my previous guides failed where routing tables were administered with the older iptables framework. Adjustments to use the newer nftables framework which has just been adopted in the January 2022 release of Raspberry Pi OS based on Debian 11.2 (Bullseye) were needed. This new version of the guide is mostly unchanged except for a new section, 4.1 Enabling and Configuring nftables, and a modified 4.6 Editing the Server Configuration Template section (previous section 3.5). Note that this is a very important aspect of setting up a server, but is of no practical significance for WireGuard clients.

Hopefully this overview will dispel any misgivings one may have about setting up WireGuard server on a Raspberry Pi (or other computer for that matter). Before proceeding with the installation, know that I am by no means an expert on networks as stated probably too many times already. However, I am a fast reader, blessed with a stubborn streak and, if I may say with blushing modesty, an ability to synthesize information gathered from many sources. In other words, everything here is just a rehash of stuff that I found elsewhere on the Web that has worked for me. I have tried to give credit where it is due, but if I have forgotten someone or made a mistake with the attribution of anyone's contribution, please accept my apologies and do send an e-mail so that I can correct my errors.

Installing WireGuard toc

Gone are the arcane instructions on accessing the wireguard package from unusual repositories of even of compiling the source code; installing WireGuard is now a breeze. Indeed while I go on and on in this section, it's a one-line command. In fact WireGuard has so quickly grown in popularity that by the time you read this post, the WireGuard tools may already be included in the distribution you are using. Try the following commands to see if that is the case.

pi@tarte:~$ which wg pi@tarte:~$ which wg-quick

If the two programs are found (probably in /usr/bin/), WireGuard is installed, so skip this section. If you are using a January 28, 2022 version of Raspberry Pi OS then the tools will not be installed. By the way, if the OS on the Pi is an older release or if you are using the January 28, 2022 Legacy version of the OS, please consult the appropriate older guide.

Before proceeding with the installation of Wireguard, update and upgrade the OS.

pi@tarte:~ $ sudo apt update; sudo apt upgrade -y 20 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Need to get 141 MB of archives. After this operation, 154 kB of additional disk space will be used. ... pi@tarte:~ $ uname -a Linux tarte 5.10.92-v7+ #1514 SMP Mon Jan 17 17:36:39 GMT 2022 armv7l GNU/Linux

As can be seen, the host name of the Pi on which I am installing the VPN is tarte (French for pie). The Pi itself is a model 3B. You may see something different than armv7l in the uname command if using a different model. Exactly which packages will be upgraded and the disk space freed or used will depend on how long it has been since the last upgrade was done. I checked and WireGuard had not sneaked in, so I installed the tools.

pi@tarte:~ $ which wg wg-quick pi@tarte:~ $ pi@tarte:~ $ sudo apt install wireguard -y ... The following additional packages will be installed: wireguard-tools The following NEW packages will be installed: wireguard wireguard-tools 0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. Need to get 85.1 kB of archives. After this operation, 302 kB of additional disk space will be used. ...

It is now possible to verify that the WireGuard utilities have been installed.

pi@tarte:~ $ which wg wg-quick /usr/bin/wg /usr/bin/wg-quick

To further test the installation, let's create an empty interface configuration file and bring up the interface.

pi@tarte:~ $ sudo touch /etc/wireguard/wg0.conf pi@tarte:~ $ wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip link set mtu 1420 up dev wg0

Verification shows that the WireGuard module was loaded and the network interface is created and that the server is up and waiting for incoming UDP packets on port 40213. The port may be different, because it is chosen randomly as far as I can make out.

pi@tarte:~ $ lsmod | grep wire wireguard 69632 0 curve25519_neon 28672 1 wireguard libcurve25519_generic 24576 2 curve25519_neon,wireguard libchacha20poly1305 16384 1 wireguard ip6_udp_tunnel 16384 1 wireguard udp_tunnel 24576 1 wireguard libblake2s 16384 1 wireguard ipv6 495616 39 wireguard pi@tarte:~ $ ifconfig wg0 wg0: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1420 unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 pi@tarte:~ $ sudo wg interface: wg0 listening port: 40213

However, it is rather pointless to bring up the interface because it will not do anything without proper configuration. Taking the interface down and stopping the server is just as easy, but note how the WireGuard module remains loaded.

pi@tarte:~ $ wg-quick down wg0 [#] ip link delete dev wg0 pi@tarte:~ $ sudo wg pi@tarte:~ $ ifconfig wg0 wg0: error fetching interface information: Device not found pi@tarte:~ $ lsmod | grep wire wireguard 69632 0 curve25519_neon 28672 1 wireguard libcurve25519_generic 24576 2 curve25519_neon,wireguard libchacha20poly1305 16384 1 wireguard ip6_udp_tunnel 16384 1 wireguard udp_tunnel 24576 1 wireguard libblake2s 16384 1 wireguard ipv6 495616 39 wireguard pi@tarte:~ $ sudo modprobe -r wireguard modprobe: ERROR: ../libkmod/libkmod-module.c:793 kmod_module_remove_module() could not remove 'libchacha20poly1305': Device or resource busy pi@tarte:~ $ lsmod | grep wire pi@tarte:~ $

Enabling Remote Access to the Local Network toc

Hopefully, the home local area network is not easily accessed from outside the LAN because that would mean that it is vulnerable to attacks from any bored script kiddie out there in the nasty world. However, easy yet secure access to the local network from anywhere on the Internet is possible when hosting WireGuard or another virtual private network server on the home network. The server is not the only element that needs to be in place for remote access. This section examines other prerequisites.

If you already have access to an IP camera, a home automation system or a self-hosted cloud or NAS then you are probably quite familiar with dynamic host names and port forwarding so that you can skim through the next three steps, but do read carefully about Enabling IP forwarding.

Static Host IP Address toc

How could one even hope to set up a virtual private network if the server does not have a fixed address? This is especially true for WireGuard which is "very quiet" as explained later. So the Raspberry Pi hosting the WireGuard server must have a fixed IP address on the local network. Many recommend adding a "DHCP Reservation" for the Pi in the router static IP address table. Each router is different, but essentially the desired IP address is given along with the Raspberry Pi MAC address which the DHCP server on the router uses to identify the Pi when it is time to assign IP addresses to devices on the LAN. If this is done, then it's a good idea to choose a static IP address outside the range of dynamic DHCP addresses. Most routers let the user specify that range.

To display the MAC address of the network interfaces use the ifconfig command

pi@tarte:~ $ ifconfig eth0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 ether b8:27:eb:cc:12:a8 txqueuelen 1000 (Ethernet) ... wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 ... ether b8:27:eb:cb:21:8b txqueuelen 1000 (Ethernet) ...

and search for the ether entry under each interface. By default the Ethernet interface is named eth0 and the Wi-Fi interface is named wlan0. The information is also buried in the system directory.

pi@tarte:~ $ cat /sys/class/net/eth0/address b8:27:eb:cc:12:a8 pi@newpi:~ $ cat /sys/class/net/wlan0/address b8:27:eb:cb:21:8b

Of course, on older Pi models there will not be a Wi-Fi interface unless some hardware such as a Wi-Fi USB dongle has been added.

The static IP address table of my router holds a rather limited number of entries. And sometimes I think that its non-volatile memory is less reliable than the SD cards I use with the Raspberry Pi. Consequently, I let the DHCP client of the Raspberry Pi set up a static IP address for the Wi-Fi and Ethernet interfaces instead of getting a dynamic address from the router. This is done by adding the needed information at the end of the configuration file.

pi@tarte:~ $ sudo nano /etc/dhcpcd.conf

... # static IP interface eth0 static ip_address=192.168.1.21/24 static routers=192.168.1.1 static domain_name_servers=192.168.1.1 interface wlan0 static ip_address=192.168.1.22/24 static routers=192.168.1.1 static domain_name_servers=192.168.1.1

Of course this is the settings for a newer Pi with built-in Wi-Fi. On the old model 1 Pi, there is no wlan0 interface.

On my router, the Raspberry Pi shows up as a connected device with a "self-assigned" IP address. Again, the static IP address assigned to the Pi should be outside the pool of dynamic DHCP addresses controlled by the DHCP server on the router. For some important devices such as the Raspberry Pi that hosts my home automation system, I chose to set a static IP address with a DHCP reservation on the router and have the Raspberry Pi set up a static IP address, just to be safe.

Public IP Address and Dynanic DNS Host Name toc

All the computers and devices connected to your home network (often called LAN for Local Area Network) are assigned an IP address by the DHCP server which is probably the router provided by your Internet Service Provider (ISP). On my system the router has 192.168.1.1 for an IP address and the Raspberry Pi hosting the VPN server has a fixed IP address: 192.168.1.22. If you think about it, there are many thousands of devices spread around the globe with that particular address. There is no hope that my Raspberry Pi can be reached from outside the LAN using 192.168.1.22 as the destination address. That problem has been solved with clever routing algorithms. All my devices connected to the local network send their traffic to the router at 192.168.1.1 when receiving or sending data to sites on the Internet. The router then passes each packet on to the ISP, changing the source IP address from say 192.168.1.22 to a public IP address assigned to my network by the ISP. That assigned public IP is unique on the whole of the Internet so that sites that receive packets from devices on my LAN can reliably reply using as the destination IP the public IP address assigned by my ISP. When the router receives these packets of data, it routes them to the appropriate device on the LAN. There's obviously a little bit of magic going on to keep track of which device gets which packets as they come in, but that's another story. The point is that to talk to my Raspberry Pi from outside the LAN, the public IP address assigned by the ISP must be known. That's not difficult to find. Usually the router with the outside connection to the Internet shows that information.

Status page in router showing public IP address

In addition, there are plenty of sites on the Web that will display your public IP address.

If only it were that simple. Unfortunately, the public IP address cannot be trusted because it is dynamically assigned by the ISP and may change from time to time. Any DHCP server can force a client to reconnect at any time and change the assigned IP at that point. Also, when one logs off a network, the DHCP server will reserve the assigned IP for a certain "lease" time should the client connect again. After the lease time is expired, the IP address is returned to the pool of available addresses that the DHCP server can assign to any new client. I have no idea just how long lease time is but it is not very short. Furthermore, devices like smart speakers and phones seem to be calling the mothership often enough to restart the lease so that I sometimes have the same public IP address for days on end. For the duration of this post, let's say that my sticky dynamic public IP address is 168.102.82.120. Indeed, I could get away with using 168.102.82.120 as the public IP address of my network for testing the WireGuard configuration later on.

In practice, though, one should avoid using a dynamic IP address. Instead the local network should be reached through a dynamic host name. DNS is the domain name system which translates the name of a website such as www.google.com into an IP address (172.217.6.4). Some sites offer a service, often free, that associates a domain name with an IP address. These sites update the IP addresses in their database at regular intervals. So, we will put in the HTML request the domain name obtained from the DNS service. The DNS will translate this name into an IP address that will be updated each time the ISP assigns a different IP address to the home server. There are plenty of sites on the Web that describe how to set up a dynamic domain name with any one of a number of DDNS providers and among them there is a description of how I did it using freedns.afraid.org back in 2018. So get yourself a dynamic host name, and learn how to signal any change in the public IP address assigned to your network to the DDNS service.

In what follows, my dynamic host name is deemed to be modomo.twilightparadox.com, which I hope is a fictitious name.

Port Forwarding toc

Part of the magic behind the routing of data packets across the router is that each packet must be sent through a "port". Ports are not physical entities, they are more like an apartment number added to a street address to ensure that a letter gets to the proper mail box. In the jargon, they are "end points" of a communication link and must be tacked on at the end of an IP address or host name. As an example, FTP control packets sent from the desktop computer to the Raspberry Pi, have as a destination address 192.168.1.22:21. Some port numbers are implicit. All HTTP traffic is usually sent to port 80, while HTTPS traffic is sent to port 443. Try https://www.google.com:9090 in a browser. This will send the request to port 9090, which is specified after the colon. The search engine does not "listen" to that port, so nothing will be displayed unless you are very patient and then some sort of error message may appear. Try https://www.google.com:443 and you will see the familiar search page very quickly, but you don't have to write the port number, it is implicit the HTTPS protocol.

For security reasons, consumer class routers such as the one supplied by an ISP have a built-in firewall that controls incoming and outgoing network traffic. Typically, outgoing traffic can only be sent out if the end point (i.e. port) is for some "well-known" use. Typically, incoming traffic is blocked outright unless it is part of an exchange initiated by a device on the LAN. That is why you can use a Web browser from your home computer to read this post! There's an obvious problem for us. How can the Raspberry Pi be reached if the firewall will not let through IP packets destined to the Pi. So a "hole" has to be punched through the firewall. In technical terms, a port forwarding rule has to be established. That rule will instruct the firewall to send any IP packet addressed to the correct port to be sent on to a Pi.

OpenVPN which is a very popular VPN package uses a default destination port, 1194 to be precise, although that can be changed. Furthermore, whichever port OpenVPN uses, it will identify itself when queried with a port scanner. WireGuard does not have a default port nor will it reply if the port it does use is probed. That means that when configuring WireGuard later on, you will have to choose a port number. The latter are 16 bit integers, which means they have a range from 0 to 65435. However, choosing a number between 0 and 1023 is generally a bad idea. Typically, tutorials on the installation of WireGuard use relatively big numbers such as 53133 which are in the dynamic, private or ephemeral range.

It is difficult to give instructions about implementing port forwarding because each router model is different. On mine, there is a Port Forwarding tab in the Basic menu, and a Add Rule button which displays the window shown below when clicked.

Adding a rule to forward a port in router

As can be seen the router wants to forward a range of ports, so I specified a range of one port. I used the same port number for the public (Internet facing) port and for the private (local network) port. The latter will be appended to the local IP address, 192.168.1.22. In my case, all IP traffic sent to modomo.twilightparadox.com:53133 will end up at the outward facing edge of my router as traffic sent to 168.102.82.120:53133. The router will then pass it over to the local network as traffic bound for 192.168.1.22:53133. If everything is set up correctly, WireGuard will know what to do with it.

If you are having trouble setting up the port forwarding rules on your router, there are sites such as PF Network Utilities that have information about many router models. They also offer utilities that perform various functions including port forwarding, which I cannot endorse because I am much too paranoid to install such software and much too cheap to pay for it in the first place. I must say that the site provided accurate information about my router, but it was hidden behind a lot of advertising for their products.

Enabling IP Forwarding toc

If access to other LAN resources such as an IP camera or a Web server is needed, then IP forwarding has to be enabled on the computer hosting the WireGuard server. If you do not enable IP forwarding, you will not be taking full advantage of the virtual private network. I repeat, skipping IP forwarding only makes sense if the only device that needs to be reached from outside with the VPN is the WireGuard host machine.

pi@tarte:~ $ cd /etc pi@tarte:/etc $ ls -l sysctl* -rw-r--r-- 1 root root 2683 Apr 8 06:56 sysctl.conf sysctl.d: total 8 -rw-r--r-- 1 root root 51 Nov 26 2018 98-rpi.conf lrwxrwxrwx 1 root root 14 Apr 8 07:51 99-sysctl.conf -> ../sysctl.conf -rw-r--r-- 1 root root 639 May 17 2018 README.sysctl

Note how /etc/sysctl.d/99-sysctl.conf is a symbolic link to /etc/sysctl.conf. It will suffice to edit the later to enable IP packet forwarding.

pi@tarte:/etc $ sudo nano sysctl.conf

Change

... # Uncomment the next line to enable packet forwarding for IPv4 #net.ipv4.ip_forward=1 ...

to

# Uncomment the next line to enable packet forwarding for IPv4 net.ipv4.ip_forward=1

as instructed in the configuration.file. A reboot will be necessary for the change to take effect.

pi@tarte:~ $ sudo reboot Connection to tarte.local closed by remote host. Connection to tarte.local closed. ... michel@hp:~$ ssh pi@tarte.local ... ... pi@tarte:~ $ sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1

Configuring WireGuard toc

Configuring WireGuard server is the most complicated part of setting up the VPN. There are many tutorials on how to proceed, starting with the WireGuard Quick Start guide. Frankly, I could not make much of it, because I really did not and still do not know enough to configure network interfaces, ip routing and so on from the command line. I did find other resources on the Web that helped me gain some knowledge, but in the end I have found that Adrian Mihalko, who provided some of the first instructions for installing WireGuard on the Raspberry Pi back when it was rather complicated, also created a user management script that perfectly suited my needs and level of understanding. It turns out that the script is actually a fork of the wg-config project by faicker on GitHub. I should have credited faicker just as Adrian did. I wanted a VPN server on the home network and VPN clients on Android devices (could be iOS) and this is precisely what the script facilitates. It turns out that the script takes all the drudgery out of installing VPN "clients" on a dual boot (Linux and Windows) portable computer also.

The instructions below are very detailed, perhaps too much so. I would suggest that you read User management with Wireguard User Management Script written by Adian Milhalko and return here for more information if needed.

It may be useful to belabour a point. WireGuard operates a peer-to-peer network. However it is sometimes useful to look at it as a client-server model, such as here where the WireGuard peer running on the Raspberry Pi is viewed as a server and the WireGuard peers on various Android devices are viewed as clients. In the end, peer, server, client and user are all the same thing. Hopefully, that will not be a source of confusion.

Enabling and Configuring nftables toc

By default, the nftables service was not enabled, but this is easily remedied. However the barebones configuration in /etc/nftables.conf, as shown here,

#!/usr/sbin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority 0; } chain forward { type filter hook forward priority 0; } chain output { type filter hook output priority 0; } }

has to be modified to enable the proper routing of packets transiting the VPN tunnel. Luckily, the Debian Wiki contains a page entitled Simple Private Tunnel VPN With WireGuard with simple instructions on how to add Wireguard NAT table at the end of the configuration file. As usual, I used the nano editor to make the addition.

pi@tarte:~/wg_config $ sudo nano /etc/nftables.conf

Here is what the configuration file should look like after the NAT table, shown on a green backround, has been added.

#!/usr/sbin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority 0; } chain forward { type filter hook forward priority 0; } chain output { type filter hook output priority 0; } } add table wireguard-nat table ip wireguard-nat { chain prerouting { type nat hook prerouting priority -100; policy accept; } chain postrouting { type nat hook postrouting priority 100; policy accept; oifname "wlan0" masquerade } }

Note the output interface name (oifname) in the last line of text. It must be set to "eth0" if the Pi's connection to the LAN is with an Ethernet cable.

Finally, enable nftables

pi@tarte:~/wg_config $ sudo systemctl enable --now nftables

It is easy to check that the service is enabled and that the nftables configuration file is correct.

pi@tarte:~ $ sudo systemctl status nftables ● nftables.service - nftables Loaded: loaded (/lib/systemd/system/nftables.service; enabled; vendor preset: enabled) Active: active (exited) since Sat 2022-02-19 12:17:02 AST; 6h ago Docs: man:nft(8) http://wiki.nftables.org Process: 129 ExecStart=/usr/sbin/nft -f /etc/nftables.conf (code=exited, status=0/SUCCESS) Main PID: 129 (code=exited, status=0/SUCCESS) CPU: 117ms Feb 19 12:17:02 tarte systemd[1]: Finished nftables. Warning: journal has been rotated since unit was started, output may be incomplete. pi@tarte:~ $ sudo nft list ruleset table inet filter { chain input { type filter hook input priority filter; policy accept; } chain forward { type filter hook forward priority filter; policy accept; } chain output { type filter hook output priority filter; policy accept; } } table ip wireguard-nat { chain prerouting { type nat hook prerouting priority dstnat; policy accept; } chain postrouting { type nat hook postrouting priority srcnat; policy accept; oifname "wlan0" masquerade } }

I suggest that these two commands be tried after a reboot just to check that the service is running as expected.

Everything in this section needs to be done only once.

Installing the faicker/Mihalko User Management Script toc

There is one prerequisite to install that will be used to generate QR-code images that will make it very easy to configure a WireGuard client on an Android or iOS device.

pi@tarte:~ $ $ sudo apt install qrencode -y Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: libqrencode4 The following NEW packages will be installed: libqrencode4 qrencode 0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. Need to get 71.8 kB of archives. After this operation, 145 kB of additional disk space will be used. ... Setting up libqrencode4:armhf (4.0.2-1) ... Setting up qrencode (4.0.2-1) ...

Next we download an archive containing the script and supporting files and extract its content to a directory that will be called ~/wp_config.

pi@tarte:~ $ wget https://github.com/adrianmihalko/wg_config/archive/master.zip --2020-07-02 19:00:52-- https://github.com/adrianmihalko/wg_config/archive/master.zip Resolving github.com (github.com)... 140.82.112.4 ... 2020-07-02 19:00:53 (2.60 MB/s) - ‘master.zip’ saved [3688/3688] pi@tarte:~ $ unzip -j master.zip -d wg_config Archive: master.zip 3e27f97b45b201603d9522d41959b356e787260b inflating: wg_config/README.md inflating: wg_config/client.conf.tpl inflating: wg_config/server.conf.tpl inflating: wg_config/user.sh inflating: wg_config/wg.def.sample pi@tarte:~ $ mkdir downloads pi@tarte:~ $ mv master.zip downloads/wg_config_script.zip

I chose to create a ~/downloads directory and to moving the script archive in it with a more meaningful name, but it would have been fine to just delete the archive.

Instead of playing around with a zip archive, I could have followed the recommended installation process by installing git along with qrencode and then cloning the repository containing the script.
pi@tarte:~ $ sudo apt install git qrencode -y ... pi@tarte:~ $ git clone https://github.com/adrianmihalko/wg_config.git Cloning into 'wg_config'...

The following list of steps might look daunting; it is actually rather easy to configure the WireGuard server and to add clients or peers with the script.

Generating the Private and Public Server Keys toc

WireGuard encrypts the data exchanged over the virtual network. Much like SSH, asymmetric encryption is used to set up the secure session. Both server and client (or peers actually) have private and public keys, but only the latter are exchanged for authentication. Again, like SSH, the private keys have to be shared "out-of-band" beforehand. In this case that means that the keys must be manually copied to each peer configuration file. There is no third party "certificate authority" for SSL certificates as in the HTTPS or OpenVPN protocols.

The first step, which is done only once, is to generate the private and public keys of the WireGuard server on the Raspberry Pi.

pi@tarte:~ $ cd wg_config pi@tarte:~/wg_config $ wg genkey | tee server_private.key | wg pubkey > server_public.key

If the command seems a bit opaque to you as it did to me, here is what it actually translates to:

pi@tarte:~ $ wg genkey > server_private.key pi@tarte:~ $ wg pubkey > server_public.key < server_private.key

These two keys are needed in the next steps. Copy them into a text editor on the desktop or open a second SSH session on the Raspberry Pi for easy access to the keys later.

pi@tarte:~/wg_config $ cat server_public.key 5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= pi@tarte:~/wg_config $ cat server_private.key aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY=

Normally, one never makes the private key public. So the keys shown above are only for demonstration purposes, and you must replace those values with the one actually generated.

Creating and Editing the Server Definition File toc

This step, performed once only, creates the wg.def file which contains data the script will use to make the server configuration and client configuration files.

pi@tarte:~/wg_config $ cp wg.def.sample wg.def pi@tarte:~/wg_config $ nano wg.def

This is the template.

_INTERFACE=wg0 _VPN_NET=192.168.99.0/24 _SERVER_PORT=51820 _SERVER_LISTEN=wg.example.com:$_SERVER_PORT _SERVER_PUBLIC_KEY= _SERVER_PRIVATE_KEY=

And this is how I changed it.

_INTERFACE=wg0 _VPN_NET=192.168.99.0/24 _SERVER_PORT=53133 _SERVER_LISTEN=modomo.twilightparadox.com:$_SERVER_PORT _SERVER_PUBLIC_KEY=5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= _SERVER_PRIVATE_KEY=aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY=

If subnet 192.168.99.xxx is used on the local area network, then the value of _VPN_NET will need to be changed. The _SERVER_PORT is the UDP port that will have to be forwarded to the WireGuard sever by the LAN router or gateway. This can be (perhaps should be) changed. And, of course, it is necessary to change wg.example.com in _SERVER_LISTEN to the host name of the Raspberry Pi which, in my (fictitious) case, is: modomo.twilightparadox.com as explained in 2.2 Public IP Address or Dynamic Host Name. Do not put the protocol prefix such as https://, just the domain name. As I explained above, the public IP address assigned to me by my ISP changes so rarely that I could get away with the public IP address instead of a host name for testing purposes.

If the CIDR notation 192.168.99.0/24 is not familiar, just think of the trailing integer after the slash as the number of fixed most significant 1 bits in the subnet mask. Well, that's really clear. Perhaps working out the example will help. The subnet mask is 32 bits (or 4 bytes) of which the most significant 24 are 1s and the least significant 8 bits are 0. Hence the mask is 255.255.255.0. So the virtual network peers will have IP addresses in the 192.168.99.xxx block. The server will be at 192.168.99.1, the first client at 192.168.99.2, the second at 192.168.99.3 and so on. The script will handle this sequential allocation of IP addresses automatically.

Editing the Client Configuration Template toc

The client configuration template, client.conf.tpl, used by the script to create each user (or client) configuration file is quite short.

[Interface] Address = $_VPN_IP PrivateKey = $_PRIVATE_KEY [Peer] PublicKey = $_SERVER_PUBLIC_KEY AllowedIPs = 0.0.0.0/0 Endpoint = $_SERVER_LISTEN

If this template is not changed, then the user configuration script will create two identical configuration files with different names to connect to the VPN server. When either of these configuration file is used, all IP traffic destined outside the client's LAN will be routed through the VPN "tunnel". That means that a site such as Google or Bing will "think" that a search request made by the client machine is actually made by the server on the other end of the "tunnel". However, the configuration script is quite clever and if the AllowedIPs is changed then it will create two distinct configuration files: one that routes all IP traffic through the VPN and one that only routes traffic for the LAN on which the server sits through the tunnel. The only difference between the files is the AllowedIPs field. Here is how to change the AllowdIPs.

pi@tarte:~/wg_config $ nano client.conf.tpl

[Interface] Address = $_VPN_IP PrivateKey = $_PRIVATE_KEY [Peer] PublicKey = $_SERVER_PUBLIC_KEY AllowedIPs = 192.168.99.1/32, 192.168.1.0/24 Endpoint = $_SERVER_LISTEN

Note that the first AllowedIPs (192.168.99.1/32) is the address of the Wireguard server on the virtual network and the 32-bit mask means that the client/user will not be able to reach any other IP address on the 192.168.99.xx subnet. The second allowed IP address 192.168.1.0/24, which is the 192.168.1.xxx block of IP addresses corresponding to my home local network. The Raspberry Pi has a static IP address on that network: 192.168.1.22, the ISP supplied cable modem/router is at 192.168.1.1 and its integrated DHCP server allocates IP addresses in the 192.168.1.100-200 range where most of my IoT devices can be found. You will, of course, have to adjust the AllowedIPs to refer to the correct IP addresses in your particular situation. It could be that your LAN is on subnet 192.168.1.xxx as suggest above, or 192.168.0.xxx, but some LANs use other blocks of private IPv4 addresses such as 10.0.3.xxx.

Editing the Server Configuration Template toc

The server configuration template, uses iptables to modify the IP packet routing. As mentioned at the very beginning that package is not installed in the latest version of Raspberry Pi OS. It should be possible to use nft commands instead, but that is not recommended. Consequently, remove the PostUp and PostDown keys in the Wireguard sever interface template.

pi@tarte:~/wg_config $ nano server.conf.tpl

Original version:

[Interface] Address = $_SERVER_IP ListenPort = $_SERVER_PORT PrivateKey = $_SERVER_PRIVATE_KEY PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

Modified version:

[Interface] Address = $_SERVER_IP ListenPort = $_SERVER_PORT PrivateKey = $_SERVER_PRIVATE_KEY

If, for whatever reason, one wants to modify packet routing when the VPN server is enabled, then the following seems to work.
 
[Interface] Address = $_SERVER_IP ListenPort = $_SERVER_PORT PrivateKey = $_SERVER_PRIVATE_KEY PostUp = nft create table ip wireguard-nat; nft 'add chain wireguard-nat prerouting { type nat hook prerouting priority dstnat; policy accept; }'; nft 'add chain wireguard-nat postrouting { type nat hook postrouting priority srcnat; policy accept; oifname "wlan0" masquerade; }' ; systemctl restart nftables PostDown = nft delete table wireguard-nat ; systemctl restart nftables

It may be necessary to adjust the interface name in the PostUp value. As shown it is assumed that the Pi connects to the LAN with the Wi-Fi interface, hence oifname "wlan0", but if a wired Ethernet connection is used then the entry should contain oifname "eth0".

If you decide to control routing on the fly in this fashion then DO NOT add the Wireguard NAT table to the nftables configuration file as shown in section 4.1 Enabling and Configuring nftables. Nevertheless, the nftables.service must to be enabled as explained in that section.

"While it’s still possible to jam rules onto nftables chains with PreUp [or PostUp] statements in your WireGuard config, it’s probably best to just put them all in a master nftables config file". It's not me that says that, because figuring out what's advisable is in this context is beyond my ken, it's a WireGuard management firm that provides useful information on How to Use WireGuard With Nftables.

Creating an Empty WireGuard Server Configuration File toc

If the (empty) configuration file, wg0.conf, was not created when testing the installation of WireGuard in the section entitled Verifying that WireGuard is Properly Installed, now is the time it must be done.

pi@tarte:~/wg_config $ cd .. pi@tarte:~ $ sudo touch /etc/wireguard/wg0.conf

This is done once only. The user management script will update this file each time it is used to add or delete a user. Consequently, the file should not be edited manually.

Managing the WireGuard Service toc

When first installing WireGuard and when testing the installation of the server, it is useful to manually start and stop the service. This is done with the wg-quick utility.

pi@tarte:~ $ sudo wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip link set mtu 1420 up dev wg0 pi@tarte:~ $ sudo wg-quick down wg0 [#] ip link delete dev wg0

Note that the output will be more voluminous when the server configuration file is finally created as shown later.

Again when testing, it may be of value to check on the status of the VPN server. This can be done with the systemd control utility and with the wg utility.

pi@tarte:~ $ sudo systemctl status wg-quick@wg0 ● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0 Loaded: loaded (/lib/systemd/system/wg-quick@.service; disabled; vendor preset: enabled) Active: inactive (dead) Docs: man:wg-quick(8) man:wg(8) https://www.wireguard.com/ https://www.wireguard.com/quickstart/ https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8 https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8 pi@tarte:~ $ sudo wg interface: wg0 listening port: 53786

Later on, when the service is completely configured and running, the status will be different.

pi@tarte:~ $ sudo systemctl status wg-quick@wg0 ● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0 Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; vendor preset: enabled) Active: active (exited) since Fri 2021-11-05 16:50:49 ADT; 20min ago Docs: man:wg-quick(8) man:wg(8) https://www.wireguard.com/ https://www.wireguard.com/quickstart/ https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8 https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8 Process: 378 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS) Main PID: 378 (code=exited, status=0/SUCCESS) Nov 05 16:48:58 domo systemd[1]: Starting WireGuard via wg-quick(8) for wg0... Nov 05 16:50:46 domo wg-quick[378]: [#] ip link add wg0 type wireguard Nov 05 16:50:46 domo wg-quick[378]: [#] wg setconf wg0 /dev/fd/63 Nov 05 16:50:47 domo wg-quick[378]: [#] ip address add 192.168.99.1/24 dev wg0 Nov 05 16:50:47 domo wg-quick[378]: [#] ip link set mtu 1420 up dev wg0 Nov 05 16:50:49 domo systemd[1]: Started WireGuard via wg-quick(8) for wg0.

Again, the above is only an indication of the information that may be displayed. The wg command will also display more information which will depend on the number of peers/clients that have been set up. When a client or peer has created a tunnel (i.e. opened a VPN connection) then the number of bytes transmitted and received through the connection will also be displayed.

Once WireGuard is properly installed, the service should be started automatically. This is done with the usual systemctl command.

pi@tarte:~ $ sudo systemctl enable wg-quick@wg0 Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service → /lib/systemd/system/wg-quick@.service.

This is usually done only once. From then on, whenever the Raspberry Pi is booted, systemd will start the VPN server. If for some reason the WireGuard server should not be started, use the disable command.

pi@tarte:~ $ sudo systemctl disable wg-quick@wg0 Removed /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service.

It will be possible to enable the service again later. If one thinks about it, a VPN server should really be functioning at all times.

Managing Users toc

Adrian Mihalko discusses setting up a mobile client on IOS using his script. Below, I show how to use the same script to set up clients in Android, Windows 10 and Linux. As will be seen, once the setup described above is finished, adding users with the script is rather simple.

Adding Users toc

When it is used to create a new user, the user.sh script creates a configuration file for the instance of WireGuard running on the user's machine and it updates the server configuration file to accept a VPN connection (or tunnel) from the new client. Creating a user is very simple, start the WireGuard (even with an empty wg0.conf file), and then run the user.sh script with the -a option followed by a unique name identifying the user/client to create.

pi@tarte:~ $ sudo wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip link set mtu 1420 up dev wg0 pi@tarte:~ $ cd wg_config pi@tarte:~/wg_config $ sudo ./user.sh -a nexus7
█████████████████████████████████████████████████████████████
████ ▄▄▄▄▄ █ ▄▄▄█  ▄ ██▀  █▄█▀▀▄▄██▄ █▀█  ▄ █▄ ▀██ ▄▄▄▄▄ ████
████ █   █ █  ▀▄▀█ ██ █▄▀▀▀▄▀▄  ▄▀▄ █ █▄▀▄▄█▀ ▀▄▀█ █   █ ████
████ █▄▄▄█ █▀▄▀█ ▀▀▀▄▄▀▄▀▄ ▀ ▄▄▄ ▄▄  ▄▄▀█▀▀█▀▀ ███ █▄▄▄█ ████
████▄▄▄▄▄▄▄█▄▀ █▄▀▄█ ▀▄▀ ▀▄▀ █▄█ █ █ █ █▄█ ▀ █▄█▄█▄▄▄▄▄▄▄████
████ ▄█▀ ▄▄▀▀▀   ▄ ▄█▀ ▄▀▄ ▀ ▄  ▄█▄  ▄▀██  ▀█▄ █ ▀█▄▀  ▄ ████
█████▀▀▄█ ▄ ▄ █▀▀▄▄▀  ▀██▄▄▄▀▄█▀█ ▄▄▀ █▀   ▀▄▀ ▀▄ ██▀▄█▀ ████
████▄▄▄▀▀▀▄▀█▀▄██▄▀█ ▀█ ▀█▀█ ▀█▀█▀ ▀ █▀▄█▄█▄██ ▀██▀█ █▀▄▀████
████▄▄▀▄▀ ▄█▀▄ █▄▄ ▀ ▀█ █  ▀ ▀█▄ ██▄ ▀ █▄▄▀█▄▀▄▄ ▄▀▀  █▄ ████
█████▄▄▄▄█▄ ▄▀▄▄▄▀▀▀▀█ █▀ ▄▄ ▄▄▀▄██ ██ █ ▀ █▀▀█ ▄▀█  █▀▀▀████
████▀▀▀   ▄▄  ▄▄███▄█ ▀█▄▄ █ ▄█▀██ ▀▄▀ ▄█▄ ▄ ▀ ██▀█▄▀▄█▀▄████
█████▀▄▄█ ▄▄▀██▀█▄ ▄ ▀█  █ ██▀▀▄ ▀▀▀ █ ▄▀▄▄▀██▀▀▀▄▀█▀▀▀▄ ████
████▀▀▀██ ▄█▄█ █▄▀█▄ ▀  █ ▀ ▄▀█▄ ▄▄▀▀▄▀▄▄▀████▀▄ ▀ ▀▄▀▄▄█████
██████▄▄ ▄▄▄  █▄▀▄ ▄█▄▀▄▀▄▄▀ ▄▄▄ ▄▄  ▄▀█▀ ▄▄ ▄▄▄ ▄▄▄  ▀▄▀████
█████▀█▀ █▄█ ▄███▄ ▀ ▄▄█ ▀▄  █▄█ ▀█▄▀ ▀█▄ ▄ ▄ ██ █▄█ █ █ ████
█████▄▀  ▄▄ ▄█▀▄█▄▀▄▄█▄ ▄▀▄█▄▄▄▄▄█ ▀ ▀▀▄▀ ▄█▄█ ▀▄▄   ▄▀▀ ████
████▄ ▄▀▄▄▄▀█▀▀██▄ ▄▀▄█▀  █▀▀█ █▄▀▀▄ ▄█▄ █▀▀▀▄  █▀▄  ▀▄▄▄████
████▀██ ▄▄▄▄█▄▀██▀▀▀▀▀ ▄▀ █ ▀ ▄███▀▄▄████▀▀ ██▄ ▄ ▀▄▄▄▀██████
████▄█▀ ▀▀▄█▀█▀▄▀██▄█▀ ▀█▄▀▀▀█  ███▀▄█ ▄█▄  ▄▄▀▀▄ █▀██▄█▄████
█████▀ █▄ ▄ ██▀██▄ ▄▄█▀▀▄▀▀▄█▀  ██▄▀ ▀▄▄█▄▄▀▀▀▀   ██ ▀▀█ ████
████ ▄▄▀█▀▄ █▀▄  ▀█▀ ▀ █▀▀▀▄▀ █▄█ ▀▀█ ▄▄▀█▄ █  ▀██▄█▀ █ ▄████
████▄▀▄▄▀█▄▀▄▄▄▀ ▄ ▄█▄▀█▀▄▄  ▄▄█▀█▄▄▄█▄▀▀ ▄█▀ ▀▀▄ ███ ▀ █████
████▄ ▀▄▄▄▄ ▀ ██ █▄▀ ███ ▄█▀ ██▀█  ▄▀ ▀▀█ ▄███  █▄█▀ ▄▀█ ████
███████▄██▄█▀ ▀▄▄  ▄ █▄ ██▀█ ▄▄▄ ▀█▀ ▀██▀▄▀▀▀██▄ ▄▄▄  ▀ ▀████
████ ▄▄▄▄▄ █▀▀█  ▀  █▄██▀▀▄▄ █▄█ ▀██▀▀█▀▀▄ ██▄▄▄ █▄█  ▄ █████
████ █   █ █▄  ██▄ ▀▀▀ █▄ ▄▄▄▄▄ ▄██ ▀▄███ ▀ ▀██▄ ▄▄  ▄▀▀▄████
████ █▄▄▄█ █▀█▀ ▀▀▀▄█ ▄▀ ▀▄█▄▀▄ ▀█ ▄█▀█▄█▄   ▀▄▀██▀██▄█  ████
████▄▄▄▄▄▄▄█▄▄▄▄▄▄▄▄▄█▄█▄█▄██▄█▄▄█▄█▄█▄█▄▄███▄▄█▄▄▄▄▄▄█▄█████
█████████████████████████████████████████████████████████████

The following message

Unable to modify interface: No such device wg set failed

will be printed just below the QR codes if the WireGuard service was not running on the Pi. In that case, rerun the two commands shown above. Don't worry about the QR code, it can be displayed later when needed to configure the WireGuard client on the Android or iOS device. The script executes very quickly but it nevertheless does quite a bit of work.

Here is the content of the user directory just created.

pi@tarte:~/wg_config $ ls -l users/nexus7 total 24 -rw-r--r-- 1 root root 216 Jul 5 17:49 client.all.conf -rw-r--r-- 1 root root 216 Jul 5 17:49 client.conf -rw-r--r-- 1 root root 913 Jul 5 17:49 nexus7.all.png -rw-r--r-- 1 root root 913 Jul 5 17:49 nexus7.png -rw-r--r-- 1 root root 45 Jul 5 17:49 privatekey -rw-r--r-- 1 root root 45 Jul 5 17:49 publickey

There are two client configuration files, client.conf and client.all.conf and two QR code images that correspond to these configurations. One or both of these will be used to configure the Android or iOS client later on. Finally, as with the WireGuard server, the client has a private and public encryption key.

Here is the content of one of the client configuration files and the server configuration file.

pi@tarte:~/wg_config $ cat users/nexus7/client.conf [Interface] Address = 192.168.99.2/24 PrivateKey = gH5xInhP2NZw0t8hVgJPhTRDUh3Bir7FEynRcW8IHlg= [Peer] PublicKey = 5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= AllowedIPs = 192.168.99.1/32, 192.168.1.0/24 Endpoint = modomo.twilightparadox.com:53133 pi@tarte:~/wg_config $ sudo cat /etc/wireguard/wg0.conf [Interface] Address = 192.168.99.1/24 ListenPort = 53133 PrivateKey = aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY= [Peer] PublicKey = BEnqBZ6rWcDO6lKhb6oXM7aRvE7fuIWCZw1PxgyMMyE= AllowedIPs = 192.168.99.2/32

There are two sections to a WireGuard configuration file. The first, [Interface] defines the IP address of the client or server on the virtual network. As already mentioned, the script will assign the first valid IP address on the virtual network, 192.168.99.1 to the Raspberry Pi hosting the server. So the script assigned the next valid address, 192.168.99.2, to the Nexus 7 client. The script also generated public and private keys for the client and server and includes the private key of each in its interface definition. The second part of the configuration file lists all the peers with which a tunnel can be established. To create the virtual connection, the client must know how to reach the server (the Endpoint of its peer) and its public key. Similarly, the server must know its own address, on which UDP port it is listening, and the IP address and public key of any client (peer) that will be allowed to create a tunnel. Perhaps seeing the two configuration files side by side may make these links more obvious.

client.conf and wg0.conf side by side with arrows between common or related parameter values

There is a second user configuration file.

pi@tarte:~/wg_config $ cat users/nexus7/client.all.conf [Interface] Address = 192.168.99.2/24 PrivateKey = gH5xInhP2NZw0t8hVgJPhTRDUh3Bir7FEynRcW8IHlg= [Peer] PublicKey = 5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= AllowedIPs = 0.0.0.0/0 Endpoint = modomo.twilightparadox.com:53133

It is identical to the first one except for the AllowdIPs field. I use both configuration files as explained below.

To add an additional user, just repeat the steps. Remember to start the WireGuard server if it is not already running.

pi@tarte:~ $ cd wg_config pi@tarte:~/wg_config $ sudo ./user.sh -a zenfone pi@tarte:~/wg_config $ ls users nexus7 zenfone

A new directory with the new user configuration files and so on is created alongside the directories for the previously created users. The script updates its own list of IP addresses assigned to the clients and their public keys.

pi@tarte:~/wg_config $ cat .saved nexus7 192.168.99.2/24 BEnqBZ6rWcDO6lKhb6oXM7aRvE7fuIWCZw1PxgyMMyE= zenfone 192.168.99.3/24 dVq8SvBwcKrFnBBQL2F7JcsVNB4jSf6f3kbtfnsYGCA=

It also removes these assigned IP addresses from the list of available IPs.

pi@tarte:~/wg_config $ cat .available_ip 192.168.99.4/24 192.168.99.5/24 ... 192.168.99.253/24 192.168.99.254/24

A new /etc/wireguard/wg0.conf configuration file is created by the script.

[Interface] Address = 192.168.99.1/24 ListenPort = 53133 PrivateKey = aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY= [Peer] PublicKey = BEnqBZ6rWcDO6lKhb6oXM7aRvE7fuIWCZw1PxgyMMyE= AllowedIPs = 192.168.99.2/32 [Peer] PublicKey = dVq8SvBwcKrFnBBQL2F7JcsVNB4jSf6f3kbtfnsYGCA= AllowedIPs = 192.168.99.3/32

The new client shows up as an additional Peer in the server configuration file. That is all that needs to be done on the server for each additional client. All the "hard work" of editing templates and so on does not have to be repeated.

Installing a WireGuard Client in an Android Device toc

The first step in installing WireGuard in a Android device is to install the WireGuard Application from Google Play. Once that is done, launch the application.

Start screen of <span class=WireGuard on an Android tablet">

Click on the blue button as told.

Adding a <span class=WireGuard tunnel on an Android tablet">

Click on Create from QR code.

The above images were displayed on a tablet with a larger screen. The same steps should be performed on a phone, but the appearance will probably be different as shown below.

Start screen of <span class=WireGuard on an Android telephone">

Again, click on the + button to "Add a tunnel using the blue button" as displayed in the application window, and then select the SCAN FROM QR CODE in the menu that is displayed on the bottom part of the screen.

It is now time to display the QR code image on the Raspberry Pi hosting the WireGuard server.

pi@tarte:~/wg_config $ sudo ./user.sh -v nexus7

This time the two configuration files and the corresponding QR codes images will be displayed, but it will be necessary to scroll back to see them. Aim the device camera towards the QR code displayed on the desktop monitor. I started with the QR code for the client.conf file (with AllowedIPs = 192.168.99.1/32, 192.168.1.0/24). Once the information was acquired, the following dialog appears.

Naming and creating imported tunnel on an Android tablet

I named the tunnel "Rpi3-split" and then pressed on the CREATE TUNNEL button. I repeated the steps to add the second tunnel, named "RPi-all", from the second QR code.

Wireguard start screen with the two imported tunnels available

Again, the layout will be different on the smaller screen of a phone but functionally it is the same.

Three images corresponding to that last two except on an Android telephone

In the larger screen, the list of tunnels is always displayed in the left panel of the screen and the "public" information about each tunnel is displayed on the right panel as the tunnel is selected. In the smaller screen, either the list of tunnels is displayed or the public information for a single tunnel is displayed when it is selected. In the latter case, there is a backward-pointing arrow to go back to the list of tunnels.

Installing a WireGuard Client in Windows 10 toc

The release of an official WireGuard client for Windows was a welcomed development for many. It makes it just as easy to add WireGuard tunnels and activate them as the Android app shown above. First download the correct 32 or 64-bit version from the WireGuard Installation page. These are Windows Installer Packages, so a mouse click on the downloaded file is all that is required to start the installation. And that's basically it, once the installation is completed, the WireGuard icon is visible in the Windows Tray.

Wireguard Icon in Windows Tray

The first time the client is started by clicking on the icon, an empty list of tunnels is visible and there is an option to Import tunnel(s) from file.

Windows 10 client with no defined tunnels

That's what I did and I was rewarded with the following.

Windows 10 client confirming the importation of two tunnel definitions

PuTTY SFT client named PSFTP in Windows 10 menu Of course that raises the questions of where does the imported file come from? That's quite simple. I took the two client configuration files generated by the user.sh script, renamed them and then created a zip archive containing those files. There are doubtless many ways of doing this, here is how I went about it.

I downloaded and installed the latest version of PuTTY on a Windows 10 machine: Download PuTTY: latest release. Then I started its SFTP client PSFTP from the menu and used it to download the two client configuration files in ~/wg_config/users/winnner where a new user called "winner" were stored on the tarte system. The user was created with the user.sh script as explained twice over above.

psftp: no hostname specified; use "open host.name" to connect psftp> open 192.168.1.129 login as: pi pi@192.168.1.129's password: my_password not echoed to screen Remote working directory is /home/pi psftp> cd wg_config/users/winner Remote directory is now /home/pi/wg_config/users/winner psftp> lcd C:\Users\michel\Documents New local directory is C:\Users\michel\Documents psftp> get client.conf remote:/home/pi/wg_config/users/winner/client.conf => local:client.conf psftp> get client.all.conf remote:/home/pi/wg_config/users/winner/client.all.conf => local:client.all.conf psftp> exit

After that I renamed the configuration files to something more meaningful:

  client.conf      -->  tarte-split.conf
  client.all.conf  -->  tarte-all.conf

I then simply created a Zip archive named tarte.zip containing the two renamed configuration files. This is done in File Explorer by selecting the files and then clicking on the Zip icon in the Share ribbon and adjusting the name of the archive. This is the file I then selected to import in the WireGuard Window client.

Installing a WireGuard Client in Linux toc

To the best of my knowledge there is no such thing as a Wireguard client for Linux because, as stated several times already, the VPN is actually a peer-to-peer protocol. What we will be doing is installing WireGuard on a Linux host just as done on the Raspberry Pi above. The only significant difference will be in the configuration.

The Linux machine on which I installed WireGuard is a portable computer with Linux Mint 19.2 Xfce which I want to use to connect with my home network from remote locations. My first action was to update the system and then check to make sure that WireGuard was not already installed.

michel@tosh:~$ sudo apt update && sudo apt upgrade -y ... then after a reboot ... michel@tosh:~$ sudo apt-cache policy wireguard* wireguard-dkms: Installé : (aucun) Candidat : 1.0.20200611-1ubuntu1~18.04.1 Table de version : 1.0.20200611-1ubuntu1~18.04.1 500 500 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages 500 http://archive.ubuntu.com/ubuntu bionic-updates/universe i386 Packages wireguard-modules: Installé : (aucun) Candidat : (aucun) Table de version : wireguard-tools: Installé : (aucun) Candidat : 1.0.20200513-1~18.04.1 Table de version : 1.0.20200513-1~18.04.1 500 500 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages wireguard: Installé : (aucun) Candidat : 1.0.20200513-1~18.04.1 Table de version : 1.0.20200513-1~18.04.1 500 500 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages 500 http://archive.ubuntu.com/ubuntu bionic-updates/universe i386 Packages

This is not the most up-to-date version according to the Installation page but I nevertheless installed the package in the repository.

michel@tosh:~$ sudo apt install wireguard ...

The same tests done on the Raspberry Pi can be used to check that the modules and tools have been installed.

michel@tosh:~$ which wg wg-quick /usr/bin/wg /usr/bin/wg-quick michel@tosh:~ $ sudo touch /etc/wireguard/wg0.conf michel@tosh:~ $ sudo wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip link set mtu 1420 up dev wg0 michel@tosh:~$ lsmod | grep wire wireguard 221184 0 ip6_udp_tunnel 16384 1 wireguard udp_tunnel 16384 1 wireguard michel@tosh:~$ ifconfig wg0 ... wg0: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1420 unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ... michel@tosh:~$ sudo wg interface: wg0 listening port: 53133

Configuring this instance of WireGuard as a "client" could hardly be simpler. I used FileZila to copy the client.conf and client.all.conf configuration files from the Raspberry Pi /home/pi/wg_config/users/tosh directory. These files were created by the users.sh script as explained above. After that I renamed the configuration files to something more meaningful:

  client.conf      -->  tarte-split.conf
  client.all.conf  -->  tarte-all.conf

I then moved the configuration files to the /etc/wireguard directory and erased the empty wg0.conf file that was created to test the installation but which will not be used. I also made sure that root is the owner of the configuration files which is an added security measure.

michel@tosh:~$ sudo mv tarte-split.conf /etc/wiregard/ michel@tosh:~$ sudo mv tarte-all.conf /etc/wiregard/ michel@tosh:~$ rm /etc/wiregard/wg0.conf michel@tosh:~$ sudo chown -R root: /etc/wireguard

That's all there is to setting up a Linux client which is basically the same as what was done to configure the Windows client. There are three main differences with the server configuration.

Removing Users toc

The user.sh script can also be used to remove a single user

pi@tarte:~ $ cd wg_config pi@tarte:~/wg_config $ sudo ./user.sh -d nexus7

or all users at once.

pi@tarte:~ $ cd wg_config pi@tarte:~/wg_config $ sudo ./user.sh -c

Of course, the server configuration file will also be updated.

Using the WireGuard VPN Clients toc

Imagine the following scenario. I am sitting in a coffee shop, and I want to see the video feed from an IP camera at home. On the local network, I would start VLC and view the stream at the following address: rtsp://192.168.1.95/11. On my tablet, I can do exactly the same thing as long as I start the WireGuard application and open one of the tunnels to the VPN server at home and if the WireGuard VPN server is running on the local network at home.

Android client start screen

I just slide the wanted tunnel button to the right as shown above. On my Android phone the connection details are displayed by clicking on the tunnel name, but opening a tunnel would be done just the same, by sliding to the right the control beside the desired tunnel. As soon as that is done, I have access to all resources on my home network on 192.168.1.xxx just as if my Android device were connected directly to the LAN. I can therefore watch the rtsp://192.168.1.95/11 video stream as if I were home. It is so simple and yet secure. Anyone eavesdropping on the Wi-Fi network in the shop or anywhere along the route between my tablet and my home router would see IP packets with encrypted content. Instead of seeing the address 192.168.1.95:554 from which it could be surmised that there is an IP camera on my home network (554 is the typical RTSP port), the visible address will be 168.102.82.120:53133 which is the public IP address of the router and the obscure port used by the WireGuard interface which encodes everything else end-to-end, including the final destination address.

Note that the Android client gives very little feedback when opening a tunnel. A small key icon signifying the VPN is active will be shown at the top of the device screen. But that icon is present even if the settings are wrong or if the WireGuard server at home is not online. The only "symptom" that something is wrong will be that all devices on the 192.168.1.xxx subnet are unreachable and the WireGuard app will probably show that the number of received bytes is zero.

What if I decide to consult the latest stock market indices? I would start a web browser and go to the say Yahoo! Finance in Canada: https://ca.finance.yahoo.com/. The destination IP, 66.218.84.42, is not on the 192.168.1.xxx subnet so routing of the packets would not go through the WireGuard tunnel. Instead, packets will be routed directly as if WireGuard was not even running. Anyone eavesdropping on the Wi-Fi network may be able to follow the data sent and received by the Android device. Actually, that's exaggerated: addresses could be traced, but the actual data is encoded and should be almost impossible to crack.

If I then want to check my bank balance, I can either start a Web browser and establish a secure HTTPS connection with the bank's Web server or use the Google Play Store app provided by the bank. Either way, I am counting on the built-in encryption of the data exchanged to keep my password and the details of my finances private. However, being paranoid, before checking the balance, I usually start the other tunnel that I named rpi3-all or test-all where the Allowed IPs field is 0.0.0.0/0. That means all traffic in and out of my device is sent to my home network and from there it is routed to its final destination. So my outgoing financial data is double encrypted on the first leg of its journey out of the coffee shop and incoming data is also double encrypted on the last leg from my home network. Maybe I should wear a tin foil hat to protect myself from the nefarious 5G network at the same time because for most of the way, the data is transiting all sorts of bridges, routers, backbones and so on with no more and no less encryption than when I consult my bank balance from my desktop computer at home. Still I find it reassuring to use the "universal" WireGuard tunnel at all times when using a public hotspot. No harm is done, and there is no perceptible slow down even with the extra hop involved.

Of course, if you use a public hotspot in search of anonymity, don't use the Allowed IPs=0.0.0.0/0 configuration because you are in effect using your own ISP account. On the other hand, do not assume that a public hotspot provides true anonymity. The coffee shop server knows which IP was assigned to your computer and the MAC address of the network card of your computer and may very well save that type of information.

Using the Windows client is just as simple. Start WireGuard by clicking its icon in the system tray, and then select the desired tunnel in the list on the left.

Window client start screen

Click on the Activate button and if all goes well the VPN will be in place.

Activate <span class=WireGuard tunnel in Window client">

As one would assume, the Deactivate button closes an open tunnel.

Since there is no graphic WireGuard client for Linux, the command line the wg-quick tool to start and stop tunnels must be used to connect to the local area network from a remote location with the Linux Mint portable. Sometimes I am running many VPN servers for test purposes, so I often list all the installed configuration files before just to recall their exact name. Then starting a tunnel is quite easy as long as I remember the command and also remember not to include the .conf extension in the tunnel name.

michel@tosh:~$ sudo ls /etc/wireguard tarte-all.conf tarte-split.conf tarte-all.conf tarte-split.conf michel@tosh:~$ sudo wg-quick up tarte-all #] ip link add tarte-all type wireguard [#] wg setconf tarte-all /dev/fd/63 [#] ip -4 address add 192.168.99.5/24 dev tarte-all [#] ip link set mtu 1420 up dev tarte-all [#] wg set tarte-all fwmark 51820 [#] ip -4 route add 0.0.0.0/0 dev tarte-all table 51820 [#] ip -4 rule add not fwmark 51820 table 51820 [#] ip -4 rule add table main suppress_prefixlength 0 [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1 [#] iptables-restore -n

Closing the tunnel is just as easy, but you must use the correct tunnel name which, again, I often forget. Thankfully, wg shows the currently used tunnel name.

michel@tosh:~$ sudo wg interface: tarte-all ... michel@tosh:~$ sudo wg-quick down tarte-all [#] ip -4 rule delete table 51820 [#] ip -4 rule delete table main suppress_prefixlength 0 [#] ip link delete dev tarte-all [#] iptables-restore -n

Concluding Remarks toc

Once you have thoroughly tested everything, I suggest it is time to look at all ports that were being forwarded at the LAN firewall. I was able to remove all holes punched through it for the home automation system, for IP cameras, etc. and replace them with a single UDP port forwarded to the WireGuard service. Now there's a single hole in the firewall. Try it and you too may get a warm fuzzy feeling of security. Hopefully, I will not regret this in the future.

Some may wonder about the throughput of the VPN. I was surprised that the VPN performed adequately even when routing all Internet traffic through it. This was true when the VPN service was running on a single core Raspberry Pi 1 (similar to a Pi Zero). It is true that my bandwidth demands are usually relatively light when I am in a coffee shop. Nevertheless, YouTube videos could be streamed simultaneously on a tablet and portable without noticeable degradation. Of course these were not in high definition, but then I do not anticipate a pressing need to view 4K videos in coffee shops in the forseeable future.

I have found WireGuard to be very reliable and its use surprisingly seamless. That being said, I encountered a problem using the VPN. Many public access points block forwarding of UDP datagrams to most ports, and WireGuard uses UDP only. Three coffee chains with outlets across North America and beyond do not yet have such a restrictive policy, but in many institutional setting this is the case. It appears that a big well-known international fast food chain base in the USA also blocks UDP traffic. I wanted to take a closer look at this issue before physical access to restaurants was suspended due to the risks associated with the coronavirus. While restrictions have eased lately, I have yet to look into this problem.

<-WireGuard on Raspberry Pi OS (Buster)
<-Installing and Configuring WireGuard - All Posts