OpenVPN loadbalanced

Als „Freund“ von OpenVPN habe ich mir Gedanken darüber gemacht wie man einen VPN über zwei unterschiedliche Cipher abwickeln kann. Hintergrund ist, dass wenn man eine Verbindung aufgebrochen hat, weil zum Beispiel der Key gestohlen wurde, sämtlichen Datenverkehr mitlesen kann. In dem folgenden Beispiel zeige ich eine „Konfiguration“ unter FreeBSD die mit zwei OpenVPN-Prozessen auf beiden P2P-Nodes mit unterschiedlichen Ciphern arbeitet. Ein VPN verwendet „AES256-CBC“ und der andere VPN „BF-CBC„. Das Problem hierbei ist wie man die beiden VPN-Tunnel gleichzeitig ohne Routingprotokoll nutzen kann. Bisher habe ich immer TUN-basierte OpenVPN zum Einsatz gebracht die man aber nicht wirklich bündeln kann. Das folgende Beispiel verwendet TAP-basierte VPN-Verbindungen.

Im ersten Schritt werden auf dem „Server-Node“ zwei OpenVPN-Konfigurationen benötigt. Im folgenden nun die Basis-Schritte um OpenVPN mehrfach zu starten. In der „/etc/rc.conf“ werden zwei OpenVPN-Services definiert:

 [SERVER]
 cloned_interfaces="lagg0 tap0 tap1"
 ifconfig_tap0="up"
 ifconfig_tap1="up"
 openvpn1_enable="YES"
 openvpn1_configfile="/usr/local/etc/openvpn1/openvpn.conf"
 openvpn1_dir="/usr/local/etc/openvpn1"
 openvpn1_if="tap"
 openvpn2_enable="YES"
 openvpn2_configfile="/usr/local/etc/openvpn2/openvpn.conf"
 openvpn2_dir="/usr/local/etc/openvpn2"
 openvpn2_if="tap"

Im Verzeichnis „/usr/local/etc/rc.d“ verlinke ich das Original-Startskript mit zwei weiteren Einträgen:

 ln -s /usr/local/etc/rc.d/openvpn /usr/local/etc/rc.d/openvpn1
 ln -s /usr/local/etc/rc.d/openvpn /usr/local/etc/rc.d/openvpn2

Anschliessend werden die beiden Konfigurationsverzeichnisse benötigt. Dazu lege ich beide Verzeichnisse an:

 mkdir -p /usr/local/etc/openvpn1
 mkdir -p /usr/local/etc/openvpn2

In der Datei „/usr/local/etc/openvpn1/openvpn.conf“ erstelle ich für den „Server“ die erste VPN-Konfiguration wie folgt:

 daemon
 port <port>
 proto udp
 tun-mtu 1500
 topology p2p
 dev tap0
 keepalive 10 86400
 remote 10.0.0.2 # Client-LAN/WAN-IP
 local 10.0.0.1
 ifconfig 10.2.0.1 255.255.255.252 # tap0-Tunnel Adresse
 user root
 group wheel
 persist-key
 persist-tun
 comp-lzo yes
 status /var/log/openvpn1-status.log
 log-append /var/log/openvpn1.log
 verb 3
 script-security 2
 ca /usr/local/etc/openvpn1/ca.crt
 cert /usr/local/etc/openvpn1/server-certificate.crt
 key /usr/local/etc/openvpn1/server-certificate.key
 dh /usr/local/etc/openvpn1/dh2048.pem
 verify-x509-name <client.fqdn> name
 cipher aes-256-cbc
 tls-server
 tls-version-min 1.2
 tls-auth /usr/local/etc/openvpn1/vpn.key 0

Für den zweiten OpenVPN wird in der Datei „/usr/local/etc/openvpn2/openvpn.conf“ die entsprechende Konfiguration durchgeführt:

 daemon
 port <port2>
 proto udp
 tun-mtu 1500
 topology p2p
 dev tap1
 keepalive 10 86400
 remote 10.0.0.2 # Client-LAN/WAN-IP
 local 10.0.0.1
 ifconfig 10.3.0.1 255.255.255.252 # tap1-Tunnel Adresse
 user root
 group wheel
 persist-key
 persist-tun
 comp-lzo yes
 status /var/log/openvpn2-status.log
 log-append /var/log/openvpn2.log
 verb 3
 script-security 2
 ca /usr/local/etc/openvpn2/ca.crt
 cert /usr/local/etc/openvpn2/server-certificate2.crt
 key /usr/local/etc/openvpn2/server-certificate2.key
 dh /usr/local/etc/openvpn2/dh2048.pem
 verify-x509-name <client.fqdn> name
 cipher bf-cbc
 tls-server
 tls-version-min 1.2
 tls-auth /usr/local/etc/openvpn2/vpn.key 0

Jetzt die beiden OpenVPN-Server mit den Befehlen „service openvpn1 start ; service openvpn2 start“ starten und ggf. das Logfile überprüfen. Die obige Konfiguration wurde mit einem OpenVPN 2.3.6 via „pkg install“ getestet. Bitte ggf. die MTU-Werte für das L2-Medium anpassen (z.B. ADSL etc.).

Es sollten nun folgende Interfaces zu sehen sein:

 tap0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
 options=80000<LINKSTATE>
 ether 00:bd:3f:e1:08:00
 inet 10.2.0.1 netmask 0xfffffffc broadcast 10.2.0.3
 nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
 media: Ethernet autoselect
 status: active
 Opened by PID 1924
 tap1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
 options=80000<LINKSTATE>
 ether 00:bd:3f:e1:08:00
 inet 10.3.0.1 netmask 0xfffffffc broadcast 10.3.0.3
 nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
 media: Ethernet autoselect
 status: active
 Opened by PID 1944

Jetzt werden beide TAP-Interfaces der OpenVPN-Server miteinander „verbunden“. Dazu verwende ich das Kernel-Modul „if_lagg.ko„. Mit dem folgenden Befehl wird ein neues LAGG-Interface erstellt:

ifconfig lagg0 create # aka ‚cloned_interfaces=“lagg0″‚ in /etc/rc.conf

Jetzt muss das LAGG noch konfiguriert werden:

ifconfig lagg0 inet 10.1.0.1/24 laggproto roundrobin laggport tap0 laggport tap1 up

Anmerkung zu „laggproto“-Option:
Der Parameter „roundrobin“ verteilt die IP-Pakete über beide Tunnel. Bei TCP-Verbindungen muss man jedoch beachten, dass es zu sog. „Fast-Retransmits“ kommen kann. Diese erkennt man z.B. mit tcpdump an „DUP!“-Paketen. Unter Umständen verliert man also, durch unterschiedliche Laufzeiten durch beide Tunnel, an Netto-Bandbreite weil Pakete bei höherer Last wiederholt übertragen werden müssen. Da gibt es leider keinen Workaround dazu. Alternativ kann man „loadbalanced“ als Parameter für „laggproto“ verwenden. Hier bleibt die TCP-Verbindung auf dem selben Tunnel. Der Nachteil meiner Meinung dabei ist die Tatsache, dass die „Zerstreuung“ über beide Cipher ausbleibt. Eine neue/weitere Session wird dann jedoch auf dem anderen Tunnel übertragen.

Mit „ifconfig lagg0“ sieht das „Link-Aggregation“-Interface dann wie folgt aus:

 lagg0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
 options=80000<LINKSTATE>
 ether 00:bd:3f:e1:08:00
 inet 10.1.0.1 netmask 0xffffff00 broadcast 10.1.0.255
 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
 media: Ethernet autoselect
 status: active
 laggproto roundrobin lagghash l2,l3,l4
 laggport: tap1 flags=4<ACTIVE>
 laggport: tap0 flags=4<ACTIVE>

Natürlich muss es kein /24 Netz sein. Eigentlich hasse ich die generelle Verwendung von /24-Netzen für jeden Art von Netzwerkverbindung aber das ist hier ja nur ein Beispiel. Wie man bereits erkennen kann ist das „lagg0“-Interface nun „UP“ und beide OpenVPN-Tunnel-Ports daran gebunden. Wichtig ist der Status „active“ !

Jetzt geht es mit der Konfiguration der „Client„-Seite weiter. Es sollten die selben vorbereitende Schritt wie beim Server durchgeführt werden (mkdir etc.).

In der Datei „/usr/local/etc/openvpn1/openvpn.conf“ erstelle ich für den „Client“ die erste VPN-Konfiguration wie folgt:

 daemon
 port <port>
 proto udp
 tun-mtu 1500
 topology p2p
 dev tap0
 keepalive 10 86400
 remote 10.0.0.1 # Server-LAN/WAN-IP
 local 10.0.0.2
 ifconfig 10.2.0.2 255.255.255.252 # tap0-Tunnel Adresse
 user root
 group wheel
 persist-key
 persist-tun
 comp-lzo yes
 status /var/log/openvpn1-status.log
 log-append /var/log/openvpn1.log
 verb 3
 script-security 2
 ca /usr/local/etc/openvpn1/ca.crt
 cert /usr/local/etc/openvpn1/client-certificate.crt
 key /usr/local/etc/openvpn1/client-certificate.key
 dh /usr/local/etc/openvpn1/dh2048.pem
 verify-x509-name <server.fqdn> name
 cipher aes-256-cbc
 tls-client
 tls-version-min 1.2
 tls-auth /usr/local/etc/openvpn1/vpn.key 1

Für den zweiten OpenVPN des Clients wird in der Datei „/usr/local/etc/openvpn2/openvpn.conf“ die entsprechende Konfiguration durchgeführt:

 daemon
 port <port2>
 proto udp
 tun-mtu 1500
 topology p2p
 dev tap1
 keepalive 10 86400
 remote 10.0.0.1 # Server-LAN/WAN-IP
 local 10.0.0.2
 ifconfig 10.3.0.2 255.255.255.252 # tap1-Tunnel Adresse
 user root
 group wheel
 persist-key
 persist-tun
 comp-lzo yes
 status /var/log/openvpn2-status.log
 log-append /var/log/openvpn2.log
 verb 3
 script-security 2
 ca /usr/local/etc/openvpn2/ca.crt
 cert /usr/local/etc/openvpn2/client-certificate2.crt
 key /usr/local/etc/openvpn2/client-certificate2.key
 dh /usr/local/etc/openvpn2/dh2048.pem
 verify-x509-name <server.fqdn> name
 cipher bf-cbc
 tls-client
 tls-version-min 1.2
 tls-auth /usr/local/etc/openvpn2/vpn.key 1

Fertig. Nun auch auf dem Client beide OpenVPN-Prozesse starten. Anschließend sollte ein PING zwischen 10.3.0.1 und 10.3.0.2 möglich sein. Ggf. ein Regelwerk im IPFW bzw. PF anpassen damit der Tunnel-Traffic möglich ist. In meiner Testumgebung auf einem ESXi 5.5 habe ich mit ‚cat /dev/zero | ssh 10.3.0.2 „cat > /dev/null„‚ auf dem Server und gleichzeitig mit ‚cat /dev/zero | ssh 10.3.0.1 „cat > /dev/null„‚ auf dem Client Übertragungsraten von ca. 35 Mbyte/Sekunde erreicht (verteilt auf beide TAP-Interfaces). Im folgenden Beispiel sendet jedoch nur der Client zum Server:

      Interface           Traffic               Peak                Total
          lagg0  in     11.446 MB/s         11.446 MB/s            6.365 GB
                 out   622.175 KB/s        622.175 KB/s            4.168 GB

           tap1  in      5.222 MB/s          5.222 MB/s            3.184 GB
                 out   287.959 KB/s        287.959 KB/s            2.084 GB

           tap0  in      5.232 MB/s          5.232 MB/s            3.185 GB
                 out   287.921 KB/s        287.921 KB/s            2.084 GB

            lo0  in      0.000 KB/s          0.000 KB/s            0.328 KB
                 out     0.000 KB/s          0.000 KB/s            0.328 KB

            em1  in     11.162 MB/s         11.162 MB/s            7.014 GB
                 out     1.167 MB/s          1.167 MB/s            4.790 GB