How to set up a transparent VPN Internet gateway tunnel using OpenVPN

I created a transparent VPN Internet gateway tunnel (sorry, couldn’t come up with a better name for it) using OpenVPN and my new Odroid-C1 Linux mini computer. However, this will work with any Linux PC (including the Raspberry Pi). The beauty of a transparent VPN gateway is that a device in the LAN doesn’t have to know anything about the VPN. I don’t have to remember to turn on the VPN nor does it drain the battery on mobile devices to encrypt and decrypt the packets. The VPN is just “there”. On the other hand, mostly for performance reasons, I don’t want to encrypt all traffic leaving my home LAN, that’s why I didn’t set up the VPN in the existing router. vpn-gatewayI wanted to be able to choose, on a per-device basis, which devices will route their traffic unencrypted to my ISP and which devices will get their traffic encrypted and forwarded to the remote VPN server using a second gateway in my LAN. And all this without additional subnets in my LAN, VLANs or additional WiFi or Ethernet-adapters. This may not look like the brightest idea to everyone but it works for me and I wanted to document it to save time if I have to set it up again. This is not a step-by-step tutorial but should provide enough pointers to get started.

The transparent VPN gateway tunnel can be used in several ways:

  1. Set the router/gateway address and DNS resolver addresses in a client to the existing LAN router and all traffic will be sent unencrypted (unless TLS/SSL is involved) to your ISP. This is the default in most home LANs.
  2. Set the router/gateway and DNS resolver addresses to the VPN gateway tunnel and all traffic will be sent encrypted via your ISP to your remote VPN server
  3. Only change the DNS resolver address to the VPN gateway tunnel and all DNS requests will be sent encrypted via your ISP to your remote VPN server.

I have a Fritz!Box router on 192.168.178.1/24. It operates as an Ethernet router and as a WiFi router. All traffic leaving my LAN to the Internet is being sent through this router. Since I wanted to use static IP addresses, I restricted its DHCP IP range from 192.168.178.10 – 192.168.178.99 to avoid collisions. I assigned 192.168.178.2 to the Odroid-C1 which will then act as the secondary gateway in the LAN and tunnel all traffic through the primary gateway on the main router at .1 to the remote VPN server. I’m manually assigning static IPs above .100 to my LAN-clients that I want to be able to use the transparent VPN gateway on 192.168.178.2.

To sum it up:
LAN network: 192.168.178.0/24
Default gateway of the primary router: 192.168.178.1
Default DNS forwarder: 192.168.178.1
Default gateway of the VPN gateway router: 192.168.178.2
DNS forwarder for the VPN gateway: 192.168.178.2
VPN network: 10.8.0.0/24
VPN server in VPN network: 10.8.0.1

VPN Server

Here’s how to set up OpenVPN in Ubuntu. Make sure to follow all instructions to create a CA and a server certificate including copying easy-rsa to /etc/openvpn/easy-rsa.

I’m using the client-config-dir keyword in the OpenVPN server and a per-client certificate using the certificate’s common name, which in my case is “odroid”. Some time ago, I found this handy client certificate & configuration generator for OpenVPN. It creates complete .ovpn files which can be imported into a variety of OpenVPN software clients.

EASY_RSA_DIR="/etc/openvpn/easy-rsa"
KEYS_DIR="$EASY_RSA_DIR/keys"
OVPN_PATH="/etc/openvpn/clients"
REMOTE="vpn.nsa.gov"

if [ ! -d $OVPN_PATH ]; then
  mkdir $OVPN_PATH
fi

if [ -z "$1" ]
then
        echo -n "Enter new client common name (CN): "
        read -e CN
else
        CN=$1
fi

if [ -z "$CN" ]
        then echo "You must provide a CN."
        exit
fi

cd $EASY_RSA_DIR
if [ -f $KEYS_DIR/$CN.crt ]
then
        echo "Certificate with the CN $CN already exists!"
        echo " $KEYS_DIR/$CN.crt"
else
source ./vars > /dev/null
./pkitool $CN
fi

cat > $OVPN_PATH/${CN}.ovpn << END
client
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
verb 4
comp-lzo
remote $REMOTE
#script-security 2
#up /etc/openvpn/up.sh
#down /etc/openvpn/down.sh

<ca>
`cat $KEYS_DIR/ca.crt`
</ca>

<cert>
`sed -n '/BEGIN/,$p' $KEYS_DIR/${CN}.crt`
</cert>

<key>
`cat $KEYS_DIR/${CN}.key`
</key>
END

This is my /etc/openvpn/server.conf:

mode server
dev tun

ca /etc/openvpn/ca.crt
cert /etc/openvpn/server.crt
key /etc/openvpn/server.key
dh /etc/openvpn/dh1024.pem

server 10.8.0.0 255.255.255.0
keepalive 5 30
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn-status.log
log /var/log/openvpn.log
verb 3
push "redirect-gateway def1"
push "dhcp-option DNS 10.8.0.1"
comp-lzo adaptive
push "comp-lzo adaptive"
client-config-dir clients

route 192.168.178.0 255.255.255.0

Make sure there is a DNS resolver on the VPN server, allowing recursive queries from 10.8.0.0/24. To get you started apt-get install dnsmasq and a public resolver in /etc/resolv.conf should be enough.

Add this to your VPN server’s firewall-script or if you don’t have one, dump it into /etc/rc.local:

/sbin/iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
/sbin/iptables -t nat -A POSTROUTING -s 192.168.178.0/24 -o eth0 -j MASQUERADE

In order to route the traffic from my LAN through the VPN tunnel to the Internet I needed to add these lines to my client-config file /etc/openvpn/clients/odroid (yes, the filename is the same as the common name of the client certificate):

iroute 192.168.178.0 255.255.255.0
push "route 192.168.178.0 255.255.255.0"

VPN Client (the LAN gateway)

My Odroid-C1 acts as the OpenVPN client in my LAN. Since the Odroid-C1 runs an Ubuntu 14.04 minimal image, all I need is to apt-get install openvpn

Once there’s a client configuration file in /etc/openvpn, Ubuntu will start the connection to the remote VPN server automatically when the Odroid-C1 boots.

This configuration has been created with the OpenVPN config generator above. In the OpenVPN client, it needs to be renamed from .ovpn to .conf in order to get started automatically at boot time.

/etc/openvpn/client.conf

client
dev tun
remote vpn.nsa.gov 1194
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
verb 4
script-security 2 # make sure to uncomment
up /etc/openvpn/up.sh # make sure to uncomment
down /etc/openvpn/down.sh # make sure to uncomment

-----BEGIN CERTIFICATE-----
MIID...

We also need a DNS forwarder in the client: apt-get install dnsmasq resolvconf

Since I want the Odroid to forward packets only when the VPN is up, I only enable forwarding once the tunnel to the remote VPN server has been established. The up/down keywords in OpenVPN come in handy to do this. Make sure packet forwarding is not enabled in /etc/sysctl.conf per default or you may leak unencrypted data.

/etc/openvpn/up.sh

#!/bin/sh
/sbin/sysctl -w net.ipv4.ip_forward=1
/etc/openvpn/update-resolv-conf $*

/etc/openvpn/down.sh

#!/bin/sh
/sbin/sysctl -w net.ipv4.ip_forward=0
/etc/openvpn/update-resolv-conf $*

Debugging

There are quite a few places where things can go wrong while setting up the transparent VPN gateway. If things don’t work, try using IP addresses instead of FQDNs because there might be an error in the DNS forwarding. A command which always comes in handy is curl curlmyip.com. It also works with an IP address (look it up, it may change). When the tunnel is up and running, it should return the IP address of the VPN server.

Obviously, tcpdump -i any may help as well. Try with ICMP only (and the ping command) to filter away any other traffic and start pinging the tunnel endpoint addresses and things like that.

Keep an eye on the client and server OpenVPN log files and syslogs.

Performance

Since OpenVPN works single-threaded, it all comes down to single-core CPU power. Using iperf I’m able to push around 90 Mbit/s through the tunnel. However, using speedtest.net I’m seeing around 40-50 Mbit/s while maxing out a single core. Depending on the routing to the remote VPN server you may see much lower values but these have nothing to do with the Odroid’s CPU performance.

9 thoughts on “How to set up a transparent VPN Internet gateway tunnel using OpenVPN

  1. Is this possible to do without access to the VPN server itself? Like if I use some VPN service that provides access to their servers. Would this still be possible in a situation like that?

  2. Me again. I’m trying to make this work, but I know I’m in over my head.

    I figured out the DHCP numbering on the client side, but am stumped with the DNS.

    Is there any way you could post your /etc/resolv.conf files (for the server and client gateway) so I can see what those contents would be?

    When I start up the openvpn, I currently lose my ability to ping anything by name on the client gateway, or anything connected to it.

    Thanks so much.

    • In my case it looks like this:

      # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
      # DO NOT EDIT THIS FILE BY HAND — YOUR CHANGES WILL BE OVERWRITTEN
      nameserver 127.0.0.1

      The combination of dnsmasq, OpenVPN and resolvconf will take care of the DNS settings on the client if you push the VPN’s DNS resolver on the server down to the client using
      push “dhcp-option DNS 10.8.0.1”

      Obviously, everything depends on your network layout, it’s quite difficult to provide any helpful hints from the distance.

      Cheers,
      Jan

      • Thanks for all your help Jan. Got it working perfectly. (I’d missed turning on IP forwarding on the server side).

    • Shawn, I really don’t know. I don’t know if OpenVPN supports the type of broadcast messages for DHCP. Probably not in tun-mode.

      Cheers,
      Jan

  3. This is essentially what I am trying to achieve. I want my LAN clients on the gateway side to use DHCP to get their IP addresses from the server side (other the tunnel). Is this possible?

  4. By the way, my static interface configuration for the Odroid-C1 looks like this:

    #auto eth0
    #iface eth0 inet dhcp
    
    auto eth0
    iface eth0 inet static
      address 192.168.178.2
      netmask 255.255.255.0
      network 192.168.178.1
      broadcast 192.168.178.255
      gateway 192.168.178.1
      dns-nameservers 192.168.178.1 
    

Comments are closed.