OpenVPN mit Gigabit

Nach der Sicherheitslücke bei Juniper Ende 2015 und der gestern bekanntgewordenen SSH-Lücke bei Fortinet stellt sich die Frage nach Alternativen für VPN-Verbindungen. Privat nutze ich openVPN und habe mich gefragt, wie steht es eigentlich um die Performance. Nach einem etwas veralteten Artikel in der Community habe ich diese Werte mal als Anlass genommen, um meine eigenen Messungen zu machen. Zwei Erkenntnisse gleich zu Beginn:

  1. Wenn UDP geht, ist es immer TCP vorzuziehen. Über das Internet hatte ich mit TCP-Tunneln immer Performance-Probleme. Ich kam nie wirklich über 8 MBits Durchsatz. Das liegt an der Natur des Protokolls. Wenn man TCP über einen TCP-Tunnel überträgt, müssen selbst die ACK-Pakete auf der inneren Schicht mit ACK-Packeten bestätigt werden. Und da bereits auf der inneren Ebene diese Überprüfung stattfindet, kann durch die Nutzung von UDP auf der äußeren Tunnel-Ebene ein großer Performance-Gewinn erzielt werden.
  2. Neuere CPUs sind immer besser. Gerade im LAN ist häufig nicht das Netzwerk, sondern der Prozessor der Flaschenhals, da dieser die Verschlüsselung durchführt. Seit ein paar Jahren gibt es die sog. AES-NI-Erweiterung für Prozessoren. Diese beschleunigen im Prozessor die AES-Verschlüsselung durch eigene Instruktionen, sodass die Verschlüsselung nicht mehr durch Software im Prozessor sondern durch spezielle Funktionen durchgeführt werden kann. Bei Intel-Prozessoren wurden diese Funktionen mit der Westmere-Architektur eingeführt. Im Serverbereich entspricht das Xeon X56xx-Prozessoren oder moderner. Im Consumerbereich sind z.B. Corei7 970 aufwärts, Core i5 650 aufwärts bzw. Core i3 530 und höher. Alle vierstelligen Core-i-Modelle unterstützen also AES-NI bereits. Und seit der openssl-Version 1.0.1. ist die Funktion von AES-NI nativ implementiert und muss nicht mehr (wie im Community-Tutorial) separat aufgerufen werden über einen engine-Parameter.

verwendete Hardware

Um nicht durch das Netzwerk limitiert zu werden, habe ich einen Trunk aus 4x1GBit verwendet, welchen ich im vorherigen Tutorial beschrieben habe. Dieser ermöglicht es mir, mit einer einzigen Session (z.B. einem TCP-Stream von iperf) die vollen 4 GBit zu nutzen. Die zwei verwendeten Server sind von Dell, Modell R710. Der Erste (Server genannt) hat einen Intel Xeon 5677-Prozessor mit 4x 3.46 GHz und 8 logischen Kernen. Der zweite Server (Client genannt) hat zwei Prozessoren der Serie Intel Xeon X5550 mit 4x 2,66 GHz und 8 logischen Kernen. Wie oben in Punkt 2 erwähnt hat somit der Server bereits einen Prozessor, der AES-NI-Instruktionen unterstützt, der Client jedoch nicht.

Für den grundlegenden Aufbau des VPN-Tunnels muss zuerst ein Zertifikat für eine Static-Key-Verbindung auf dem Server generiert werden:

openvpn --genkey --secret secret.key

Die Datei secret.key muss nun auch auf den Client kopiert werden, damit beiden das gleiche Geheimnis kennen. Der folgende Befehl für Server und Client baut einen einfachen openVPN-Tunnel mit BF-CBC-Verschlüsselung auf, den wir später durch weitere Parameter bei den Benchmarks erweitern werden.

Server:
openvpn --dev tun --proto udp --port 11000 --secret secret.key --ifconfig 192.168.222.11 192.168.222.10

Client:
openvpn --dev tun --proto udp --port 11000 --secret secret.key --ifconfig 192.168.222.10 192.168.222.11 --remote *server-IP*

openSSL-Benchmarks

Getestet wurde auf CentOS 7 mit OpenSSL in der Version 1.0.1e. Ohne Angabe der Verschlüsselung nutzt OpnVPN standardmäßig den Blockverschlüsselungsalgorithmus Blowfish (BF-CBC), welchen wir jedoch nicht verwenden wollen, da dieser nicht durch den Prozessor beschleunigt wird. Mit OpenSSL testen wir zunächst den reinen Durchsatz für eine AES-256-Verschlüsselung auf einem CPU-Kern mit folgendem Befehl:

openssl speed -evp aes-256-cbc

Die Ergebnisse sind in KByte/s zu interpretieren („The ’numbers‘ are in 1000s of bytes per second processed.“):

 Server (unterstützt AES-NI):
  type         16 bytes    64 bytes   256 bytes   1024 bytes  8192 bytes
  aes-256-cbc 636248.63k  678124.29k  684753.42k  698684.07k  698389.85k

 Client (kein AES-NI):
  type          16 bytes    64 bytes   256 bytes  1024 bytes  8192 bytes
  aes-256-cbc 195849.68k  215510.40k  220915.29k  221183.66k  220848.13k

Die beiden Prozessoren unterscheiden sich durch den Takt nur geringfügig von der Leistung und basieren auf dem selben Chip-Layout (der Client auf dem ein Jahr älteren Intel Nehalem und der Server auf dessen Shrink Westmere). Trotzdem ist ein enormer Unterschied von 636 MB/s zu 196 MB/s bei 16 Byte Dateigröße zu sehen, was den Leistungssprung durch AES-NI deutlich macht. Ebenfalls sehen wir, dass der reine Prozessor-Durchsatz ausreicht, um einen GBit-Ethernetlink auszulasten.

iperf-Benchmark mit openVPN

Zuerst messen wir die „Rohleistung“ der Verbindung. Hierbei handelt es sich, wie bereits erwähnt um eine 4Gbit-Verbindung. Entsprechend ist das Ergebnis mit iper:

[[email protected] ~]# iperf -c 192.168.3.11
------------------------------------------------------------
Client connecting to 192.168.3.11, TCP port 5001
TCP window size: 325 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.3.12 port 44810 connected with 192.168.3.11 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 4.55 GBytes 3.91 Gbits/sec

Als nächstes bauen wir einen einfachen Tunnel mit den oben genannten Befehlen auf. Gemessen wird zuerst immer in Richtung Client -> Server und anschließend andersrum. Da der Server eine schnellere CPU hat und das verschlüsseln scheinbar etwas rechenaufwändiger ist, als das entschlüsseln, kommen bei den Messungen unterschiedliche Werte raus.

  279 Mbits Client -> Server
  337 MBits Server -> Client

Wie zu sehen ist, wird die Netzwerkverbindung selbst als normale GBit-Verbindung noch nicht ausgelastet. Während der Übertragung war ein Prozessorkern auf 100%, welche den OpenVPN-Prozess bediente. Die CPU ist also der limitierende Faktor.
Als nächstes beenden wir den Tunnel, starten ihn neu und hängen dabei die Parameter –cipher aes-256-cbc an für eine AES-Verschlüsselung:

  377 MBits Client -> Server
  411 MBits Server -> Client

Im Gegensatz zu höchstwahrscheinlich älteren Performance-Messungen ist hier bereits eine deutlich bessere Geschwindigkeit festzustellen. Möchte man auf die erhöhte Sicherheit zu Gunsten von Geschwindigkeit verzichten, kann auch aes-128-cbc verwendet werden.
Im nächsten Schritt werden ein paar Optimierungen vorgenommen:
– die maximale Paketgröße (MTU) wird zunächst auf 6000 Bytes festgesetzt. Somit kann mehr Payload in einem Paket transportiert werden und der Overhead durch den Paket-Header wird kleiner.
– Deaktivierung des Fragmentierungsalgorithmus von OpenVPN. Die Fragmentierung verhindert normalerweise, dass keine UDP-Pakete gesendet werden, welche größer sind als die maximale Paketgröße (Default: 1450 Bytes)
– Deaktivierung der Limitierung der Paketgrößen für TCP-Paketströme, die über den Tunnel übertragen werden
Die entsprechenden Parameter lauten –tun-mtu 6000 –fragment 0 –mssfix 0. Zu beachten ist, dass hier die AES-Verschlüsselung wieder deaktiviert ist:

  561 MBits Client -> Server
  515 MBits Server -> Client

Wie man sieht, sind auch die Anhebung der MTU und Deaktivierung weiterer Limitierungen für einen Performance-Gewinn gut zu gebrauchen. Kombiniert man beides mit –tun-mtu 6000 –fragment 0 –mssfix 0 –cipher aes-256-cbc: steigert sich der Gesamtdurchsatz nochmal und lastet eine einzelne GBit-Verbindung schon fast aus:

  811 MBits Client -> Server
  767 MBits Server -> Client

Im nächsten Versuch erhöhen wir einfach die MTU weiter auf 48000 Bytes mit –tun-mtu 48000 –fragment 0 –mssfix 0 –cipher aes-256-cbc

  1.03 GBits Client -> Server
  1.06 GBits Server -> Client

Jetzt sind wir bereits an einer Grenze angelangt, an der ein normales Gigabit Ethernet gesättigt ist. Eine weitere Vergrößerung der MTU auf 60000 Bytes bringt keinen weiteren Leistungsgewinn, sondern eher einen Einbruch. Das darunterliegende physikalische Netzwerk überträgt mit einer MTU von 9000 (sog. Jumboframes, die maximal mögliche Größe auf einer Gigabit-Eternetverbindung):

  1.06 GBits Client -> Server
  868 MBits Server -> Client

Zum Schluss habe ich noch einmal die Verschlüsselung deaktiviert und OpenVPN als ungesicherten, normalen Datentunnel verwendet mit –tun-mtu 60000 –fragment 0 –mssfix 0 –cipher none –auth none

  2.96 GBits Client -> Server
  3.04 GBits Server -> Client

Fazit

Die verwendeten Prozessoren sind schon wieder etwas in die Jahre gekommen (ca. 2009). Mit aktuellen Prozessoren und ein wenig Optimierung ist es also durchaus möglich, eine Gigabit-Verbindung auszulasten. Diese Messung bezieht sich allerdings nur auf ein LAN, indem die beiden Geräte im selben Subnetz standen und Jumbo-Frames verwenden konnten. Durch eine Internetverbindung könnte die Datenübertragungsrate noch deutlich sinken. Außerdem ist zu beachten, dass meine Optimierungen eher in Richtung von großen Datenpaketen gehen und auch nur mit einer einzigen TCP-Sesssion getestet wurden. Schon bei 5 paralleln Verbindungen über den Tunnel war der Durchsatz leicht geringer.

Ein Problem an OpenVPN ist, dass es (noch) nur Single-Threaded ist und nicht von Mehrkernprozessoren profitieren kann. Allerdings ist es möglich, mehrere Instanzen parallel zu starten und so parallele Tunnel aufzubauen und jede Instanz an einen eigenen Prozessorkern zu binden. Damit dürften in der Summe auch Datenübertragungsraten jenseits von einem Gigabit möglich sein.

Stay Tuned – Støertebeker

Veröffentlicht unter Allgemein, Linux, Messungen

Schreibe einen Kommentar