28th February 2013

OpenVPN bridge

To setup OpenVPN, there are really multiple variations. My main objective is very simple: I have hosts on several locations, where I run OpenVPN, and I would like to automatically being able to appear as having an IP address on those hosts. That is, by connecting to specific ports on my router, all my network traffic will appear as originating in one of those hosts.

This goal is better achieved by setting a OpenVPN bridge (as opposed to the more simple point to point tunnel approach). Security requires using a static key, or SSL/TLS + certificates for authentication and key exchange. For my objective, it is enough to use a static key, much simpler to setup.

The setup requires multiple steps, to setup VPN on the remote (server) side, and on the router (client) side. Note that the final client -any device connected to the router- requires no setup at all.

Configure remote server for VPN

This configuration obviously depends on the server, which in this case runs Debian. To just slightly beef up the security, we will use port 11943 instead of the default 1194 (using UDP).

To install openVPN, the server must support TUN/TAP devices. I use a VPS based on OpenVZ which lacked this support, so I had to request its inclusion to my hosting company, who obliged very quickly (the second host I use -also VPS / OpenVZ- turned my request off, which says plenty on the options that I will renew my plan with them).

sudo apt-get update
sudo apt-get install openvpn

generate now the static key:

cd /etc/openvpn
sudo openvpn --genkey --secret static.key

And configure the tun device:

sudo vi /etc/openvpn/tap0.conf

In this file, we enter the following contentto create a network on 10.192.168.x, where the server is itself 10.192.168.1. Note that the communications will use UDP on port 11493:

dev tap0
ifconfig 10.192.168.1 255.255.255.0
port 11943
proto udp
secret /etc/openvpn/static.key

script-security 3
keepalive 10 120
persist-key
persist-tun
comp-lzo        

status /var/log/openvpn-status.log
verb 3

Some details are very important here. If you miss in this configuration the persist-tun line, the tun device can be reset every two minutes (as defined by the keepalive directive). In that case, the device is effectively taken down and up again. If you happen to have a dhcp server listening on that device, it will report that the network is down and will just stop serving DHCP requests.

The setup can now be tested with:

sudo openvpn --config /etc/openvpn/tap0.conf --verb 6
sudo ifconfig

The first line will fail if there is no TAP device support. If everything goes ok, the secon line will show a device tap0 with address 10.192.168.1.

In debian, installation of openvpn sets it up as a service, and it will automatically use the created configuration file /etc/openvpn/tap0.conf, so it is effectively sufficient to enter:

sudo service openvpn restart

Configure OpenVPN on OpenWrt router

We assume now than OpenVPN is installed, either as compiled in the image, or installed as a package. As reported, the TL-841N has not memoty enough to support the package option, so I had to compile it in the image.

First thing: get the static.key file that we created on the server, and copy it in the router as /etc/openvpn/static.key. This file should be chmod 600:

chmod 600 /etc/openvpn/static.key

configure now the tap device

vi /etc/openvpn/tap0.conf

And enter the following content to create a network on 10.192.168.x

remote YOUR_SERVER_IP_ADDRESS #the public IP address of the server
dev tap0
ifconfig 10.192.168.2 255.255.255.0
port 11943
proto udp
secret /etc/openvpn/static.key

keepalive 10 120
persist-key
persist-tun

resolv-retry infinite
mute-replay-warnings
comp-lzo
verb 3

Test it now doing:

openvpn --config /etc/openvpn/tap0.conf --verb 6

Now, the VPN connection between the server and the router is already working; it can be tested the connection doing:

ping 10.192.168.1

NOTE: It could possibly not work if you have some firewall / iptables rules etc setup on your router. In that case, it is obviously needed to open the required ports for VPN communication.

Once the configuration is tested okay, we can enable the OpenVPN connection from startup by editing the file /etc/config/openvpn:

vi /etc/config/openvpn

That should contain:

config openvpn custom
    option enabled 1
    option config /etc/openvpn/tap0.conf

To test it, try now rebooting. Doing then on the router:

ifconfig

should show the tap0 connection. And doing ping 10.192.168.1 should work as well

Create the bridge on the router

An assumption now is that the device has been split into several virtual interfaces (vlans). This is not strictly required, but helps with the setup, by allowing us to login in the router (the ports allocated to the bridge will connect us directly to the VPN server).

When created the virtual interface for the router, I had configured it as (file /etc/config/network):

config interface 'landenver'
    option ifname 'eth1.2'
    option type 'bridge'
    option proto 'static'
    option ipaddr '192.168.2.1'
    option netmask '255.255.255.0'

But to use this virtual interface as a VPN bridge, we need to change slightly this configuration, into:

config interface 'landenver'
    option ifname 'eth1.2'
    option proto 'static'
    option ipaddr '192.168.2.1'
    option netmask '255.255.255.0'

Not including the option type bridge line, means that no ifname will be created for this virtual lan. Note also that the changes we had included on the router's file /etc/config/dhcp related to interface landenver can be commented out, as the router will not be serving any DHCP leases on this interface.

The router contains in this moment two interfaces (tap0 and eth1.2) that we want to bridge. This is achieved by entering:

brctl addbr br0
brctl addif br0 tap0 
brctl addif eth1.2

Ready now to bring the VPN up:

ifconfig br0 up

NOTE: if you are connected to the router via any port allocated to the VLAN, you will lose now connection to the router; to continue working on the router you need therefore to connect now to other port on a separate VLAN.

The bridge can be easily tested now on any client attached to the router (on a port of the VLAN).On that client, allocate a static address on the VPN:

ifconfig eth0 10.198.168.201

So the client attached to the router can now directly ping the server:

ping 10.198.168.1

For this configuration to be permanent, we create in the router the file /etc/init.d/vpn-bridge as the following script:

#!/bin/sh /etc/rc.common

START=96

SERVICE_DAEMONIZE=1
SERVICE_WRITE_PID=1

start() {
        stop
        ifconfig tap0 &> /dev/null || sleep 1
        ifconfig tap0 &> /dev/null || sleep 1
        ifconfig tap0 &> /dev/null || sleep 1
        ifconfig tap0 &> /dev/null || sleep 1
        ifconfig tap0 &> /dev/null || sleep 1
        brctl addbr br0                                                     
        brctl addif br0 tap0                                                
        brctl addif br0 eth1.2                                              
        ifconfig br0 up             
}                                   
                                    
restart() {                         
        start               
}                           
                       
shutdown() {           
        stop                               
}                                          
                                           
stop() {                                   
        ifconfig br0 &> /dev/null || return
        ifconfig br0 down                  
        brctl delbr br0                    
}                                          
                                           
reload() {                                 
        restart                            
}

And setup the rc.d links:

chmod +x /etc/init.d/vpn-bridge
cd /etc/rc.d
ln -sf ../init.d/vpn-bridge S98vpn-bridge

Now the bridge is configured and brough up on startup.

Get connected

A client connected to the router is now on the same VPN as the remote server. But unless there is a DHCP daemon on the server, the client will have no IP address (ergo no real connection).Assigning a static address is not good -we want the client to be attached to different networks depending on the chosen rooter port, and we want it to be automatic-. The router itself is not envolved here anymore: it should be now a transparent tunnel between both ends: each of our client devices and the remote server.

So what is needed is the server providing the IP address to the client. Furthermore, we will need to setup the server so that all traffic coming from the VPN tunnel appears as coming from the server itself, to ensure full internet conectivity.

To provide the IP address, it is needed to have a DHCP daemon on the server. In my case, my VPS has a static IP address, but there could be already a DHCP server on the network. Never minds, we execute our own daemon serving IP addresses for the created VPN. In Debian, install the DHCP server as:

sudo apt-get install isc-dhcp-server

We ensure now that it serves the tap0 interface:

sudo vi /etc/default/isc-dhcp-server

and modify the line INTERFACES to contain:

INTERFACES="tap0"

Then, the file /etc/dhcp/dhcpd.conf needs some content like:

ddns-update-style none;
option domain-name "codingnight.net";
option domain-name-servers 8.8.8.8, 8.8.4.4;
default-lease-time 43200;
max-lease-time 86400;
authoritative;
log-facility local7;

subnet 10.192.168.0 netmask 255.255.255.0 {
    range 10.192.168.100 10.192.168.150;
    option subnet-mask 255.255.255.0;
    option broadcast-address 10.192.168.255;
    option routers 10.192.168.1;
}

In this way, it will serve IP addresses on the created VPN network in 10.192.168.x

To ensure that traffic appears as coming from the server:

echo 1 > /proc/sys/net/ipv4/ip_forward

And we do this change persistent by editing the file /etc/sysctl.conf and ensuring it contains:

net.ipv4.ip_forward = 1

The last step is to update the iptables; in my OpenVZ VPS, the ethernet driver is virtualizes as venet0, so it is needed:

iptables -t nat -A POSTROUTING -s 10.192.168.0/24 -o venet0 -j MASQUERADE
iptables -F FORWARD
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 10.192.168.0/24 -j ACCEPT
iptables -A FORWARD -j DROP

And we do the change persistent by entering:

iptables-save > /etc/iptables.up.rules

If no previous iptables rules were defined, it is needed to create the file /etc/network/if-pre-up.d/iptables with the following content:

 #!/bin/bash
/sbin/iptables-restore < /etc/iptables.up.rules

And chmod it:

sudo chmod +x /etc/network/if-pre-up.d/iptables

Now, it is enough to start the DHCP server:

sudo service isc-dhcp-server start

Presto!, any client attached to the router should receive now an IP address from the VPN server -assuming the clients are configured to receive such IP address automatically!