-
-
Notifications
You must be signed in to change notification settings - Fork 46
WireGuard VPN
WireGuard is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPSec while avoiding the massive headache. It intends to be considerably more performant than OpenVPN.
With older versions of Raspbian you would need to install the necessary packages before WireGuard setup begins. If you're running the latest version of the Raspberry Pi OS, we can skip all this and go straight to the sudo apt install wireguard
command (skip to Install WireGuard below). Otherwise, start here:
sudo apt install raspberrypi-kernel-headers libelf-dev libmnl-dev build-essential git
Next, install the Debian distribution keys (otherwise your apt update
will fail further down the line):
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7638D0442B90D010
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 04EE7237B7D453EC
Switch to root with sudo su
and enter the next 2 commands per the Debian installation commands. since WireGuard is not included in the Raspbian distribution, we'll use the Debian one instead:
echo "deb http://deb.debian.org/debian/ unstable main" > /etc/apt/sources.list.d/unstable.list
Then we'll prevent our Raspberry Pi from using the Debian distribution for normal Raspbian packages:
printf 'Package: *\nPin: release a=unstable\nPin-Priority: 90\n' > /etc/apt/preferences.d/limit-unstable
Then exit
root.
Update the package list:
sudo apt update
Then install dirmngr
for handling certificates if it isn't already installed (use which dirmngr
to check):
sudo apt install dirmngr
Now you can install the WireGuard package:
sudo apt install wireguard
And to enable IP forwarding, you'll need to uncomment the net.ipv4.ip_forward=1
line from your /etc/sysctl.conf
file:
sudo nano /etc/sysctl.conf
Or you can type the following command to handle this for you:
sudo perl -pi -e 's/#{1,}?net.ipv4.ip_forward ?= ?(0|1)/net.ipv4.ip_forward = 1/g' /etc/sysctl.conf
Finally, reboot your Raspberry Pi:
sudo reboot
After reboot, verify that IP forwarding is enabled by running:
sysctl net.ipv4.ip_forward
You should see net.ipv4.ip_forward = 1
as a result, otherwise add the above command to your /etc/sysctl.conf
file.
In the next steps, we'll need to create private and public keys for both the WireGuard server as well as a VPN client. Once everything is set up, we can create additional keys for other clients to use the VPN as well.
I've found it easiest to first become root before running the commands below:
sudo su
Then switch to the directory where we'll store the WireGuard keys:
cd /etc/wireguard
If this directory doesn't exist, just run mkdir /etc/wireguard
and then cd /etc/wireguard
. Set permissions on the entire directory with the umask
command so that only the root
user can read or write data here:
umask 077
Next, generate the server’s private & public keys in a single command:
wg genkey | tee server_privatekey | wg pubkey > server_publickey
Then generate a client’s private & public keys:
wg genkey | tee peer1_privatekey | wg pubkey > peer1_publickey
To confirm the keys were generated and have the correct file permissions:
ls -la
which should display something similar to:
total 24
drwx------ 2 root root 4096 Feb 26 13:28 .
drwxr-xr-x 91 root root 4096 Feb 26 13:26 ..
-rw------- 1 root root 45 Feb 26 13:28 peer1_privatekey
-rw------- 1 root root 45 Feb 26 13:28 peer1_publickey
-rw------- 1 root root 45 Feb 26 13:28 server_privatekey
-rw------- 1 root root 45 Feb 26 13:28 server_publickey
Finally, output your new WireGuard keys to the console and save them (somewhere safe, otherwise be sure and delete them when we're done here) for the next steps:
cat server_privatekey
cat server_publickey
cat peer1_privatekey
cat peer1_publickey
Lastly, exit
root before continuing.
Create and edit the WireGuard configuration file:
sudo nano /etc/wireguard/wg0.conf
and replace the contents with the WireGuard wg0.conf from this repository.
This is the WireGuard interface, which will create a virtual subnet of 10.9.0.0
and assign itself an internal IP address of 10.9.0.1
. You can change this if you'd like, but you'll also need to change the internal IP of VPN clients as well.
[Interface]
Address = 10.9.0.1/24
The default port for WireGuard, which you can change if you'd like. You'll also need to open up this port on your router, otherwise incoming VPN traffic from outside your network will not make it to WireGuard. Information on how to do this is later in the guide.
# Default WireGuard port, change to anything that doesn’t conflict
ListenPort = 51820
Note: Some public wifi networks will block all ports other than 80
(TCP), 443
(TCP), and 53
(UDP) for HTTP, HTTPS, and DNS respectively. If you are connected to a public wifi network that does this, you will not be able to connect to your WireGuard VPN. One way around this is to set your WireGuard ListenPort
to 53
and create a forward on your network's router on port 53
, thus circumventing the issue with blocked ports. Do this at your own risk, and definitely, do not enable Pi-hole's Listen on all interfaces, permit all origins DNS option if you are forwarding port 53
on your router.
Replace 192.168.x.x
with the static IP address of your Raspberry Pi:
DNS = 192.168.x.x
Replace <server_privatekey>
with the output of your cat server_privatekey
command earlier:
PrivateKey = <server_privatekey>
We're using eth0
here when the Raspberry Pi is connected over ethernet (wired), but you can replace both instances with wlan0
if your Raspberry Pi is connected via wifi (wireless):
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
The next section of the WireGuard configuration file is for clients that connect to the VPN. For each client (device), you'll need to add another [Peer]
section here and also create a separate client configuration file (details for that are next).
Replace <peer1_publickey>
with the output of your cat peer1_publickey
command earlier:
[Peer]
# Peer 1
PublicKey = <peer1_publickey>
Using the virtual subnet created by WireGuard, give this device an internal IP address of 10.9.0.2
:
AllowedIPs = 10.9.0.2/32
Once your WireGuard configuration file is complete, exit the nano
editor and save your changes.
Now that WireGuard is configured, we'll need to create a client configuration file for each VPN client we want to connect to the network. First, create and edit your first client configuration file:
sudo nano /etc/wireguard/peer1.conf
and replace the contents with the WireGuard client peer1.conf from this repository.
Use the same virtual IP address that you used in the wg0.conf
file earlier for this client:
[Interface]
Address = 10.9.0.2/32
Replace 192.168.x.x
with the static IP address of your Raspberry Pi:
DNS = 192.168.x.x
Replace <peer1_privatekey>
with the output of your cat peer1_privatekey
command earlier:
PrivateKey = <peer1_privatekey>
Replace <server_publickey>
with the output of your cat server_publickey
command earlier:
[Peer]
PublicKey = <server_publickey>
The Endpoint
here refers to the public IP address and port number for incoming traffic to your network's router from the outside world. This is necessary so that devices outside your network know where to go to connect to your internal network's VPN. Your public IP address is available by visiting IP Leak, and the ListenPort
should be set to the same port you set in your WireGuard's wg0.conf
file (the default port is 51820
).
So you can edit this line:
Endpoint = YOUR-PUBLIC-IP/DDNS:ListenPort
To include your public IP address and the WireGuard port you set previously:
Endpoint = 11.22.33.44:51820
For this use case, we're using a full tunnel rather than a split tunnel (which allows some network traffic to come through outside of the VPN).
# For full tunnel use 0.0.0.0/0, ::/0 and for split tunnel use 192.168.1.0/24
# or whatever your router’s subnet is
AllowedIPs = 0.0.0.0/0, ::/0
Exit the nano
editor and save the configuration file.
If your ISP does not provide you with a static IP address (most don’t), and your IP changes for some reason (cable modem reboot, connectivity issues, etc.), your home network may be unreachable from the outside until you update it in your configuration files. The solution is to use a DDNS (Dynamic DNS) service where you choose a readable domain name and can automatically notify the service when your public IP address changes.
So instead of worry about whether your public IP address is 98.10.200.11
or 98.10.200.42
, you can instead point a domain name like username.us.to
at your public IP address and have the DDNS service update the domain record when your public IP address changes.
There are plenty of DDNS services out there, but I’m using Afraid.org’s Free DNS service because it doesn’t nag you to login every 30 days, even on the completely free plan (it does however mark your account as "dormant" after 6 months without a login, but it's easy to resume your account without upgrading).
First, create a Free DNS account at https://freedns.afraid.org/.
After account creation, verify your account using the link you’ll receive in your email. Then go to the Subdomains page, click Add a subdomain. Enter a Subdomain (a username, your name, company name, etc.) and choose a Domain (there are many to choose from besides what’s listed, follow the instructions to find the rest). Leave Type set to an A record, TTL is set to 3600 for free accounts, and Wildcard functionality is only for paid accounts.
Your public IP address should be automatically filled in, but you can visit IP Leak in a browser to get your public IP address if you need to.
Enter the CAPTCHA text and hit Save to continue. You should now have a shiny new subdomain pointing to your network’s public IP address!
Having a domain name pointed to your public IP address is useless if that IP address changes in the future, which is why DDNS services exist in the first place. We'll need to update our Free DNS record if our network's public IP address changes, which is fairly simple to do.
While logged in at Free DNS, go to the Dynamic DNS page and look for your new subdomain record. If you're using version 2 of the Dynamic Update Interface, you'll need to enable Dynamic DNS for your new subdomain first.
Once enabled, copy the sync.afraid.org
URL associated with your subdomain record. We'll use this to update your subdomain record directly from the Raspberry Pi.
On the Raspberry Pi, create a cronjob with:
crontab -e
and choose an editor (nano is usually the default). In this default crontab file, add the following to a new line:
*/15 * * * * curl http://sync.afraid.org/u/XXXXXXXXX/
Replacing the XXXXXXXXX with the unique identifier in your custom URL. You can change the timing from 15 minutes to 5 minutes (or whatever you'd like) by adjusting the */15 * * * *
part to */5 * * * *
. Then save the crontab file and exit nano.
Verify that you’ve added the cronjob correctly with:
crontab -l
Once you've finished, restart the cron service with:
sudo service cron restart
Now your DDNS subdomain will always point to the correct public IP address of your network, and VPN clients will be able to reach your network remotely regardless of whether your public IP address changes.
Go back to your WireGuard client configuration file and use your new DDNS subdomain with the ListenPort
you set earlier and never worry about your public IP address changing! In the /etc/wireguard/peer1.conf
file, edit the Endpoint
:
Endpoint = subdomain.us.to:51820
using the subdomain/domain you chose on Free DNS and the ListenPort
you set in your /etc/wireguard/wg0.conf
file.
Unlike IPSec or IKEv2, WireGuard isn’t built into the Android or iOS operating system (yet), so you’ll have to download the WireGuard app to each device to connect to your VPN. Here are some of the VPN clients available:
- Android: https://play.google.com/store/apps/details?id=com.wireguard.android&hl=en_US
- iOS: https://apps.apple.com/us/app/wireguard/id1441195209
- macOS: https://apps.apple.com/us/app/wireguard/id1451685025?mt=12
Rather than manually enter all the WireGuard configuration details into your phone, we can create a QR code directly on the Raspberry Pi console that your phone's native WireGuard app can scan and automatically fill out the details for you.
First, install the QR encoder on the Raspberry Pi:
sudo apt install qrencode
Become the root user in order to read the WireGuard client config:
sudo su
Create a QR code from the VPN client configuration we set up earlier:
qrencode -t ansiutf8 < /etc/wireguard/peer1.conf
Note: You may have to adjust the size of your terminal/console window to properly show the QR code generated
Then exit
to return to your pi
user.
Open the WireGuard app on your phone, tap Add a tunnel and select the Create from QR code option. Scan the QR code with your phone’s camera, give the tunnel a name, and allow WireGuard to add VPN configurations to your phone's operating system.
Now you can enable or disable VPN access directly through the WireGuard app!
On your Raspberry Pi, there are a few more steps needed to complete the setup of the WireGuard VPN. First, allow WireGuard to start on boot:
sudo systemctl enable wg-quick@wg0
Set the correct permissions on the WireGuard configuration file with:
sudo chown -R root:root /etc/wireguard/
sudo chmod -R og-rwx /etc/wireguard/
On your Pi-hole Web Interface, go to Settings > DNS and choose the Listen on all interfaces, permit all origins option under Interface listening behavior, then save your settings.
Start WireGuard now with:
sudo wg-quick up wg0
To check and see if the WireGuard interface was created successfully:
ifconfig wg0
You should see that 10.9.0.1
internal IP address we created in the wg0.conf
file, along with some other flags.
Restart your Raspberry Pi with:
sudo reboot
Once the Raspberry Pi is done booting, check if WireGuard working:
sudo wg
You should see interface wg0
and a peer:
interface: wg0
public key: XXXXXXXXXXXXXXXXXXXXX
private key: (hidden)
listening port: 51820
peer: XXXXXXXXXXXXXXXXXXXXX
allowed ips: 10.9.0.2/32
Without this step, you will not be able to reach your VPN from outside of the network. None of the write-ups I found mentioned this step, most likely assuming you would already know to do this (I didn't).
In order to reach your VPN from outside your network, you'll have to set up a UDP-specific port forward on your network's router that passes traffic on that port to an internal IP address of your choosing. Generally speaking, this might look like:
Description: WireGuard VPN
Public UDP Ports: 51820
Private IP Address: 192.168.x.x
Private UDP Ports: 51820
Where 192.168.x.x
is the internal static IP address of the Raspberry Pi running WireGuard, and 51820
is whatever you set the ListenPort
to in your /etc/wireguard/wg0.conf
file. Each router has different settings available, but in the Eero settings it looks like this for my Raspberry Pi's static IP:
Note: We're passing only UDP traffic on this port, as WireGuard uses UDP and not TCP.
Once you've added this port forwarding on your network's router, restart the device and now you should be able to connect to your WireGuard VPN from outside your network and enjoy the benefits of network-level ad-blocking from anywhere, at any time!
When connected to your VPN from outside the network, you can check to see if there are any leaks in your DNS lookups using the DNS Leak Test service.
There are several write-ups out there on how to do this, as well as install scripts to do it for you. Since the Raspberry Pi was meant to be a learning tool, I used this opportunity to figure things out on my own with the help of documentation from both software creators and the community. If it weren't for the latter, I doubt I would've been able to do this on my own. Thanks to everyone who has taken the time to share their knowledge, and experience, in setting up a Raspberry Pi.
-
Build Your Own WireGuard VPN Server with Pi-hole for DNS Level Ad Blocking
Seth Enoka's write-up includes some additional firewall setup with IPtables, which I skipped. But this is an excellent reference for what we're doing, even though he uses a VPS with Ubuntu rather than a Raspberry Pi. -
Raspbian GNU/Linux 10 (buster) Lite
Harry Nyce posted his Pi experiments on Reddit, which prompted me to start this endeavor. I found his notes helpful, though the process was confusing at times. -
AdBlocking VPN Proxy Server (Pi-hole, Wireguard, Privoxy, Unbound)
Richard Crosby's overview here is great, though missing some of the details. He's also adding a VPN proxy server, which I decided to skip. -
Set up Pi-hole as truly self-contained DNS resolver
Anudeep's setup of Unbound to work with Pi-hole was extremely helpful to understand how the two work together. -
Unbound: How to enable DNSSEC
NLnet Labs explains what DNSSEC is and how to enable it in Unbound. -
Easy As Pi Installer
Shane Caler's "one-stop-shop" to set up WireGuard, Pi-hole, and Unbound on a Raspberry Pi. I didn't have a chance to try this out, but it might be a nice replacement for all of this at some point (and it's also probably a good place to learn). -
WireGuard Installation (Raspberry Pi 2 v1.2 and above)
Adrian Mihalko's excellent instructions on installing WireGuard on a Raspberry Pi. -
Some Unofficial WireGuard Documentation
Unofficial, but hugely helpful, documentation on WireGuard. -
How to Set Up WireGuard on a Raspberry Pi
Step-by-step instructions on getting WireGuard working on a Raspberry Pi. Matches what I had already written, but I cleaned up a bit of my own documentation thanks to this article.