VPN with WireGuard
Here is another use case for an old PC: create your self-hosted VPN!
For me, a self-hosted VPN has two main use cases:
-
to be able to access my devices that are otherwise not accessible from the internet
-
to be able to access regionally restricted websites when I am temporarily somewhere else
Those are (more or less) my step-by-step notes for installing and configuring WireGuard as a VPN on a Debian machine.
Install Debian on the Raspberry
While this is a perfect job for an old PC, I used a Raspberry.
The main reason is that since I wanted to place the device with WireGuard in another country, I needed to set it up on a different device. The Raspberry, even if it does not have a lot of computational power, has enough resources for handling this task.
Thus the machine running WireGuard must be located somewhere else, and I needed to use a different machine from the one I’m already using.
To setup the raspberry, download the desired image, as described on the wiki:
wget https://raspi.debian.net/daily/raspi_${RPI_MODEL}_${DEBIAN_RELEASE}.img.xz.sha256
wget https://raspi.debian.net/daily/raspi_${RPI_MODEL}_${DEBIAN_RELEASE}.img.xz
sha256sum -c raspi_${RPI_MODEL}_${DEBIAN_RELEASE}.img.xz.sha256
# if sha256sum reports success...
xzcat raspi_${RPI_MODEL}_${DEBIAN_RELEASE}.img.xz | sudo dd of=${SD_CARD} bs=64k oflag=dsync status=progress
Insert the SD card in the Raspberry, attach it to a monitor, ethernet cable, and keyboard, and power it on.
Except for adding a normal user, nothing should be specific to the Raspberry.
From a shell with administrative rights:
# add a normal user, in this case named "urpi"
adduser urpi
usermod -aG sudo urpi
# install some useful packages for working remotely
apt update
apt install tmux sudo
# get ip address of the machine
ip address show
From my PC
# add entry in .ssh/config for the Raspberry, set up the ssh key, and so on
ssh rpi
# at this point, it is possible to detach the Raspberry from the monitor and keyboard
# just leave the power and ethernet cable and work from a more comfortable device through SSH
sudo tmux
# start upgrade process in one pane/window
apt upgrade
# change hostname from a different tmux pane
hostnamectl set-hostname rpi
# also edit value in /etc/hosts
# 127.0.1.1 rpi
# eventually do other tasks while updates are installed, it will take a while on the Raspberry
Some hardening
Contrary to my other other devices, this one will be directly accessible from the internet.
Thus it would be nice if "bad guys" are not simply able to connect to it.
Most GNU/Linux distributions are pretty robust in their default configuration, but there are some things that can be improved.
SSH
Enable ssh
access only with a key, disable root login, and use an alternate port:
# disable login methods that are not key-based
ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no
# disable login as root
PermitRootLogin no
# use a non-standard port
Port <something different than 22>
On many Linux systems accessing as root via SSH is not possible, the Raspberry image is a notable exception.
I also enabled logging; it can be used to verify if there are multiple authentication attempts.
You need to apt install rsyslog
, otherwise /var/log/auth.log
will not even be present.
rsyslog
is normally already available, but it might be if you have a minimal system.
root
account
If you already use sudo
or some alternate mechanism (like doas
) for administrative tasks, then it is possible to "disable" the root
account
# with admin rights
passwd -dl root
passwords
Use a stronger password. Ideally a passphrase; pick a book from your library and copy a random sentence, even better if not in English: "Hermione rimase in infermeria per diverse settimane." It does not have to be something memorable, you have a password manager for that, but it should be easy to type, dictate, and so on. Eventually add some numbers, exclamation marks, or other symbols.
passwd
Security updates
Contrary to most other machines, this one will be running even when I’m not using it actively, and I might not access it regularly. At least it is possible to install security updates automatically without too much effort.
apt install unattended-upgrades
# test with
unattended-upgrades --dry-run --debug
# verify if service is running
systemctl status unattended-upgrades
Verify open ports
The output of lsof -i -P
or netstat -tulpen
should enlist all currently open ports and applications listening on them.
Consider also installing a firewall manager (apt install ufw
), close all ports except those that are needed, and use it for monitoring:
ufw disable
ufw default deny incoming
ufw allow 50022/tcp comment 'Open port for ssh'
ufw allow 51820/udp comment 'Open port for wireguard'
ufw enable
ufw status verbose
There is also the possibility to rate limit the connections, for example with.
ufw limit 50022/tcp comment 'port rate limit for ssh'
Setup WireGuard
Finally, the most important piece of this note.
WireGuard uses keys as authentication mechanism, the following snippets will help to configure two machines.
You can otherwise use the Wireguard Config Generator to create the necessary keys and configuration files.
# add entries to local.conf if you want to use WireGuard as vpn for
# accessing internet
# if ouy only want to use as vpn for accessing local machines, then
# those changes are not required
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.d/local.conf
echo 'net.ipv6.conf.all.forwarding=1' >> /etc/sysctl.d/local.conf
sysctl -p /etc/sysctl.d/local.conf
# install wireguard
apt install wireguard openresolv
# generate keys
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey | tee /etc/wireguard/server_public.key
# create config file
# note that it needs the public key of the client, which is generated in the
# snippet after this one
printf '[Interface]
PrivateKey = %s
Address = 10.10.0.1/24
ListenPort = 51820
#DNS = <optional dns entry>
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 # Add forwarding when VPN is started
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 # Remove forwarding when VPN is shutdown
[Peer]
PublicKey = %s
AllowedIPs = 10.10.0.2/32
' "$(cat /etc/wireguard/server_private.key)" "$(cat /etc/wireguard/client_public.key)" > /etc/wireguard/wg0.conf
# start and verify wireguard service
systemctl enable wg-quick@wg0.service
systemctl status wg-quick@wg0
systemctl start wg-quick@wg0.service
systemctl status wg-quick@wg0
apt install wireguard openresolv
# generate keys, I do not think they need to be there
wg genkey | tee /etc/wireguard/client_private.key | wg pubkey | tee /etc/wireguard/client_public.key
# ip address of RPI
server_address_and_port="12.34.56.78:51820"
printf '[Interface]
Address = 10.10.0.2/32
PrivateKey = %s
#DNS = <optional dns entry>
[Peer]
PublicKey = %s
# forward everything
AllowedIps = 0.0.0.0/0, ::/0
Endpoint = %s
PersistentKeepalive = 25
' "$(cat /etc/wireguard/client_private.key)" "$(cat /etc/wireguard/server_public.key)" "$server_address_and_port" > /etc/wireguard/wg0-client.conf
openresolv
is not a dependency of wireguard, but required if you provide a DNS entry, otherwise you’ll get /usr/bin/wg-quick: line 31: resolvconf: command not found
as error.
Once everything is setup up, you can connect with
wg-quick up wg0-client
wg show
#open webpage in browser that shows ip address, ping hangs
wg-quick down wg0-client
Access WireGuard from a different network
Unless your ISP garantees a static IP (most do not), you will need a Dynamic DNS
The idea is to have a fixed name in you configuration file on your computer:
[Interface]
Address = 10.10.0.2/32
PrivateKey = <client private key>
[Peer]
PublicKey = <server public key>
AllowedIps = 10.10.0.1/32
# dynamic dns
Endpoint = <ddns>:51820
PersistentKeepalive = 25
The raspberry should configure a website to redirect everything on it’s ip address.
In most cases, it should be not more complicated than to load a page with curl periodically (thus to create a cron job), if you are required to execute a binary blob or something complex, you might want to look for alternatives.
Do not forward everything to the VPN
Sometimes I do not want to forward everything, but just want to connect the Raspberry with ssh
and verify if everything is OK or do some mantainance tasks.
In such cases, it is sufficient to forward the traffic only to a specific IP address:
[Interface]
Address = 10.10.0.2/32
PrivateKey = <client private key>
[Peer]
PublicKey = <server public key>
# 10.10.0.1 is the Interface address in wireguard of the raspberry
AllowedIps = 10.10.0.1/32
Endpoint = <ddns>:51820
PersistentKeepalive = 25
and enable/disable the VPN with wg-quick up wg0-client-ssh
/wg-quick down wg0-client-ssh
.
Enable logging
It is possible to enable logging in WireGuard through the following commands.
At runtime:
# with admin rights
modprobe wireguard
echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
At boot time:
# with admin rights
echo "wireguard" > /etc/modules-load.d/wireguard.conf
echo "options wireguard dyndbg=+p" > /etc/modprobe.d/wireguard.conf
Once configured, you can inspect the logs with
# with admin rights
journalctl --catalog --grep='wireguard*'
# or
dmesg --human --ctime --color=always | grep -i wireguard
Conclusion
That was it.
The only thing left to do is to place the Raspberry at the new location, connect it with an ethernet cable to the router, enable UDP port forwarding on the router, and you are ready to connect with wg-quick up wg0-client
from a different country.
Currently, I’m mainly interested in accessing regional restricted resources, but this setup opens new use cases for reusing older devices.
Note 📝 | I also tried using an Android phone instead of the raspberry, it would have made the device even more portable. Setting up wireguard is more or less the same process as on a normal computer. What does not work is forwarding the traffic, and thus use the phone to access resources that are geographically restricted. If you have administrative rights on the device, you might be able to set it up, but I’ve decided that the solution with the raspberry is good enough. Maybe another time I’ll try it again. |
Do you want to share your opinion? Or is there an error, some parts that are not clear enough?
You can contact me anytime.