This is the sixth part of a multi-part tutorial describing how to configure the "perfect" Kodi media centre running on top of ubuntu server. Other parts of the tutorial may be found here:
- Introduction and Overview
- Part 1: Kodi installation and configuration
- Part 2: NFS file sharing
- Part 3: Auto-mounting hard drives with Udev
- Part 4: Remote administration with SSH
- Part 5: Transmission torrent client
- Part 6: VPN connection
- Part 7: Firewall configuration with UFW
This section covers configuration of an openvpn client connection to an external server, to anonymise traffic to and from the Kodi box and prevent bandwidth throttling of torrent traffic by your ISP. You will also find some additional configuration to ensure the VPN does not break secure SSH connections from the public internet via your router, and some scripts to make sure Transmission binds to the local IP address of the VPN interface (so it doesn't try and send traffic out over your public connection if the VPN goes down).
Always on VPN connection
My ISP throttles torrent traffic, so I choose to run an always on VPN client connection on this box. The aim is to send all torrent traffic out through this interface. I personally use a VPN service called privateinternetaccess (PIA), which I have found to be very useful. First, install openvpn:
sudo apt-get update sudo apt-get install openvpn
Now download the config files from your provider, into your home directory. For PIA, this can be achieved with:
cd ~ curl -O https://www.privateinternetaccess.com/openvpn/openvpn-strong-tcp.zip
Now unpack it (the unzip command will unpack it into a subdir called openvpn):
sudo apt-get install zip unzip unzip -d openvpn openvpn-strong-tcp.zip
Now copy the files you need into the config dir. Here I've used the config file for Germany as an example, but you could use any of them:
sudo cp ~/openvpn/Germany.ovpn /etc/openvpn/ sudo cp ~/openvpn/ca.rsa.4096.crt /etc/openvpn/ sudo cp ~/openvpn/crl.rsa.4096.pem /etc/openvpn/
Openvpn only creates connections for files ending in .conf
. I chose to create a symlink from the .ovpn config file. This way you can store many .ovpn
configuration files in the directory and update the symlink if you want to use a different config:
cd /etc/openvpn sudo ln -s Germany.ovpn client.conf
Ubuntu ships with a script that can be used to update which nameservers are used for DNS when the VPN is active, which is installed at /etc/openvpn/update-resolv-conf
. We need to add to the openvpn config file (/etc/openvpn/client.conf
) to make sure this script gets called when the VPN starts and stops. Add these lines:
up /etc/openvpn/update-resolv-conf down /etc/openvpn/update-resolv-conf
If your provider lets you authenticate with a username and password, you need to change the config so that openvpn knows without having to prompt interactively. Add this line (or modify existing lines):
auth-user-pass userpass.txt
Then create a file to store the username and password. This needs to be owned by root:root with read and write permissions for root only:
sudo touch userpass.txt chmod 600 userpass.txt
Edit the file and add the username on the first line and the password on the second line. Save and close the file. To enable user defined scripts to be called by openvpn, we also need to change the script security level. Add this to your client.conf
file:
script-security 2
Now we can enable openvpn so it starts automatically at boot, and start it up (restart it instead if it's already running):
sudo systemctl enable openvpn sudo systemctl start openvpn
Test your IP from the commandline to see if your IP address has changed :
curl icanhazip.com
Enabling SSH connections from the WAN when the VPN is running
The linux kernel uses routing tables to determine where it should send network traffic. When openvpn brings up the VPN interface, it changes the routing table so that the VPN becomes the default route for all traffic not destined for your local network. Unfortunately, this means that if you SSH to the server from the WAN, the packets will come in on the wifi interface and be sent out on the VPN interface. The SSH client will be listening for a reply from your global IP address, not the VPN server's global IP address, and you will get a connection error. The default routing table can be shown using the route
command. Without the VPN running, the routing table will look something like this:
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default 192.168.1.1 0.0.0.0 UG 0 0 0 wlp2s0 192.168.1.0 * 255.255.255.0 U 0 0 0 wlp2s0
With the VPN, the routing table will have additional entries for the local network of the VPN server:
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default 10.31.1.13 128.0.0.0 UG 0 0 0 tun0 default 192.168.1.1 0.0.0.0 UG 0 0 0 wlp2s0 10.31.1.1 10.31.1.13 255.255.255.255 UGH 0 0 0 tun0 10.31.1.13 * 255.255.255.255 UH 0 0 0 tun0 128.0.0.0 10.31.1.13 128.0.0.0 UG 0 0 0 tun0 6d.3e.32a9.ip4. 192.168.1.1 255.255.255.255 UGH 0 0 0 wlp2s0 192.168.1.0 * 255.255.255.0 U 0 0 0 wlp2s0
The routing tables can look quite confusing, but basically the kernel looks down the list and chooses the most specific entry for the traffic that is being sent, and uses that route to send the packet. The default destination is a catch-all for packets with no specific entry. The "gateway" is the next hop for packets that can't be sent directly (for example packets destined for the WAN when you are behind a router need to be sent to the router and not directly to the WAN address). The "genmask" is the netmask of the destination - in the last line, the genmask of 255.255.255.0 means that the rule applies to any 192.168.1.X address instead of just the single IP address. To make sure traffic that arrives from your router gets sent back out via the router and not over the VPN, we need to add a new routing table. The route
command we used earlier shows the default table if no other arguments are given. However, we are free to define multiple routing tables. As before, the following commands still assume that your router is at 192.168.1.1
and the IP address of your server is 192.168.1.2
. First, add a new rule to tell the kernel to use a new routing table for traffic from 192.168.1.2
:
sudo ip rule add from 192.168.1.2 table 128
You can show the existing rules using this command:
sudo ip rule show
Output should look like this:
0: from all lookup local 32764: from 192.168.1.2 lookup 128 32766: from all lookup main 32767: from all lookup default
Now we can create some rules in table 128. These assume that you have configured wireless internet and your wifi interface is wlp2s0
:
sudo ip route add table 128 to 192.168.1.0/24 dev wlp2s0 sudo ip route add table 128 default via 192.168.1.1
You can see these rules in the new routing table if you use the ip route show table 128
command:
default via 192.168.1.1 dev wlp2s0 192.168.1.0/24 dev wlp2s0 scope link
These entries specify that traffic to any 192.168.1.X address should be sent out using the wifi interface, and that the default route is via your router. Now test a SSH connection from the WAN to the server while the VPN client is running (e.g. using a mobile phone, or from another server outside your LAN). If the new static route has worked, we need to make it permanent, which can be achieved by listing up commands in /etc/network/interfaces
:
auto wlp2s0 iface wlp2s0 inet dhcp wpa-ssid "yourSSID" wpa-psk secrethash up ip rule add from 192.168.1.2 table 128 || true up ip route add table 128 to 192.168.1.0/24 dev wlp2s0 || true up ip route add table 128 default via 192.168.1.1 || true
In the above, the || true
part is there to make sure that if any of the commands produce warnings (e.g. because the route already exists) then the interface configuration continues instead of exiting with an error. Double check your have the commands entered correctly, and then restart the server and verify that the table has been added:
sudo ip rule show sudo ip route show table 128
And test to see if the rest of the outgoing traffic is still passing through the VPN:
curl icanhazip.com
Binding Transmission to the IP of the VPN interface
Transmission does not have an option to bind to a specific interface. By default, transmission binds to 0.0.0.0
, which means that packets will use the default route. When the VPN is up, the default route is the VPN, but if the VPN goes down for any reason Transmission will continue to send traffic on your normal IP address. To get around this, we can bind transmission to the local IP address of the VPN interface. To see your current config, type:
ifconfig
Here's an example:
enp3s0 Link encap:Ethernet HWaddr c0:3f:d5:69:6d:90 UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:118477 errors:0 dropped:0 overruns:0 frame:0 TX packets:118477 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:1364691847 (1.3 GB) TX bytes:1364691847 (1.3 GB) tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 inet addr:10.30.1.22 P-t-P:10.30.1.21 Mask:255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:2 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:100 RX bytes:0 (0.0 B) TX bytes:195 (195.0 B) wlp2s0 Link encap:Ethernet HWaddr 80:86:f2:bd:91:f6 inet addr:192.168.1.2 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::8286:f2ff:febd:91f6/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:9566008 errors:0 dropped:0 overruns:0 frame:0 TX packets:2290526 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:12721694676 (12.7 GB) TX bytes:260800582 (260.8 MB)
In this example, there are four interfaces:
- enp3s0 is the ethernet connection. At the moment it's not connected.
- lo is the loopback address (localhost)
- tun0 is the VPN, which has local ip address 10.30.1.22
- wlp2s0 is the wireless card, which has local ip address 192.168.1.2
What we need to do is run a script every time the VPN client starts, which will stop transmission, update the address it should listen to in /etc/transmission-daemon/settings.json
and then restart it again, causing it to bind to the IP address of the VPN interface. The OpenVPN manual lists a number of events where you can define a script to run (jump to the section called SCRIPTING AND ENVIRONMENTAL VARIABLES). Not all of them are relevant to openvpn in client mode. The ones we care about are:
- up - executed when openvpn has created the VPN tunnel
- down - executed after the VPN tunnel is closed
- ipchange - executed after the connection is first authenticated, or when the remote IP address of the server changes
We have already specified update-resolv-conf
to change the nameservers for DNS when the vpn starts and stops. What's great about these scripts is that they have access to some environment variables set by openvpn. To see what these are, we can edit the update-resolv-conf
script and add the following line to the end of the script:
printenv > /etc/openvpn/printenv-updown.log
This will print the environment variables to a file next time the script is called. Restart openvpn to make this happen:
sudo service openvpn restart
If you look in this file you will see some environment variables like:
script_type=up ifconfig_local=10.30.1.22
You can see in update-resolv-conf
that the $script_type
variable is used to take certain actions depending on whether the interface is being brought up or taken down. The $ifconfig_local
variable contains the local IP of the interface, which is exactly what we need for our script. This is much simpler than other solutions I have seen online, which try to parse the output of ifconfig
and chop out all of the unwanted bits - sometimes minor changes in the output of ifconfig can cause such approaches to break. Open /etc/openvpn/update-resolv-conf
again and add the following to the end of the file, which will allow us to write additional scripts without putting all of our changes in that file:
# added to call additional up and down scripts case "$script_type" in up) /etc/openvpn/up ;; down) /etc/openvpn/down ;; esac
Now create two new files and make them executable:
sudo touch up down sudo chmod +x up down
Paste the following into up:
#!/bin/sh # script to be run when the VPN is brought up logger "Local IP of VPN interface is $ifconfig_local" # rebind transmission to the local IP of the VPN interface # transmission settings.json looks something like: # { # ... # "bind-address-ipv4": "0.0.0.0", # ... # } # settings.json is overwritten when transmission exits. # If file is edited and transmission is restarted, the edit is lost because # transmission dumps the current config to the file when it stops. logger "stopping transmission" systemctl stop transmission-daemon wait 10 logger "replacing bind IP with current local IP of VPN interface" # the following command could be used if the version of sed installed had the -c option, but it is only included in redhat versions # see https://stackoverflow.com/questions/2610167/how-to-sed-search-and-replace-without-changing-ownership#2610198 #sed -i -r -c 's/("bind-address-ipv4": ")([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(",)/\1'$ifconfig_local'\3/g' /etc/transmission-daemon/settings.json # instead execute sed without the -c option and then restore the proper permissions and ownership manually sed -i -r 's/("bind-address-ipv4": ")([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(",)/\1'$ifconfig_local'\3/g' /etc/transmission-daemon/settings.json chown debian-transmission:debian-transmission /etc/transmission-daemon/settings.json chmod 660 /etc/transmission-daemon/settings.json logger "restarting transmission-daemon" systemctl restart transmission-daemon exit 0
And the down script:
#!/bin/sh # script to be run when the VPN is brought down logger "stopping transmission" systemctl stop transmission-daemon
Now let's test. Before reloading anything, have a look and see which IP addresses transmission is currently bound to:
sudo netstat -tulpn | grep transmission
You should see output like the following but without the header, which I've added back in:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:51413 0.0.0.0:* LISTEN 19885/transmission- tcp 0 0 0.0.0.0:9091 0.0.0.0:* LISTEN 19885/transmission- tcp6 0 0 :::51413 :::* LISTEN 19885/transmission- udp 0 0 0.0.0.0:51413 0.0.0.0:* 19885/transmission-
Now restart openvpn:
sudo service openvpn restart
The logger lines in the script should appear in the journal (you can view these as they are written in another SSH window with sudo journalctl -f
) and if you run the netstat
command again you should see:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 10.30.1.22:51413 0.0.0.0:* LISTEN 19885/transmission- tcp 0 0 0.0.0.0:9091 0.0.0.0:* LISTEN 19885/transmission- tcp6 0 0 :::51413 :::* LISTEN 19885/transmission- udp 0 0 10.30.1.22:51413 0.0.0.0:* 19885/transmission-
So transmission has successfully bound to the IP address of the VPN interface. If you have IPv6 enabled, note that this hasn't stopped transmission from listening on all addresses for IPv6 connections (so you would need to make further changes). Also note that transmission is still listening on all interfaces for the web UI on port 9091 (although access is controlled using the whitelist as mentioned earlier). There's a useful tool called iftop
that will show you a summary of traffic on a given interface. I find it quite useful for checking that all outgoing traffic is on the right interface. You can install it like this:
sudo apt-get update sudo apt-get install iftop
And then use it to check traffic on the wifi like this:
sudo iftop
Or if you want to see the traffic being passed through the VPN tunnel, specify the interface:
sudo iftop -i tun0
Problems with this section? Let me know in the comments. Otherwise, continue to part 7 - firewall configuration.
Comments
cannot connect to Rasberry Pi from WAN with OpenVPN running
Hello Sam,
I love your tutorials, they are to the point and explain well enough without covering all the linux basics.
I followed everything to the tee above and stopped where I had to check the ssh connection from a remote client with the VPN running (WAN to Server via SSH). I could not connect because the keys don't match and upon failing that the password does not match (checked with SSH -v host@VPNPubIP and I am 2000% my passwords are entered correctly). I can connect without a problem over WAN without the VPN running and router ports forwarded but not with the VPN running. I then did some digging and found that the PIA Aus server doesn't support port forwarding. So I then tried the CA Torronto one, as it did. Still no luck.
I then turned off my custom listening port in /etc/ssh/sshd_config and left it on Port 22 as PIA VPN left it open and still no luck (FYI, almost all the ports are blocked when I run the OpenVPN PIA connection). Is there anything you could recommend? I might even try installing the Ubuntu server / Germany PIA Server on my pi rather than Rasbian just so I have an almost identical setup to you (software wise) and try it all again.
Also for other people who ran into this, I had to add the following to my client.conf for Openvpn as there was an error with script security on execution:
"script-security 2"
The up / down additions for the DNS didn't work and the OpenVPN daemon crashes due to fatal error without the addition.
I really hope you might be able to shed some light on my issues and I look forward to your response,
Kind Regards,
Chrishan
SSH to the public IP address of your server
RE: SSH to the public IP address of your server
Hi Sam,
Thanks for all of your help, I was trying to ssh via the VPN public IP as you'd suspected. It is now working perfectly.
Regards,
Chrishan
Glad I could help!
Unable to log and bind
Hi!
First of all, thanks for the instructions, these has guided me furthest with VPN settings combined with SSH remote connection (other guides has failed). I stumbled into some troubles and hoped you could help to get through them.
I'm new with Ubuntu so these problems may be too easy for me to google around. I am unable to get the printenv output to log which makes me suspect that the problem is with script running on up/down events.
This also causes the binding of the transmission to fail.
My network device is nep4s0 which I think I have noticed in needed places (ip rules, routes and in network interfaces (also leaving out wpa SSID and password commented out)). So the VPN connection is working and I am able to remote connect via SSH. What am I missing with OpenVPN config that causes possible script running to fail? Or could there be some other problem I'm missing?
Thank you so much for your detailed instructions.
Best regards,
Matson
Is update-resolv-conf
update-resolv-conf
called in your/etc/openvpn/client.conf
file? I noticed just now that there was a typo in the tutorial before, I wrote/etc/openssh/client.conf
when I meant/etc/openvpn/client.conf
, so I'm wondering if you made the changes to the wrong file? SamEverything seems to be in order... but.
Thanks for the reply.
I did notice the typo with openssh/openvpn so it was as it should. And yes,
update-resolv-conf
was called in thenclient.conf
file. I found some workaround, however, by calling theup
anddown
scripts straight from theclient.conf
. This way the triggers worked. Is there any other need to use theupdate-resolv-conf
in up/down VPN events?Anyways, the VPN connection crashed at some point after running few hours. When figuring out the cause I noticed that there was two local IPs in the router: in addition to the original 192.168.1.33 there was new IP 192.168.1.39 with the same host name but with caps. Moreover, I noticed some strange behavior with
tail /var/log/syslog
which implied that I had another VPN service trying to restart over and over again with old configuration file that no longer existed. I guess this was some ghost service from the previous VPN with remote SSH attempts that I tried following other instructions. After disabling the service the syslog was ok and the VPN and transmission daemon has been running without problems.So all in all, thank you for your assistance. If you have any advice on the double IP (why is this and is it good or bad) I'm truly thankful. I think the main thing is that the server works as it should so if double IP with server is no big deal then I'm good.
Thanks again for the tutorial!
Best regards,
Matson
Talked too soon...
Apparently something is still going wrong on the server. Just lost connection to the server (on the work atm) after about 20 hours uptime. I can see my router info remotely and it seems that the new local IP address is still there but the old one that has port forwarding on the router for remote connection has gone missing. Oh, by the way, instead of Kodi I'm using Plex to stream media (I don't suppose this causes problems in terms of these instructions) and Plex is now also unavailable through the web interface.
So if there are any approach to start digging the problem up I'd be super thankful!
Br.
Matson
/etc/openvpn/update-resolv-conf not existing
This file seems to have been moved or is non existent now. I had to add update-systemd-resolved package to replace it. After that it worked.
Add new comment