Let’s start with a description of my needs. I have two remote systems and I want to be able to connect to them both. Both systems are behind a standard NAT firewall (like a home router). And I want to be able to copy files between them easily. I am not a vpn or network whizz but know my way around IP addresses. I know that besides WireGuard there are more options, like OpenVPN, but I prefer an easy setup with enough security. So I got hacking the other day and found a few small pitfalls. To help others setting this up I decided to write a small “The complete guide to setting up a multi-peer WireGuard VPN network”
Getting started is the easy part. There are enough guides on the Internet by now on how to get some initial setup. The thing is, after following those directions you are probably only half way there.
So, let’s get started.
First, take a piece of paper and draw the network you want to setup. Draw all hosts, and assign them all a unique IP-address in a new network that you are not already using. So, in my case, I choose 10.10.1.0/24. This means that my network is 10.10.1 and the last digit is for each systems IP address.
Since both systems are behind a firewall this means I can not access them from the outside world. This also means I need to have at least one system in my network that is (accessible from the outside world). For this I choose to instantiate a very cheap cloud virtual machine at some supplier. This will be my vpn-router-vm system. All it will do is route all traffic within my 10.10.1.0/24 vpn network. Of course it has a public IP address that is visible to the outside world.
Now that I have decided on all the above I can assign IP addresses to my two systems. It makes common sense to assign 10.10.1.1 to my vpn-router-vm. This also means my other nodes will be 10.10.1.2 and 10.10.1.3.
I instantiate the vpn-router-vm, and choose Ubuntu 20.04 for the OS. I do an apt update and apt upgrade to make sure I am using the latest patches. I install the UFW firewall tool and make it only accessible over SSH from my home server. No need for script kiddies to do dictionary attacks, right?
# apt install ufw # ufw default deny incoming # ufw default allow outgoing # ufw allow from <my.home.ip.address> to any port 22 proto tcp comment 'ssh access from home'
Since this vpn-router-vm needs to be accessible from the outside world, the default port for WireGuard is 41194 and one of my systems does not have a fixed IP address I need to allow all WireGuard traffic:
# ufw allow 41194/udp
Now, on all our systems we are going to do exactly the same commands to install WireGuard, make a configuration directory, generate private key, and, based on this private key, a public key. So any system that connects with a public keys is checked whether or not this public key is based on our (hidden!) private key before it is allowed access.
# apt install wireguard # mkdir -m 0700 /etc/wireguard/ # cd /etc/wireguard # umask 077; wg genkey | tee privatekey | wg pubkey > publickey # cat privatekey # cat publickey
Okay, now, all our systems will get a new network interface with the name ‘wg0’. So we need to create a wg0.conf file in every /etc/wireguard directory. We will start with a skeleton configuration (yes, you will add stuff to this later, and for good reason) for our vpn-router-vm system. Note that in our interface definition we use /24 because we define our vpn network here.
## Set Up WireGuard VPN on Ubuntu By Editing/Creating wg0.conf File ## [Interface] ## My VPN server private IP address ## Address = 10.10.1.1/24 ## My VPN server port ## ListenPort = 41194 ## VPN server's private key i.e. /etc/wireguard/privatekey ## PrivateKey = private-key-of-von-router-vm [Peer] ## Desktop/client VPN public key ## PublicKey = public-key-of-my-first-peer-system ## client VPN IP address (note the /32 subnet) ## AllowedIPs = 10.10.1.2/32
On my first remote node I also create a wg0.conf file, but with slightly different contents:
[Interface] ## This Desktop/client's private key ## PrivateKey = my-systems-private-key ## Client ip address ## Address = 10.10.1.2/24 [Peer] ## Ubuntu 20.04 server public key ## PublicKey = the-public-key-of-my-vpn-router-vm ## set ACL ## AllowedIPs = 10.10.1.0/24 ## Your Ubuntu 20.04 LTS server's public IPv4/IPv6 address and port ## Endpoint = the-public-ip-address-of-my-vpn-router-vm:41194 ## Key connection alive ## # This is needed because we are behind NAT firewall PersistentKeepalive = 15
One thing to note in the text above is the last line. Since the system is behind a NAT firewall it is not accessible from the outside world. I like it that way. But it also means that this node has to ‘ping’ the vpn server from time to time.
Okay. All that is left now is to start WireGuard on the vpn-router-vm and on my first peer:
# systemctl enable wg-quick@wg0 # systemctl start wg-quick@wg0 # systemctl status wg-quick@wg0
The status should show something like/similar to this:
email@example.com - WireGuard via wg-quick(8) for wg0 Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; vendor preset: enabled) Active: active (exited) since Sat 2022-03-12 12:35:01 CET; 23h ago 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 Main PID: 1316620 (code=exited, status=0/SUCCESS) Tasks: 0 (limit: 38309) Memory: 0B CGroup: /system.slice/system-wg\firstname.lastname@example.org mrt 12 12:35:01 inzicht systemd: Starting WireGuard via wg-quick(8) for wg0... mrt 12 12:35:01 inzicht wg-quick: [#] ip link add wg0 type wireguard mrt 12 12:35:01 inzicht wg-quick: [#] wg setconf wg0 /dev/fd/63 mrt 12 12:35:01 inzicht wg-quick: [#] ip -4 address add 10.10.1.2/24 dev wg0 mrt 12 12:35:01 inzicht wg-quick: [#] ip link set mtu 1420 up dev wg0 mrt 12 12:35:01 inzicht systemd: Finished WireGuard via wg-quick(8) for wg0.
Assuming they are active and working properly on both systems you should now be able to ping one another:
# ping 10.10.1.1 PING 10.10.1.1 (10.10.1.1) 56(84) bytes of data. 64 bytes from 10.10.1.1: icmp_seq=1 ttl=64 time=13.3 ms 64 bytes from 10.10.1.1: icmp_seq=2 ttl=64 time=12.4 ms ^C --- 10.10.1.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 12.441/12.875/13.309/0.434 ms
All rejoice for you have a working VPN!! \0/. Unfortunately your not done yet. Sorry.
So add a second remote system to your vpn setup by adding another “peer” section to the wg0.conf file on the vpn-router-vm and configure the second remote system like you did before, taking care of course of the other peer’s private key.
If all goes according to plan then that server is capable of pinging your vpn-router-vm. Again, we all rejoice \o/.
Now try to ping one of the remote systems from the other remote system. I am guessing it doesn’t work. That’s a bummer but can easily be fixed. The thing is that usually a default Linux system does not automatically do forwarding of IP packets. To enable IP forwarding you need two command:
# cat /proc/sys/net/ipv4/ip_forward # <- probably this is zero/0 # sysctl -w net.ipv4.ip_forward=1 # store setting in startup config file # echo 1 > /proc/sys/net/ipv4/ip_forward # enable IP forwarding on running system
So, can you now ping the remote system from the other remote system? Yes, you can! Again, we all rejoice \o/. Surely you can now also ssh into a remote system from the other remote system? And, again, bummer, you can’t. Something is prohibiting access to the ssh port from the remote system. What can it be? Yes, the firewall on the vpn-router-vm system of course! So, you add a few lines to the wg0.conf on the vpn-router-vm to enable traffic to all ports in the vpn network (All credits to user ‘dddma’ on Reddit for this). Your wg0.conf file on the vpn-router-vm will now look like this (Both PostUp and PostDown are very long single lines!):
## Set Up WireGuard VPN on Ubuntu By Editing/Creating wg0.conf File ## [Interface] ## My VPN server private IP address ## AddreThe complete guide to setting up a multi peer WireGuard VPN networkss = 10.10.1.1/24 ## My VPN server port ## ListenPort = 41194 ## VPN server's private key i.e. /etc/wireguard/privatekey ## PrivateKey = private-key-of-von-router-vm #Allow forwarding of ports PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] ## Desktop/client VPN public key ## PublicKey = public-key-of-my-first-peer-system ## client VPN IP address (note the /32 subnet) ## AllowedIPs = 10.10.1.2/32 [Peer] ## Desktop/client VPN public key ## # dskdesk PublicKey = public-key-of-my-second-peer-system ## client VPN IP address (note the /32 subnet) ## AllowedIPs = 10.10.1.3/32
Don’t forget to do a “systemctl restart wg-quick@wg0” when you change a config file. Anyway, that’s it. Your done. Enjoy! I hope you enjoyed this “Complete guide to setting up a multi-peer WireGuard VPN network”. No likes needed. Have a nice day.