Here lies a tale of a fun adventure with helping a friend remotely set up a VPN server, the joys of UPnP and SystemD. It’s a sort of tutorial but more documenting my own notes for if I have to do it again in future.
I want a VPN server Tim
It all started when I was asked “I want a VPN server because I want to be nice and safe when travelling about” said my friend via Skype that bastion of
So the goal of the project, a small VPN server sitting inside his home network, which he could then connect to with a remote client (his laptop).
Building a VPN server
This was fairly painless a spare RasberryPi that was lying around the house a copy of Rasbian a Debian based distro to get an OS on there. Turning off the desktop x.org and associated components and setting up SSH access with a key on a random port (this was less about security and more not wishing to avoid conflict should my friend have something running
For the VPN server it self I used
curl -L https://install.pivpn.io | bash
So let’s be clear a security product wants me to install a shell script from a remote source and then execute it without even a cursory look! something-something, grumble, grumble. However, the source is available from Github and once downloaded and piped through Bash like a dutiful sheep, using
Testing the VPN
Setting up the VPN probably took all of twenty minutes, and testing I went to my router control panel and went to open a port. That’s when the little voice started going… You’re going to have to talk him through port forwarding
UPnP
Universal Plug and Play is a set of protocols that lets applications talk to switches and routers to enable port forwarding its actually way more then that, its a communication layer for devices to talk to each other. For what I want it should allow me to request that router open and allow traffic to and from the Pi on several ports.
The ease you can basically break out of a network using UPnP is frightening and I think I should say that my advice is:
Good friends don’t let their friends have UPnP enabled
However I’m not a good friend
UPnP clients
I initially looked at writing a small script to open UPnP most programming languages have classes for working with UPnP. However to test everything was working I used miniupnp which is a small client and server for talking over UPnP. In the end I just used the client for this project.
Indeed opening a port is as simple as:
upnpc -a $IP 11948 11948 UDP
where $IP is the machine IP that you wish to pass the port to, you can also specify if you want TCP, UDP or both.
There is something very weird about just downloading a networking tool client, running a single command and it just work. I’m use to spending hours dealing with bridges and network interfaces on multiple devices before anything works!
Realising I could just use
Everyone loves SystemD
Setting up S
/etc/systemd/system/holepunch.service
[Unit]
Description=Hole Punching for OpenVPN
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/holepunch.sh >> /var/log/holepunch.log 2>&1
...
You can then specify its “type”, in this case, a one-off activity because we are going to then use the timer to call it, rather than a service.
Next up is the timer unit which is a replacement for the cron aspect, in this case the timer is set to wait 5 minutes then run and then rerun every 30 minutes.
/etc/systemd/system/holepunch.timer
[Unit]
Description=Runs and manages Holepunch services
[Timer]
# Time to wait after booting before we run first time
OnBootSec=5min
#Run every 30min
OnUnitActiveSec=30min
Unit=holepunch.service
...
Why wait 5 minutes? Yeah I’m lazy and well when I said the networking just worked, working out at what stage it would work took more then 5 seconds, while in theory network-online should have been the correct point and should have been instant, the reality was this didn’t quite behave as expected. the 5 minutes means post reboot the holepunch should have kicked in.
/usr/local/bin/holepunch.sh
#!/bin/bash
echo $(date -u) Starting Up
...
IP=$(hostname -I | grep -o '^\S*')
upnpc -a $IP 11948 11948 UDP
upnpc -a $IP 11948 11948 TCP
.....
echo "======= END ======="
The script has been heavily simplified, it does a bunch of other stuff, like sending a CURL request to a VPS to update the DNS on the fly. Originally I was going to put in some basic checks so that upnpc didn’t constantly barrage the router, but in this side project that somehow got missed, going forward if I need to do this again, I will set up some remote testing to make sure the ports are accessible and if not then punch though. By default OpenVPN is set to do UDP and fallback to TCP, originally during testing I was just using UDP but added TCP later after some feedback.
systemctl enable holepunch
systemctl start holepunch
Loads of improvements I can make, and in the end the core reason for using SystemD timers (to make sure we controlled that everything was ready for our script) actually turned out to not fully work, so a cron might have been easier. I console myself with the belief that in years to come sysadmin Tim will thank me for making it a service so it will be obvious what’s happening. I’m unconvinced, so am writing this blog post to basically remind older me of this.