(This is currently a work in progress.)

Creating a Dynamic VPN

Introduction

This informal document focuses on a single Virtual Private Network (VPN) configuration that I use today. It has the following general characteristics:

This method provides the most benefit when one of the following is true:

It assumes that you already know about most or all of the following:

I used the O'Reilly "IP Routing" book for the protocols, and experience (a.k.a. "years of breaking stuff") for the rest. Hopefully, I can fill in some of the "other stuff" as I get feedback about this guide.

Knowing the Cisco router command set will also help. At least, it did for me.

FreeS/WAN

FreeS/WAN is another type of VPN software. It used to be the sole VPN software in our network until I designed this dynamic routing configuration.

As of the last time I used it (2002), FreeS/WAN operated on a much different model than OpenVPN. The latter sets up a separate interface for every VPN tunnel (e.g. tun0), creating a WAN-like point-to-point link over which traffic can be routed.

FreeS/WAN sets up only one interface (e.g. ipsec0) per system interface (e.g. eth0). Traffic is routed normally, until a packet is sent to a host that FreeS/WAN is configured to route for. That packet is rerouted through the appropriate tunnel based on the packet's destination.

While ideal for "opportunistic" encryption, this method will not work for the purpose of this guide. The missing requirements are a) separate interfaces to route over, and b) allowing the routing table to do the work, not the VPN software.

At various points throughout this document, I will refer to FreeS/WAN for comparisons, typically regarding topology. This is not to be taken as criticism; I used FreeS/WAN for many years before I realised OpenVPN was better for my needs. FreeS/WAN is geared towards opportunistic encryption with strangers; OpenVPN is best suited for static tunnels between your own installations. The latter is the situation I am addressing.

Topologies

Back when I used FreeS/WAN, the lattice topology (all nodes connected to each other) was the only supported method. Dynamic routing brings other options, and can also use true lattices to their full potential.

Large Networks: Star Topology

With a lattice topology, for every node that requires private access to another node, you need a distinct tunnel. If n nodes require access to each other, you need to create and maintain 1+2+3+...+(n-1) different tunnels. This can start to get a little tedious with as few as five nodes, and downright painful with ten or more.

For the sake of this guide, "node" refers to individual gateway machines and/or physical locations. Back when I used FreeS/WAN, "node" instead meant "network", prematurely escalating the lattice problem when a single gateway hosted multiple networks!

Using dynamic routing allows for more flexibility. Like the Internet itself, you can have one set (or multiple sets) of reliable core routers, and have other nodes connect to one or more of those. Or you could segregate the network into smaller nets, with a few core pipes inbetween.

Finally, you could have every node make a single tunnel to one router -- the "porcupine" topology. :)

Small Networks: Lattice Topology

On the flip side, creating a lattice has a major advantage: Reliability. The more tunnels you have, the more reliable the network will be in general, since failures can be routed around whenever possible.

Lattices are most reliable when they use a strongly diverse set of Internet providers. In our network, we use three different providers. One of our nodes has both cable and DSL from different companies. It uses one tunnel on each to talk to our hub; for everything else, it uses whichever interface makes the most sense.

Even if your VPN all uses a single provider, having a lattice means that tunnels can be taken down for reconfiguration, or due to a crash, without rendering nodes unreachable. Even if routing via someone's home isn't the most ideal situation (typically due to low speed), it's almost always better than having no connectivity at all.

Example Layout

This document assumes a relatively typical network layout. Though this strategy can be designed to work in many different situations, I only have time to document a) what I know, and b) what I suspect other people will face.

Gateways

When a PC running Linux operates as the border gateway for a network, the most typical configuration is to have separate interfaces pointing inwards and outwards. The outside IP is routable from the Internet; the inside network can optionally be routable, but doesn't need to be.

For a running example, we'll use two routers. ALPHA is a router at a datacentre. Its outwards IP is 11.22.33.44, and it routes for a routable class C network, 100.20.30.*, allocated by the ISP.

Meanwhile, BETA is a gateway at my home, using simple single-IP broadband. Its outwards IP is 55.66.77.88, and that's all it gets. Internally, I choose to use 192.168.66.* as my LAN.

One situation to watch out for is this: The network you want to route over the VPN ("inside") must not include the IP you use to establish the VPN links ("outside"). (For example, some installations do the border routing for you; they just give you a block of IPs.) If you do this, then everything will look great until you turn on routing. Since you can't send VPN packets over the VPN itself, it won't work. Then the route times out, and it works. Then it renegotiates, and it doesn't work. Etc.

IP Addressing

I use the 192.168.* reserved IPs for unroutable networks, and the 10.* reserved IPs for the actual VPN linkages. I assign every gateway a unique number -- typically the third octet of the network it routes for. (So far, my third octet has always been unique.)

For the VPN links, I use 10.from.to.link, where from and to are the unique numbers of the source and destination router, respectively. Note that this applies on both ends. Since a VPN link has two endpoints, each one needs an IP address. I use this same method in reverse on the other end.

On a point-to-point link (netmask 255.255.255.255), the two addresses don't need to be close together. In fact, they can be absolutely anything; that's what allows this addressing strategy to work!

link is an arbitrary number, the same on both ends. I use 100 for the first link, and 200 for the second. (I've never had more than two links between a pair of nodes.)

For our example, ALPHA is routing for 100.20.30.*, so it gets a unique number of 30. BETA gets 66. Therefore, on the VPN link between the two, ALPHA is given 10.30.66.100, while BETA gets 10.66.30.100.

Why do I use this strategy? Aside from easy visual identification during a traceroute, I found it also made it easier to do (fake) reverse DNS. I have a DNS server that handles my home DNS, and one that handles work DNS. Since every valid address starting with "10.66" actually points to BETA, my home DNS server is responsible for 10.66.*, while the office handles 10.30.*.

OpenVPN Setup

Configuration files for OpenVPN are nothing more than a bunch of command-line options without the double dash prefix. In a Debian install of OpenVPN, all configuration and shared key files are stored in /etc/openvpn. For every file with a .conf extension, the init.d script launches a separate OpenVPN process. In Debian, the "cd" option is automatically set to /etc/openvpn, so you can specify files (e.g. your secret key file) relative to that directory.

At least one of the two endpoints should have a static IP address. Failing this, you'll have to use a dynamic DNS service such as DynDNS.org. If both ends are dynamic, it's a good idea to have dynamic DNS on both; if either side can contact the other, they then know where to find each other. Choose your "float" option according to static versus dynamic IP. Alternatively, you can just always leave "float" turned on.

Personally, I use the "dynamic update" feature of our own BIND server. It's more secure, and far more forgiving of redundant updates. But that's a topic for another document.

I recommend you start with shared key encryption until you're comfortable with the network. Later, you can switch to the TLS method if you want more security.

For shared key encryption, I recommend each connection use a different shared key. Aside from being more secure, this also detects configuration mistakes such as using the wrong ports. Note that while each connection may have a different key, the machines on either end of that connection both need to use the same key – that's the whole point. It's also a good idea to make sure the key file is only readable by root; this helps keep the keys secure.

Static Interfaces

It's suggested that the devices be static and exist from system initialisation (persistent). Most versions of Zebra/Quagga are okay with devices going up and down, but older versions take issue with devices appearing and disappearing.

I prefer to set all devices up at boot time, using "--mktun". I like to call my devices vpn0, vpn1, etc. for explicitness. In Debian, it's nice and easy to set this up, in /etc/network/interfaces:

auto vpn0
iface vpn0 inet static
        address 10.30.66.100
        pointopoint 10.66.30.100
        netmask 255.255.255.255
	mtu l400
pre-up /usr/sbin/openvpn --mktun --dev vpn0 --dev-type tun
up /sbin/sysctl -w net/ipv4/conf/vpn0/rp_filter=0

Notes on the above snippet:

Dynamic Interfaces

This is a theoretical method that has never been tested. If this is your first time setting up OpenVPN and Quagga, don't do this unless you have a very good reason.

When I originally used this method, it wasn't by choice; the kernel was too old and we couldn't afford a reboot to upgrade it. It was terrible – any tunnel going up and down required a Zebra reboot.

However, as of Quagga version 0.97.3, I've noted that it doesn't crash or hang when interfaces disappear and reappear (so far). This new behaviour might allow for dynamic VPN device creation and deletion.

For this layout, you let OpenVPN do all the work. Use the "ifconfig" method to configure the interface, and the "tun-mtu" parameter to set the network-wide MTU. Then, you have one of two options:

This approach is more complicated and (historically) more failure-prone than static interfaces. The three advantages are

  1. centralising the configuration by putting the interface data into the OpenVPN files;
  2. notifying Quagga immediately when an OpenVPN link dies and takes the tunnel with it;
  3. it will work for older kernels (early 2.4 series) that lacked persistent tunnel support.

Note that with regards to (2) above, the most common cause is a timeout (with "ping-restart"), which the OSPF protocol would notice long before OpenVPN.

Also, please note: This approach is only theoretical, has never been tested, and may not work. In particular, it depends on how early in the initialisation OpenVPN sets the tunnel MTU. (When I used it, Zebra/Quagga OSPFd would only check the MTU on the device once, and this would put both itself and the adjacent node into an "MTU mismatch" failure mode.)

Success (or failure) stories are welcome, particularly regarding this method.

OpenVPN Configuration

First, decide on a name for the tunnel. You can use the same name on both ends, or different; this is just a symbolic name. I use the hostname of the machine on the other end, so each end's configuration is named after the other.

Second, generate a secret key using "openvpn --genkey --secret /etc/openvpn/name.key". Copy this to the target host, naming it appropriately on that end.

This guide assumes OpenVPN stuff goes in /etc/openvpn, which is the way Debian does it.

Third, create /etc/openvpn/name.conf. Supply options according to the openvpn(8) man page.

Mandatory options:

Other useful options:

Verify VPN Links

At this point, once you bring up an interface and the associated OpenVPN daemon, each end should be able to ping the other. If you need a reminder regarding the remote IP, running "ifconfig" on a VPN interface will list it under "P-t-P".

It's suggested you ping from both ends (e.g. ping machine A from machine B, and ping machine B from machine A). This can expose some rare glitches I've seen.

Routing Setup

Finally, it's time to configure Zebra or Quagga. I prefer a recent version of Quagga, since it's a more active community-based fork, and has fixed several bugs in Zebra. However, I have not used recent versions of Zebra as a result. I'll refer to them collectively as 'Quagga' for simplicity and accuracy, although the configuration should be identical for Zebra.

When running Quagga in OSPF mode, there are actually two different daemons running. The main daemon (still called 'zebra' even in Quagga) handles static routing and provides required services to the actual OSPF daemon ('ospfd').

To configure each daemon, you have one of two choices: You can either edit the configuration files and restart it, or you can telnet to port 'zebra' (2601) or 'ospfd' (2604) and enter commands as per the Cisco command set. The latter has the advantage of context-sensitive syntax help, but it's also more complicated than just editing a file. Use whichever you're more comfortable with. I prefer using commands, but I will be talking about editing the files because that's the simplest way.

!
! Zebra configuration saved from vty
!   2005/04/04 11:47:04
!
hostname kitson-zebra
password 8 xxxxxxxxxxxxx
enable password 8 xxxxxxxxxxxxx
log file /var/log/quagga/zebra.log
log record-priority
service password-encryption
!
debug zebra events
debug zebra kernel
!
interface eth0
 link-detect
!

...a bunch of empty 'interface' sections...

! ip route 10.0.0.0/8 dummy0 ip route 192.168.0.0/16 dummy0 ip route 192.168.65.0/24 dummy0 ! access-list term permit 127.0.0.1/32 ! ! line vty access-class term exec-timeout 0 0 no login !