Ubuntu Linux and OpenVPN Client, using UFW to force traffic via VPN tunnel interface

Updated 24/07/17; included startup configuration to ensure automatic docker container connectivity via VPN post reboot/ startup.

First, you’ll need to obtain your “.ovpn” configuration file from your VPN provider. Find and replace <config file> with the name of the file excluding the file extension.

There are two stages to this guide:

  1. VPN Client Connection/ Configuration
  2. UFW Firewall Configuration (to ensure traffic can only use VPN and prevent DNS Leak)

If you are using docker containers be sure to check the considerations at the end of this article.

VPN Client Connection/ Configuration

Prepare the configuration file:

# Disable IPv6
echo "#disable ipv6" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.all.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.default.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.lo.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# Move config file to /etc/openvpn
mv <config file>.ovpn /etc/openvpn/<config file>.conf

# Create .secrets file
echo '<username>' >> /etc/openvpn/.secrets
echo '<password>' >> /etc/openvpn/.secrets
chmod 600 /etc/openvpn/.secrets

# Modify config file
vi <config file>.conf

# Ensure you have no other auth-user-pass lines defined
auth-user-pass .secrets

# Add redirect-gateway to force traffic down TUN interface
redirect-gateway

# Add DNS server update script execution to config file
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf

# Save/close config file

We’ll now configure OpenVPN client to automatically connect to this VPN interface on startup:

# Edit /etc/default/openvpn, un-comment AUTOSTART="all" then save/close 
vi /etc/default/openvpn

# Start / enable openvpn service at boot
sudo systemctl start openvpn
sudo systemctl enable openvpn

Confirm the VPN tunnel is up and public IP is that of the VPN provider:

# Look for a tun0 interface, if found you are connected!
ifconfig

# Check public IP is "hidden"/ different vs. machine not on the VPN
curl ipinfo.io/ip

UFW Firewall Configuration

We’ll configure UFW to allow only allow outbound traffic to the VPN provider (“tun0” in this example) interface.

First we must enable forwarding:

# Set the DEFAULT_FORWARD_POLICY policy to ACCEPT
vi /etc/default/ufw

Next we’ll configure the necessary UFW rules to facilitate the outbound traffic to the VPN provider, but block everything else. You’ll need to change:

  • <VPN Server IP> to IPv4 address as-per the “remote xx.xx.xx.xx” line in your OpenVPN configuration file.
  • <port> to the remote OpenVPN port  (usually 1194 or 443)
  • <LAN subnet> to network/subnet that represents your network – i.e. 172.16.0.0/16.
# Defaults
ufw default deny incoming
ufw default deny outgoing

# Allow SSH from local LAN
ufw allow from <LAN subnet> to any port 22

# UFW rule to ensure we only hit the VPN
ufw allow out to <VPN server IP> port <VPN server port>
ufw allow out to <LAN subnet>
ufw allow out on tun0

# Enable the firewall
ufw enable

If DNS lookups on the client fail you’ve missed the configuration lines from your OpenVPN configuration file, as above, you want to force DNS lookups over the VPN otherwise you’re leaking DNS requests, reducing the privacy value of your VPN.

# Add DNS server update script execution to config file 
script-security 2 
up /etc/openvpn/update-resolv-conf 
down /etc/openvpn/update-resolv-conf

Considerations when using Docker Containers

If you want your docker containers to sit behind the VPN, ensure you use the–net=host” argument. As per: https://docs.docker.com/engine/userguide/networking/default_network/container-communication/

If your containers have no internet connectivity at startup it is likely because docker started before the VPN connection was established. Credits for this solution here.

sudo mkdir /etc/systemd/system/docker.service.d/
sudo touch /etc/systemd/system/docker.service.d/depend.conf
sudo vi /etc/systemd/system/docker.service.d/depend.conf

# New conf file should only contain lines below
[Unit]
Requires=sys-devices-virtual-net-tun0.device
After=sys-devices-virtual-net-tun0.device

# Now save the file and exit vim

sudo systemctl daemon-reload

# Test container connectivity following a reboot

Docker’s forward rules permit all external source IPs by default.

By default containers will use the docker0 interface and thus when your VPN goes down, they will still have external/ internet access. This statement only applies when using the default docker0 interface, not when binding the container to the “host” interface.

My experience shows that the default docker forward rule associated with the docker0 interface  overrides any UFW rule (i.e. as defined above).

You can test container connectivity using the commands below:

# Test there is NO connectivity from container when VPN is down
systemctl stop openvpn
docker exec -it <container_name> /bin/bash
ping 8.8.8.8

# Test this IS connectivity from container when VPN is up
systemctl start openvpn
docker exec -it <container_name> /bin/bash
ping 8.8.8.8