OpenVPN and high availability

I’m gonna describe how to set up OpenVPN in high availability mode. The configuration will involve Linux bonding, tap interfaces, OpenVPN and iproute2 policy routing rules.

The idea is to set up 2 different tunnels between the 2 systems we want to interconnect. Each tunnel will use a different IP connection. The optimum configuration would involve 2 Internet connections, with different and independent providers (an ADSL and a cable one that don’t share any infraestructure should fit quite right). Linux bonding will provide with the high availability we need. It will also provide  with load balancing, so that we make a better use of the bandwidth we are paying for.  We’ll use some iproute2 rules so that each tunnel’s traffic gets routed via a different interface.

I’ll use as example a setup between a Gentoo system and a Debian one. It should work on any other GNU/Linux distro (adapting the relevant network config files, of course). The Debian system will perform the server role. The Gentoo box will be the client.

We start editing /etc/network/interfaces on the Debian system:

Debian server system - /etc/network/interfaces

# Tap interfaces
auto tap0
iface tap0 inet manual
pre-up openvpn --mktun --dev tap0
post-down openvpn --rmtun --dev tap0

auto tap1
iface tap1 inet manual
pre-up openvpn --mktun --dev tap1
post-down openvpn --rmtun --dev tap1

# Bonding interfaces
auto bond0
iface bond0 inet static
# Change the IP address range as needed!
address 192.168.2.1
netmask 255.255.255.0
slaves tap0 tap1
# We use LACP mode. Ethernet frames will be sent to test
# the end-to-end link up/down status.
bond_mode 802.3ad
bond_miimon 100
# We choose fast LACP rate. The down link events will be detected
# within some seconds.
bond_lacp_rate fast
# layer3+4 policy will balance the load according to network (IP)
# and transport (TCP and UDP) addresses and ports. It's what better
# fits our scenario.
bond_xmit_hash_policy layer3+4
# We add a route to reach the client internal network. Please adapt
# the range as needed!
post-up ip route add 192.168.0.0/16 via 192.168.2.11

The setup of the Gentoo system will be more complex. We’ll have to edit /etc/conf.d/net, /etc/modprobe.d/bond.conf and /etc/iproute2/rt_tables.  We’ll also need some iptables rules to mark the packages so that policy routing works.

Gentoo client system - /etc/conf.d/net

tuntap_tap0="tap"
config_tap0=( "null" )

tuntap_tap1="tap"
config_tap1=( "null" )

slaves_bond0="tap0 tap1"
config_bond0=( "null" )
RC_NEED_bond0="net.tap0 net.tap1"

# We configure the eth interfaces
config_eth0=( "192.168.0.11/24 scope link" )
config_eth1=( "192.168.1.11/24 scope link" )

# We add routes for interface eth0
# We are populating the main and internet1 routing tables
# Change IPV4 addresses as needed!
routes_eth0=( "1.1.1.1 via 192.168.0.1 metric 1"
 "table internet1 192.168.0.0/24"
 "table internet1 default via 192.168.0.1" )

# We add routes for interface eth1
# We are populating the main and internet2 routing tables
# Change IPV4 addresses as needed!
routes_eth1=( "1.1.1.1 via 192.168.1.1 metric 1"
 "table internet2 192.168.1.0/24"
 "table internet2 default via 192.168.1.1" )

# We configure the bonding interface
# Adapt the IP addresses as needed!
config_bond0=( "192.168.2.11/24" )
routes_bond0=( "default via 192.168.2.1" )

# We set iproute2 rules so that each tunnel's traffic gets routed
# via a different interface
postup() {
  # eth0 is the interface associated with our 1st Internet
  # connection
  if [ ${IFACE} = eth0 ] ; then
    /sbin/ip rule add fwmark 1000 lookup internet1;
    # RPF and policy routing don't play together well
    # We disable RPF for eth0
    echo 2 > /proc/sys/net/ipv4/conf/eth0/rp_filter;
  fi

  # eth1 is the interface associated with our 2nd Internet
  # connection
  if [ ${IFACE} = eth1 ] ; then
    /sbin/ip rule add fwmark 1001 lookup internet2;
    echo 2 > /proc/sys/net/ipv4/conf/eth1/rp_filter;
  fi
}

predown() {
  if [ ${IFACE} = eth0 ] ;
    then /sbin/ip rule del fwmark 1000 lookup internet1;
  fi

  if [ ${IFACE} = eth1 ] ;
    then /sbin/ip rule del fwmark 1001 lookup internet2;
  fi

  return 0
}
Gentoo client system - /etc/modprobe.d/bond.conf

options bonding miimon=100 mode=802.3ad lacp_rate=fast\
xmit_hash_policy=layer3+4

Client system - /etc/iproute2/rt_tables
(this settings are GNU/Linux generic, so I leave out
"Gentoo" from the title)

200     internet1
201     internet2

Client system - iptables packet marking rules

# This is the public IPV4 address of the Debian system
# Just an example. Change for the address of your system!
IPV4_IO_PUB=1.1.1.1
iptables -t mangle -A OUTPUT -d ${IPV4_IO_PUB} -p udp --dport 1194 \
-j MARK --set-mark 1000
iptables -t mangle -A OUTPUT -d ${IPV4_IO_PUB} -p udp --dport 1195 \
-j MARK --set-mark 1001

Now we are almost at the end of the configuration. Only the tunnels’ setup is left. Basicly we have to create 2 OpenVPN tunnels in tap mode (or, using the OpenVPN jargon, bridge mode). You may refer to the OpenVPN guide about bridges (but don’t stick to that, cause we won’t use exactly the same configuration).

We begin with the Debian system (the one that performs the server role). We create 2 OpenVPN profiles:

Server system - /etc/openvpn/io1.conf

# Some of the settings here are my particular choices
# You can tweak the configuration as you wish
#
# We use a "better" hash than the default
auth SHA256
ca io1/keys/ca.crt
cert io1/keys/io1.crt
# We use a "better" block cipher than the default
cipher AES-256-CBC
comp-lzo
comp-noadapt
connect-freq 10 5
dev tap0
dh io1/keys/dh4096.pem
# fast-io is supposed to improve performance. I've found no issue
# with it at all. So I enable it.
fast-io
float
group nogroup
keepalive 5 60
key io1/keys/io1.key
mode server
mute 3
mssfix
mtu-disc maybe
passtos
persist-key
persist-tun
# We listen on 1194 UDP
port 1194
proto udp
replay-persist io1/io1.replay
# We set a larger replay window than the default.
# This will help in WiFi-like scenarios.
replay-window 256 30
status io1/io1.status 10
status-version 2
tls-server
user nobody
verb 4
Server system - /etc/openvpn/io2.conf

# Some of the settings here are my particular choices
# You can tweak the configuration as you wish
#
# We use a "better" hash than the default
auth SHA256
ca io2/keys/ca.crt
cert io2/keys/io2.crt
# We use a "better" block cipher than the default
cipher AES-256-CBC
comp-lzo
comp-noadapt
connect-freq 10 5
dev tap1
dh io2/keys/dh4096.pem
# fast-io is supposed to improve performance. I've found no issue
# with it at all. So I enable it.
fast-io
float
group nogroup
keepalive 5 60
key io2/keys/io2.key
mode server
mute 3
mssfix
mtu-disc maybe
passtos
persist-key
persist-tun
# We listen on 1195 UDP
port 1195
proto udp
replay-persist io2/io2.replay
# We set a larger replay window than the default.
# This will help in WiFi-like scenarios.
replay-window 256 30
status io2/io2.status 10
status-version 2
tls-server
user nobody
verb 4

Now we have to configure the Gentoo system (client role).

Client system - /etc/openvpn/internet1.conf

# Some of the settings here are my particular choices
# You can tweak the configuration as you wish
#
# We use a "better" hash than the default
auth SHA256
ca internet1/keys/ca.crt
cert internet1/keys/internet1.crt
# We use a "better" cipher than the default
cipher AES-256-CBC
client
comp-lzo
comp-noadapt
dev tap0
# fast-io is supposed to improve performance. I've found no issue
# with it at all. So I enable it.
fast-io
float
group nobody
key internet1/keys/internet1.key
mssfix
mtu-disc maybe
mute 3
nobind
ns-cert-type server
passtos
persist-key
persist-tun
proto udp
# Change to the public IPV4 address of your server
remote 1.1.1.1 1194
remote-random
replay-persist internet1/internet1.replay
# We set a larger replay window than the default.
# This will help in WiFi-like scenarios.
replay-window 256 30
resolv-retry infinite
status internet1/internet1.status 10
status-version 2
user nobody
verb 4

Client system - /etc/openvpn/internet2.conf

# Some of the settings here are my particular choices
# You can tweak the configuration as you wish
#
# We use a "better" hash than the default
auth SHA256
ca internet2/keys/ca.crt
cert internet2/keys/internet2.crt
# We use a "better" cipher than the default
cipher AES-256-CBC
client
comp-lzo
comp-noadapt
dev tap0
# fast-io is supposed to improve performance. I've found no issue
# with it at all. So I enable it.
fast-io
float
group nobody
key internet2/keys/internet2.key
mssfix
mtu-disc maybe
mute 3
nobind
ns-cert-type server
passtos
persist-key
persist-tun
proto udp
# Change to the public IPV4 address of your server
remote 1.1.1.1 1195
remote-random
replay-persist internet2/internet2.replay
# We set a larger replay window than the default.
# This will help in WiFi-like scenarios.
replay-window 256 30
resolv-retry infinite
status internet2/internet2.status 10
status-version 2
user nobody
verb 4

Leave a comment

Your email address will not be published. Required fields are marked *