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 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.
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.
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. :)
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.
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.
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 220.127.116.11, 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 18.104.22.168, 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.
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.*.
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.
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:
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
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.
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.
Other useful options:
proto tcp -- I strongly recommend against using TCP for VPNs, or anything else that tunnels TCP. That being said, there are rare situations where TCP is the only option. Don't specify "lport" if you use this.
For a roaming station like my laptop, I use several UDP tunnels, and a single TCP tunnel in case I find myself on a network that dislikes UDP.
float -- This allows any host to connect if they have the right key. You probably want to use this if the other end's IP might change.
If you refer to your remote peer via a DNS address, the address is looked up at start time. The IP is used from then on. If the remote peer's address and DNS address both change, they'll still be denied access until "ping-restart" causes a restart and a new lookup.
Though I could use this on any link (dynamic or static) if I wanted, I purposely avoid using it on static links, forcing the other end to use the right IP address for added security.
persist-remote-ip -- With "float", this keeps track of the peer's last known IP. You also probably want this if the other end's IP might change.
If both ends are dynamic and use this option, each end can remind the other where it can be reached at, even after "ping-restarts".
I almost always use this whenever I use "float". The only exception is for my wireless VPN links. Since they use a client-server model, I use "float" on the server and don't give it either "remote" or "persist-remote-ip". This means that after a restart, it remains silent and doesn't broadcast VPN packets through the air for days.
ping -- This option helps keep the tunnel alive and busy, and also allows for automatic restarts (with "ping-restart") when a link is lost.
I use 60, but it probably doesn't get used much, since OSPF sends packets every 10 seconds.
ping-restart -- This causes OpenVPN to restart and attempt to renegotiate whenever it receives no packets for the specified number of seconds. (It should be larger than "ping".)
This option helps a lot for dynamic IPs with DNS resolution, since it's the only option that allows OpenVPN to change its target IP (by forcing a DNS lookup again). It might also help recover from negotiation errors.
I have this set to 600.
resolv-retry -- If connecting using DNS resolution, it's a good idea to set this quite high, or infinite. Otherwise, the tunnels could just exit in the case of a temporary DNS failure.
I have this set to "infinite" on DNS-based links.
link-mtu -- This limits the maximum UDP packet size that can be sent. This prevents annoying MTU issues when used with PPP-over-Ethernet (typically residential DSL). If your interface is pre-created, this has limited use.
Although redundant in my setup, I currently set this to the highest value supported by a link in both directions. For most links, this is 1500, but DSL-based links are limited to 1492. This helps remind me what the capabilities of a link are, even if I'm unlikely to ever use them, and acts as a hard upper bound in case OpenVPN does anything weird.
tun-mtu -- This is an alternate way of setting the MTU. Use this for the "Dynamic Interface" configuration mentioned above. The link MTU is set to the tunnel MTU plus the VPN overhead. (Specify one of "link-mtu" or "tun-mtu", not both.)
It probably makes more sense to always use "tun-mtu" instead of "link-mtu" and set it to the same MTU defined for the tunnel device above (1300 in the example). It may even be possible to omit it, but I haven't tested this.
cd -- This specifies the root for relative paths in the configuration file.
Debian automatically specifies "cd /etc/openvpn", so I can ignore this option.
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.
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 !