Installation

sudo apt update
sudo apt install wireguard
sudo apt install qrencode

Server Set Up

Generate Keys, Set Perms

wg genkey | sudo tee /etc/wireguard/private.key
sudo chmod go= /etc/wireguard/private.key

sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key

Enable Internet via VPN

Configure internet forwarding via VPN sudo nano /etc/sysctl.conf then add below code to bottom of configuration:

net.ipv4.ip_forward=1

check and confirm sudo sysctl -p

Setup Config with Firewall Rules

Open server configuration file and edit it via sudo nano /etc/wireguard/wg0.conf

[Interface]
Address = 10.8.0.1/24
SaveConfig = true
PostUp = ufw route allow in on wg0 out on eno1
PostUp = iptables -t nat -I POSTROUTING -o eno1 -j MASQUERADE
PostUp = iptables -I FORWARD -i wg0 -o wg0 -j REJECT
PostUp = iptables -I FORWARD -i wg0 -s 10.8.0.101/32 -d 10.8.0.0/24 -j ACCEPT
PreDown = ufw route delete allow in on wg0 out on eno1
PreDown = iptables -t nat -D POSTROUTING -o eno1 -j MASQUERADE
PreDown = iptables -D FORWARD -i wg0 -o wg0 -j REJECT
PreDown = iptables -D FORWARD -i wg0 -s 10.8.0.101/32 -d 10.8.0.0/24 -j ACCEPT
ListenPort = 51820
PrivateKey = <INSERT YOUR SERVER PRIVATE KEY FROM private.key file>

Notes: iptables -I FORWARD -i wg0 -o wg0 -j REJECT prevents child nodes from seeing each other whereas iptables -I FORWARD -i wg0 -s 10.8.0.101/32 -d 10.8.0.0/24 -j ACCEPT specifically whitelists .101 node to be able to view other nodes. -D is just the inverse (delete) during shutdown. Also, need to check at client side’s firewall in case if it blocks all traffic by default - then add an entry to grant permission to remote IP (i.e. 10.8.0.199) as needed.

Firewall Port Forwarding

Allow Wireguard traffic through your selected port (default 51820) and restart firewall

sudo ufw allow 51820/udp
sudo ufw disable
sudo ufw enable
sudo ufw status

Setup Wireguard as systemd service via wg-quick

sudo systemctl enable wg-quick@wg0.service
sudo systemctl start wg-quick@wg0.service
sudo systemctl status wg-quick@wg0.service

note: wg0 is the service name which should correspond to my wg0.conf file name

Client Set Up (iOS)

Generate client keys

First, on the server side, prep the server public key via sudo wg show wg0

Generate client keys from the server and print it to check.

(umask 077 && wg genkey > wg-private-client-1.key)
wg pubkey < wg-private-client-1.key > wg-public-client-1.key
cat wg-private-client-1.key && cat wg-public-client-1.key

Create client config

create a client config file e.g. nano ~/wg-client-1.conf

# define the local WireGuard interface (client)
[Interface]

# contents of wg-private-client.key
PrivateKey = <INSERT YOUR CLIENT PRIVATE KEY>

# the IP address of this client on the WireGuard network
Address=10.8.0.101/32

# define the remote WireGuard interface (server)

# DNS Resolver by our internal DNS server/router's IP address (e.g. 192.168.1.1)
DNS=<INSERT DNS IP>

[Peer]

# from `sudo wg show wg0 public-key`
PublicKey = <INSERT YOUR SERVER PUBLIC KEY>

# the IP address of the server on the WireGuard network
AllowedIPs = 0.0.0.0/0, 192.168.0.0/24

# public IP address and port of the WireGuard server (e.g. dns.example.com:51820)
Endpoint = <INSERT YOUR PUBLIC SERVER DOMAIN:PORT>

# Behind NAT and to keep connection alive
PersistentKeepalive = 25

Add client to Server’s peer list

Print client’s public key via cat wg-public-client-1.key

Edit server configuration file via sudo nano /etc/wireguard/wg0.conf and add [Peer] section at the bottom

[Peer]
PublicKey = <INSERT YOUR CLIENT PUBLIC KEY>
AllowedIPs = <DESIRED WIREGUARD CLIENT IP ADDRESS WITHIN SAME SUBNET & SPECIFIED IN CLIENT FILE e.g. 10.8.0.101/32>

Sync latest config change by restarting service.

echo "My VPN QR Code" && qrencode --read-from=wg-client-1.conf --type=UTF8

Automation with script

Simplified deployment of client so you don’t have to do it manually every time

Create script

Create the file i.e. nano script.sh

Modify Permission to make it executable

Run chmod +x script.sh

Open the script

Run nano script.sh and edit it, add below code, and modify it accordingly as needed

#!/bin/bash

while true; do
	read -p "Enter the user's name: " vpnusername
	read -p "Enter the device's type: " vpndevicetype
    read -p "Enter a number (0-255): " num
    if [[ $num =~ ^[0-9]+$ && $num -ge 0 && $num -le 255 ]]; then
        echo "Valid input: $num"
		
		# Generate the Keys
		(umask 077 && wg genkey > wg-private-client-$num.key)
		wg pubkey < wg-private-client-$num.key > wg-public-client-$num.key
		cat wg-private-client-$num.key && cat wg-public-client-$num.key
		
		# Generate text file with predefined text
		cat <<EOF > "wg-client-$num.conf"
#############################################
##   $vpnusername $vpndevicetype   
#############################################
# define the local WireGuard interface (client)
[Interface]

# contents of wg-private-client.key
PrivateKey = $(<"wg-private-client-$num.key")

# the IP address of this client on the WireGuard network
Address=10.8.0.$num/32

# define the remote WireGuard interface (server)

# DNS Resolver by our internal DNS server/router's IP address (e.g. 192.168.1.1)
DNS=<INSERT DNS IP>

[Peer]

# from `sudo wg show wg0 public-key`
PublicKey = <INSERT YOUR SERVER PUBLIC KEY>

# the IP address of the server on the WireGuard network
AllowedIPs = 0.0.0.0/0, 192.168.0.0/24

# public IP address and port of the WireGuard server (e.g. dns.example.com:51820)
Endpoint = <INSERT YOUR PUBLIC SERVER DOMAIN:PORT>

# Behind NAT and to keep connection alive
PersistentKeepalive = 25
EOF
		
		echo "File 'wg-client-$num.conf' has been saved."
		
		
		# Update Server Config?
		echo "Edit server configuration file via sudo nano /etc/wireguard/wg0.conf and add [Peer] section at the bottom"

		# Output the lines in a nice format
        cat <<EOF

[Peer]
PublicKey = $(<"wg-public-client-$num.key")
AllowedIPs = 10.8.0.$num/32

EOF
		
		while true; do
			read -p "Do you want me to add it for you? (Y/N): " choice
			if [[ $choice == "Y" || $choice == "y" ]]; then
				# Backup /etc/wireguard/wg0.conf
				sudo cp /etc/wireguard/wg0.conf ~/backups/server/wg0.bak.$(date +%Y%m%d%H%M%S)

				# Shut Down WG0 interface
				sudo wg-quick down wg0

				# Edit /etc/wireguard/wg0.conf
				sudo bash -c "cat <<EOF >> /etc/wireguard/wg0.conf

[Peer]
PublicKey = $(<"wg-public-client-$num.key")
AllowedIPs = 10.8.0.$num/32
EOF"

				echo "File /etc/wireguard/wg0.conf has been updated."
				
				# Start WG0 interface
				sudo wg-quick up wg0

				break
			elif [[ $choice == "N" || $choice == "n" ]]; then
				echo "Skipping Server Config Edit..."
				break
			else
				echo "Please enter Y or N."
			fi
		done
		
		# Generate QR Code?
		echo "To simplify setup on mobile, I recommend you to Print Client QR so that you can scan it"

		while true; do
			read -p "Do you want me to generate QR Code? (Y/N): " choice
			if [[ $choice == "Y" || $choice == "y" ]]; then
				echo "QR Code for $vpnusername $vpndevicetype" && qrencode --read-from=wg-client-$num.conf --type=UTF8
				break
			elif [[ $choice == "N" || $choice == "n" ]]; then
				echo "Skipping QR Code..."
				break
			else
				echo "Please enter Y or N."
			fi
		done
		
		# End
		echo "We're at the end of program! Terminating..."
		
        break
    else
        echo "Invalid input. Please enter a number within the range of 0-255."
    fi
done

Run the Script

./script.sh

Performance Benchmark Comparisons

Install wireguard-tools and iperf3

sudo apt install wireguard-tools
sudo apt install iperf3

Download package. For a GitHub repo, there’s a zip at https://github.com///archive/.zip, so you can download it and then unzip it.

wget https://github.com/cyyself/wg-bench/archive/master.zip

Once ready, just run three codes below in the extracted folder containing scripts, one at a time:

sudo ./setup-netns.sh
sudo ./benchmark.sh
sudo ./clean-up.sh

Results:

Connecting to host XXX, port YYY
[  5] local AAA port BBB connected to XXX port YYY
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   533 MBytes  4.47 Gbits/sec    0   1.13 MBytes
[  5]   1.00-2.00   sec   528 MBytes  4.43 Gbits/sec    0   1.30 MBytes
[  5]   2.00-3.00   sec   528 MBytes  4.42 Gbits/sec    0   1.30 MBytes
[  5]   3.00-4.00   sec   526 MBytes  4.41 Gbits/sec    0   1.45 MBytes
[  5]   4.00-5.00   sec   526 MBytes  4.41 Gbits/sec    0   1.52 MBytes
[  5]   5.00-6.00   sec   526 MBytes  4.41 Gbits/sec    0   1.52 MBytes
[  5]   6.00-7.00   sec   528 MBytes  4.42 Gbits/sec    0   1.52 MBytes
[  5]   7.00-8.00   sec   529 MBytes  4.44 Gbits/sec    0   1.52 MBytes
[  5]   8.00-9.00   sec   530 MBytes  4.45 Gbits/sec    0   1.52 MBytes
[  5]   9.00-10.00  sec   525 MBytes  4.40 Gbits/sec    0   1.60 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  5.15 GBytes  4.43 Gbits/sec    0             sender
[  5]   0.00-10.04  sec  5.15 GBytes  4.41 Gbits/sec                  receiver

iperf Done.

Remember to clean up using above script.

Quick Commands

Start/stop interface

sudo wg-quick up wg0
sudo wg-quick down wg0

Check Status

sudo wg show

Start/stop service (preferred)

sudo systemctl stop wg-quick@wg0.service
sudo systemctl start wg-quick@wg0.service
sudo systemctl status wg-quick@wg0.service

References

  • https://www.digitalocean.com/community/tutorials/how-to-set-up-wireguard-on-ubuntu-22-04#step-4-adjusting-the-wireguard-server-s-network-configuration
  • https://wireguard.how/client/ios/
  • https://gist.github.com/chrisswanda/88ade75fc463dcf964c6411d1e9b20f4
  • https://www.wireguardconfig.com
  • https://github.com/cyyself/wg-bench
  • https://www.lautenbacher.io/en/lamp-en/wireguard-prohibit-communication-between-clients-client-isolation/