How I set up WireGuard on a RaspberryPi 1 to browse as if I were home

2024-04-05

Background

This article is based on my experience with a RaspberryPi 1 (yes!), Raspbian 12, Linux kernel 6.1, iptables and internet connection. This article can be no different than many other tutorials you find online, the only thing which may change is the usage of outdated hardware although still very well supported.

I really needed a simple, free, reliable VPN to remotely connect to my home networking and use my home IP while abroad also on mobile. Many people do this for bypassing country or service restrictions while streaming, others do this to be able to connect to internal devices at home which are not exposed to the internet, others do both and more. In my case it was just an “emergency” exit, or entrance, and a case study.

I initially thought my router would be the perfect entrance / exit node for a VPN service (which, well, technically is!), but despite it being a descouraged practice it is basically locked down by my ISP (yeah, I should buy a new router), so I dropped this possibility after a couple seconds. My Samsung TV would have been an interesting exit node but the more I thought about it the more I started thinking on issues I could encounter if the installation was anyway possible. Performance was my main concern, followed by maintenance difficulties as it’s a widely used, shared device in the house.

Just to make it clear once again, my plan is to achieve the goal with the best result while keeping the costs down, ideally to $0, and minimising the effort as much as possible. I decided not to use my Samsung TV for this as soon as Roberto Calabrese told me he could lend me his RaspberryPi 1 he no longer uses.

It is worth remembering the dated hardware this devices has, which in my opinion was way more than enough for just forwarding UDP packets!

1 Core ARMv6 CPU

$ cat /proc/cpuinfo
processor : 0
model name : ARMv6-compatible processor rev 7 (v6l)
BogoMIPS : 697.95
Features : half thumb fastmult vfp edsp java tls 
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xb76
CPU revision : 7

Hardware : BCM2835
Revision : 000d
Serial  : 000000000[redacted]
Model  : Raspberry Pi Model B Rev 2

and 512 MB RAM:

$ cat /proc/meminfo 
MemTotal:         439196 kB
[...]

Tailscale

The first VPN I wanted to try was Tailscale, mainly because the set up is super user friendly, it also provides a simple web interface where you could easily share devices, invite people to my network, MagicDNS, high availability and many more! Obviously I had to install it on something which would be online most of the time. Another cool thing about tailscale is that it can be installed on many devices and it is possible to discover other devices with the subnet router mode.

The set up was immediate and I could already see my home IP address while I was away! I thought the mission was completed, so I opened the app and started streaming. The moment I did it I could see the CPU going to 100% while the temperature also increasing.

I regret the fact I did not investigate any further on why this was happening, and assumed for certain Tailscale would not have been the best choice for streaming with such limited hardware, also got biased while reading comments online saying that Tailscale is not designed for performance and that I should have looked for something else. Again, I should go back and check what and and exactly why there was such high CPU usage while streaming, just for curiosity. Please Tailscale community, forgive me! I’ll come back with further info and on whether or not the actual problem has been between the keybord and the chair!

Wireguard

The other popular choice is obviously WireGuard and it is worth pointing out that Tailscale uses WireGuard behind the scenes. I wanted to be sure how much I would have gained in performance, if I would have gained anything at all! I strongly suggest to read the whitepaper to understand the power and the philosophy of WireGuard, which also shows some benchmarks showing how WireGuard is the most performant VPN amongst the others.

NOTE: Before proceeding with the actual set up it is worth to say that most home connections are not directly exposed to the internet. So in order for your WireGuard VPN being accessible from the outside on the standard 51820 port (or another port in case you want to use a custom one) you will have to do a port forwarding on your router otherwise the external traffic will never hit your WireGuard server (Raspberry Pi). Port forwarding is not a topic I am going to discuss here, so in case you haven’t done it yet please proceed with port forwarding before continuining with the next steps.

NOTE: As public IP addresses can change (let’s say your restarted your router), and unless you have a static external IP address, I also recommend for better availability to set up a DDNS. So your DNS will always reflect your IP address without worrying whether it changes or not. I created my own script for updating my DNS entry whenever the IP changes. This is not mandatory but it can be very useful.

Set up the server: (all as root). For my personal objective I’m fine setting up /32 IPs on a 10.0.0.0/8 block to avoid any conflict with my LAN.

apt update # update repository
apt install wireguard # install wireguard
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf
sysctl -p
if test -d /etc/wireguard; then cd /etc/wireguard; else mkdir -m 0700 /etc/wireguard && cd /etc/wireguard; fi # cd into /etc/wireguard otherwise create it
wg genkey | tee privatekey | wg pubkey > publickey # generate keypair
cat <<EOF >> /etc/wireguard/wg0.conf
[Interface]
PrivateKey = $(cat /etc/wireguard/privatekey)
Address = 10.0.0.2/32
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -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
ListenPort = 51820
mtu = 1500
EOF
cat /etc/wireguard/publickey

The above PostUp hook allows traffic forwarding from the WireGuard interface (wg0) and ensures that outgoing traffic appears to come from eth0. Set up the client “client”: (as root), forwarding ALL the traffic. Now, here on the [Peer] section (the server) we need to paste the server’s public key (the output of the last command on the server set up section above) and the server’s public IP.

apt update
apt install wireguard
if test -d /etc/wireguard; then cd /etc/wireguard; else mkdir -m 0700 /etc/wireguard && cd /etc/wireguard; fi # cd into /etc/wireguard otherwise create it
wg genkey | tee privatekey | wg pubkey > publickey # generate keypair
cat <<EOF >> /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.3/32
PrivateKey = $(cat /etc/wireguard/privatekey)
DNS = 1.1.1.1
mtu = 1500

[Peer]
PublicKey = INSERT_SERVER_PUBLIC_KEY_HERE
Endpoint = your_ddns.yourdomain.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
EOF
cat /etc/wireguard/publickey

Add the client to the server with its previously shown public key (last command). Run as root:

cat <<EOF >> /etc/wireguard/wg0.conf
[Peer]
PublicKey = CLIENT_PUBLIC_IP
AllowedIPs = 10.0.0.3/32
EOF

Start the Wireguard interface on server:

wg-quick up wg0

On the client instead: Make sure you are not connected to the same LAN as your WireGuard server in order to test whether it actually works or not.

curl ifconfig.me
wg-quick up wg0
curl ifconfig.me

If everything’s gone well you should see two different IP addresses after the curl commands, obviously if you were on a different network before connecting to your Wireshark VPN.

Well, now that everything works I can test by streaming, the RaspberryPi 1 limited CPU does not exceed the 20% usage, which is what I really wanted!

Start WireGuard as a service

You may want to start WireGuard interface at start up on your server. It can be done by setting this up as a service:

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

Debug

Unfortunately it is not possible to see debug messages in dmesg in case something goes wrong with Raspian OS as the kernel debug messages are reduced for a better optimisation. You can double check by running:

if test -d /proc/dynamic_debug; then echo "Kernel dynamic debug enabled"; else "Kernel dynamic debug not enabled"; fi

or

grep -Fie 'DYNAMIC_DEBUG' -R /boot

which, in case it is not enabled will show:

CONFIG_DYNAMIC_DEBUG is not set

Anyway it is always possible to see what’s going on using tcpdump or iptables or running wg show or alternatively journalctl wg-quick@wg0

Optimisation

There are some optimisations which can be done to improve UDP throughput suggested by Tailscale which I also recommend. (Only available from Linux >=6.2)

Monitoring

Last but not least I wanted to monitor my Raspberry Pi 1, independently of the fact it is my VPN exit node. For this I created a separate article here.