Relaying OpenVPN through a Remote Server

Wherein I set up OpenVPN on my VPS so that I can access my home network (which is behind forced NAT and can't accept inbound connections of any kind) while I'm away from home.

Background

I live in student accommodation where I receive 'free Internet' (indirectly paid for through exorbitant rent fees) provided by KeySurf/KeyCom. This is accessible in two different ways:

Connecting to either of these gets you a captive portal where you must log in with your KeySurf account. You can also use the web interface to add specific devices by MAC address, which is necessary for stuff like the Nintendo 3DS.

I'm 99% sure that everybody in the building shares the same external IP address, though I haven't confirmed this for sure -- I know that my devices definitely do. Regardless, there is no way to set up port forwarding of any kind... so good luck getting remote access!

A friend gave me a TP-Link TL-WR710N router running OpenWRT (which I've since upgraded to LEDE 17.01.4). This has allowed me to set up a miniature home network, which is quite useful as it means that my laptop and desktop (plus VMs running on the latter) can communicate with each other reliably.

Of course, this only works when I'm actually physically there. Can I remotely access the network so that I can work on my desktop and access files when I'm not at home? The answer is yes -- but it took me the better part of a day to figure out the correct series of magical incantations (and a couple of experiments with magical "it just works" software packages that didn't end up actually working, like ZeroTier One and SoftEther VPN).

I'll spare you the details of how I actually got there and just describe the setup I ended up with, and how it works...

Network Setup

The router is running LEDE 17.01.4 and is accessing the Internet through the WAN interface. I've configured IP addressing as follows:

I assigned static leases to my machines to make working with them easier, but this isn't strictly necessary for the OpenVPN setup to function.

Note: I've got the subnet mask set to 255.255.254.0 rather than the somewhat more obvious 255.255.255.0 as a leftover from one of my failed experiments (following somebody else's guide to set up ZeroTier One). The latter would probably work as well, but I'm hesitant to change now that I have things working.

OpenVPN Setup: Server

My VPS is currently running OpenVPN 2.4 on Arch Linux.

I used easy-rsa to create a certificate infrastructure with keys for the server, router and laptop. This is a fairly standard setup; there are plenty of guides for this.

The following files from the process are in /etc/openvpn/server: ca.crt, server.crt, server.key, ta.key, dh.pem, as well as the following configuration in server.conf:

port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
client-config-dir ccd
client-to-client
keepalive 10 120
tls-crypt ta.key
cipher AES-256-CBC
compress lz4-v2
push "compress lz4-v2"
persist-key
persist-tun
status openvpn-status.log
verb 3

Note about the setup -- the OpenVPN network is on 10.8.0.0 with subnet mask 255.255.255.0. The OpenVPN server automatically assigns itself IP 10.8.0.1.

Additionally, you need to create a client configuration directory at /etc/openvpn/server/ccd, containing the following files. Note that the name of the file needs to match the 'common name' specified when you created the certificate for that device. No extension!

/etc/openvpn/server/ccd/router

ifconfig-push 10.8.0.200 10.8.0.1
iroute 10.99.4.0 255.255.254.0
push "route 10.8.0.0 255.255.255.0 10.8.0.1"

The ifconfig-push command assigns the router to 10.8.0.200 on the OpenVPN network.

The iroute command tells the OpenVPN server to route all packets on the 10.99.4.0 subnet (my home network) towards this device.

The push route command tells the router to route all OpenVPN network packets towards the OpenVPN server.

/etc/openvpn/server/ccd/laptop

ifconfig-push 10.8.0.100 10.8.0.1
push "route 10.99.4.0 255.255.254.0 10.8.0.1"

The ifconfig-push command assigns the laptop to 10.8.0.100 on the OpenVPN network.

The push route command tells the laptop to route all home network packets through the OpenVPN server.

No iroute is needed because the only packets going to the laptop are destined for the laptop itself -- this is in contrast to the router, where packets going to the home network need to be explicitly directed towards it.

OpenVPN Setup: Client 1 (Laptop)

This is a modified and pruned version of the template client configuration. I'm using it with Tunnelblick on macOS, but I see no reason it wouldn't work on other platforms as well.

The following files are included alongside the configuration: ca.crt, laptop.crt, laptop.key, ta.key

client
dev tun
proto udp
remote tres.wuffs.org 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert laptop.crt
key laptop.key
tls-crypt ta.key
verb 3

OpenVPN Setup: Client 2 (Router)

Getting OpenVPN to work on the router is pretty straightforward, thankfully. Install openvpn-openssl using opkg.

The following files should be in /etc/openvpn: ca.crt, router.crt, router.key, ta.key, along with the following configuration in vpn.conf:

client
dev tun
proto udp
remote tres.wuffs.org 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert router.crt
key router.key
tls-crypt ta.key
verb 3

Run /etc/init.d/openvpn start and with any luck, OpenVPN will start.

You may find it helpful when debugging to simply run OpenVPN in the foreground using openvpn /etc/openvpn/vpn.conf and view the log messages - this goes for the setup on the other devices too, not just the router.

Router Setup

Once all of this has been set up, you should have both devices connected to the VPN and you should be able to ping across the OpenVPN network: the router should be able to ping the laptop at 10.8.0.100, and the laptop should be able to ping the router at 10.8.0.100.

However, to make the home network accessible, you'll need to set up forwarding on the firewall. Thankfully this is pretty straightforward via the firewall settings in LEDE's LuCI.

From Network > Interfaces, select Add new interface, and enter the following:

Now go to Network > Firewall. Create a new Zone, with the following settings:

Save and apply everything.

If all has gone well you should now be able to access devices on the home network (the 10.99.4.x subnet) while connected to the VPN, no matter where you are.

Caveats / Room for Improvement?

There are a couple of issues I didn't bother trying to troubleshoot and fix because I was already tired of OpenVPN/firewall configuration and I'm not particularly bothered by them.

SMB (Windows file sharing) appears to work fine over the network, but names are not resolved automatically so you must for example access \\10.99.4.100 rather than \\TRASH.

OpenVPN client 1 (the laptop) can access the 10.99.4.x subnet, but the server itself can't. This is probably pretty straightforward to fix by adding extra routing rules but I didn't want the hassle.

Let me know on Twitter (@_Ninji) or through some other means if you found this useful!


Previous Post: An Unholy Combination: Closure Compiler, Preact and JSX
Next Post: Mouse Adventures #1: Introduction