Wireguard ist eine moderne VPN-Lösung, die vor allem durch die einfache Konfiguration und hohe Performance immer beliebter wird.Behauptet wird zudem, dass Wireguard die sicherste VPN-Lösung sei,unter anderem dadurch, dass der Source-Code nur einen Bruchteil des Umfangs anderer Lösungen habe.Wireguard ist Cross-Plattform und damit auf quasi allen modernen Betriebsystemen wie Linux, Windows, Mac, Android und iOS verfügbar.
In diesem Post möchte ich auf die grundlegende Einrichtung von Wireguard auf einem Linux-Server eingehenund dabei die wesentlichen Konzepte und Parameter erklären.
Grundwissen
Wireguard arbeitet technisch mit sogenannten Peers.Es wird immer eine verschlüsselter Punkt-zu-Punkt (End-to-End) Tunnel zwischen zwei Peers aufgebaut.Anders als bei anderen VPN-Lösungen wird daher nicht zwischen Clients und Servern unterschieden.Deshalb ist die Konfiguration von Clients und Servern sehr ähnlich.
Jeder Peer hat dabei ein Schlüsselpaar bestehend aus Private- und Public-Key.Es wird daher eine asymetrische Verschlüsselung verwendet.Der Partner-Peer kennt dabei immer nur den Public-Key, den er zum Verschlüsseln der Daten nutzt.Der eigene Private-Key muss geheimgehalten werden und dient zum Entschlüsseln der Daten.Zusätzlich kann ein Passwort (Pre-shared Key) eingesetzt werden, das beiden Peers bekannt sein muss.
Soll nun ein Peer als “Wireguard VPN-Server” dienen, heißt das lediglich, dass dieser Peer mehrere Peering-Partner hat.Es werden also mehrere, voneinander unabhängige Tunnelverbindungen aufgebaut, die eine Stern-Struktur bilden.Der Server-Peer verwendet dabei dasselbe Schlüsselpaar, wobei alle Client-Peers denselben Public-Key kennen.
Ob über den Server-Peer nun Kommunikation zwischen den einzelnen Client-Peers stattfinden kannoder ob die Client-Peers über den Server-Peer im Internet surfen könnenist eine Frage der Routing- und Firewallkonfiguration des Hostsystems und nicht von Wireguard.Es sollte daher ein gewisses Grundwissen über IP-Routing und Firewalls vorhanden sein.
Konfiguration des Server-Peers
Wireguard ist als Modul im Linuxkernel umgesetzt.Es ist also nötig einen aktuellen Kernel mit mindestens Version5.6 zu verwenden.In jedem Fall müssen die Kommandozeilen-Tools wg
und wg-quick
installiert werden, über die der Tunnel konfiguriert werden kann.Bei Debian- und Ubuntu-Systemen heißt das Paket einfach wireguard-tools
und kann mit sudo apt install wireguard-tools
installiert werden.
Zur Konfiguration wird die Configdatei /etc/wireguard/wg0.conf
angelegt.Der Name ist (bis auf die Dateiendung) frei wählbar, aber entspricht dem Namensschema des offiziellen Quickstart-Guides.Hier ein Beispiel inkl. Regeln für das Internetrouting (siehe Abschnitt Internetrouting).
# Konfiguration des SERVER-Peers (/etc/wireguard/wg0.conf)[Interface]Address = 10.0.1.1/24ListenPort = 54321PrivateKey = kNSNZaYfpIFP0GOlliBnibWudKg1FroH7UB+Pp+s2HI=PostUp = iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADEPostDown = iptables -t nat -D POSTROUTING -o enp0s3 -j MASQUERADE# Client A[Peer]PublicKey = 6FnnYgd8MgKz7kwfCVAT0EZZ3Zcz3FzctANcl1M52CE=AllowedIPs = 10.0.1.20/32# Client B[Peer]PublicKey = e4NgtjOkjViYqhDdKXC3T7KQYJcUsVV15KGrlg3N20o=AllowedIPs = 10.0.1.21/32
Bei [Interface]
beginnt die Konfiguration des eigenen Tunnelendpunkts.Dazu gehören:
Address
: Die eigene IP-Adresse im Wireguard-Tunnel. Mit dem/24
wird ein Adressbereich definiert, was für das Routing relevant sein wird.ListenPort
: Der UDP Netzwerk-Port, über die die Tunnelverbindung läuft. Kann bei Client-Peers weggelassen werden.PrivateKey
: Der eigene Private-Key. Der Public-Key kann daraus generiert werden und wird daher nicht eingestellt.PostUp
undPostDown
: Befehle, die bei Start und Stopp ausgeführt werden. Das wird im folgenden Abschnitt genauer erklärt.
Anschließend wird mit jedem [Peer]
-Abschnitt ein Partner-Peer konfiguriert.Die Konfiguration ist in der Regel sehr einfach und besteht hier nur aus zwei nötigen Parametern:
PublicKey
: Der Public-Key des Partner-Peers, hier also eines Client-Peers.AllowedIPs
: Die Tunnel IP-Adresse(n), die mit diesem Peer verknüpft werden. Dies kann nur ein Client-Peer sein oder ein ganzes Netz.
Ein Schlüsselpaar kann mit dem folgenden Befehl generiert werden:
umask 077 # damit erzeugte Dateien nur für den Besitzer lesbar sindwg genkey > privatekey.txtwg pubkey < privatekey.txt > publickey.txt
Um Wireguard zu Starten kann das Tool wg-quick
verwendet werden, das die Tunnel-Schnittstelle anlegt, die IP-Adresse setzt und PostUp
und PostDown
Befehle ausführt.Die dabei ausgeführten Befehle werden dabei angezeigt und es werden ggf. Fehler sichtbar.Beispielausgabe:
$ sudo wg-quick up wg0[#] ip link add wg0 type wireguard[#] wg setconf wg0 /dev/fd/63[#] ip -4 address add 10.0.1.1/24 dev wg0[#] ip link set mtu 1420 up dev wg0[#] iptables iptables -t nat -I POSTROUTING -o enp0s3 -j MASQUERADE
Alternativ kann auch die systemd
-Unit verwendet werden, z.B. sudo systemctl start wg-quick@wg0
.Damit kann auch eine automatische Verbindung bei Systemstart einfach aktiviert werden: sudo systemctl enable wg-quick@wg0
.Der Status kann über den kurzen Befehl wg
angesehen werden:
$ sudo wginterface: wg0 public key: OpQ+Usx1ZyooZWa3tIOvH6CLJwFrS3isEC28NYAaJmc= private key: (hidden) listening port: 54321peer: 6FnnYgd8MgKz7kwfCVAT0EZZ3Zcz3FzctANcl1M52CE= allowed ips: 10.0.1.20/32peer: e4NgtjOkjViYqhDdKXC3T7KQYJcUsVV15KGrlg3N20o= allowed ips: 10.0.1.21/32
Konfiguration eines Client-Peers
Analog zu dem Server-Peer werden auch die Client-Peers eingerichtet.An dieser Stelle soll erneut ein Linux-System konfiguriert werden, um das Prinzip in Textform zeigen zu können.Auf den meisten Betriebssystemen gibt es auch grafische Oberflächen, aber die Konfiguration erfolgt mit den gleichen Parametern.
Der folgende Codeblock zeigt ein Beispiel passend zur zuvor gezeigten Konfiguration für den Server-Peer.
# Konfiguration des Peers "Client A" (/etc/wireguard/wg0.conf)[Interface]PrivateKey = YPc+21+sXiwyiV+nvdBMXrADydX39lFdumPKkDmipEM=Address = 10.0.1.20/24DNS = 8.8.8.8# Server-Peer[Peer]PublicKey = OpQ+Usx1ZyooZWa3tIOvH6CLJwFrS3isEC28NYAaJmc=AllowedIPs = 0.0.0.0/0Endpoint = server.example.com:54321
Auch hier gibt es wieder einen [Interface]
-Abschnitt für den eigenen Tunnelendpunkt.Anders als beim Server-Peer kann hier in der Regel alles bis auf den eigenen Private Key und die eigene Tunneladresse ausgelassen werden.Bei dem Address
Parameter kann hier die Angabe des Netzes (/24
) auch weggelassen werden.Zusätzlich sollte für die Internetnutzung ein DNS-Server über den Parameter DNS
definiert werden.
Bei dem [Peer]
-Abschnitt wird nun der einzige Peer des Clients konfiguriert, nämlich der Server-Peer.Als ein Parameter wird dazu der Public Key des Server-Peers benötigt.Der Parameter AllowedIPs
gibt hier den gesamten IPv4-Adressbereich an (0.0.0.0/0
),damit über den Server-Peer Pakete aus dem gesamten Internet empfangen werden dürfen.Außerdem nutzt wg-quick
diese Information, um das Routing auf dem Client-Hostsystem passend zu konfigurieren.
Der letzte Parameter Endpoint
ist nötig, um überhaupt einen Tunnel zum Server-Peer aufbauen zu können.Über diesen wird der Domain-Name oder die IP-Adresse und der Port des Servers angegeben.Der Port muss mit dem ListenPort
in der Server-Peer Konfiguration übereinstimmen.
Die Verbindung kann wie beim Server aufgebaut werden (siehe oben).Mit dem Befehl wg
kann geprüft werden, ob eine Verbindung aufgebaut ist.In der Ausgabe sieht man, wie viele Daten bereits durch den Tunnel geflossen sind:
$ sudo wginterface: wg0 public key: 6FnnYgd8MgKz7kwfCVAT0EZZ3Zcz3FzctANcl1M52CE= private key: (hidden) listening port: 50072 fwmark: 0xca6cpeer: OpQ+Usx1ZyooZWa3tIOvH6CLJwFrS3isEC28NYAaJmc= endpoint: [2001:db8::4534:ab5f:33bf:7d3c]:54321 allowed ips: 0.0.0.0/0 latest handshake: 45 seconds ago transfer: 4.67 KiB received, 501.36 KiB sent
Auf der Serverseite können außerdem bei endpoint
die aktuellen öffentlichen IP-Adresse der verbundenen Client-Peers gesehen werden.
Internetrouting
Bislang können nur Server und Client miteinander kommunizieren.Um nun den gesamten Internetverkehr durch den Wireguard-Tunnel leiten zu können, muss noch das Routing eingerichtet werden.
Als erstes muss das Weiterleiten von Paketen im Kernel aktiviert werden, was standardmäßig nicht der Fall ist.Dazu sollte z.B. in der Datei /etc/sysctl.d/90-ipforwarding.conf
die folgende Zeile gespeichert werden:
net.ipv4.ip_forward=1
Danach zum Übernehmen entweder den Befehl sudo sysctl --system
ausführen oder den Server neustarten.
Weil der Server und die Clients sich in der Regel die IPv4-Adresse des Servers teilen müssen, muss NAT verwendet werden.Dazu sind in der wg0.conf
die PostUp
und PostDown
Parameter zuständig.Anzupassen ist dort der Name der Netzwerkschnittstelle, über den der Server auf das Internet zugreift, anzupassen.In der gezeigten Configdatei ist das bspw. enp0s3
.
Nicht zuletzt muss die Firewall eingerichtet werden, falls eine verwendet wird.Ich gehe an dieser Stelle davon aus, dass ufw
verwendet wird.Die folgenden Regeln müssen hinzugefügt werden:
# Tunnelendpunkt erreichbar machensudo ufw allow 54321/udp# Daten aus dem Tunnel annehmensudo ufw allow in on wg0# Weiterleiten von Internetverkehrsudo ufw route allow in on wg0sudo ufw route allow out on wg0
Tipps zur Fehleranalyse
Mit wg
und Tools wie ping
kann einfach getestet werden, ob die Verbindung funktioniert.Sollte keine Internetverbindung möglich sein, sollte zunächst geprüft werden, ob der Server-Peer überhaupt erreichbar ist.Ist dies nicht der Fall, sollten als erstes die Konfigurationsdateien genau geprüft werden.
In dem folgenden Beispiel ist zwar der Server-Peer erreichbar, aber keine Internetverbindung möglich:
client$ ping 10.0.1.1PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=8.42 ms^Cclient$ ping 8.8.8.8PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.From 10.0.1.1 icmp_seq=1 Destination Host Prohibited
Das lässt auf ein Routing- oder Firewallproblem schließen.Routingregeln lassen sich mit ip route get <IP-Adresse>
prüfen.Hier sollte für den Client-Peer das Wireguard-Interface herauskommen, z.B. dev wg0
:
<IP-Adresse> dev wg0 table 51820 src 10.0.1.20 uid 0
Auch könnte die Firewall kurz zu deaktiviert und erneut probiert werden.
Für Systeme ohne ufw
, zum Beispiel in der Oracle Cloud, müssen die äquivalenten iptables
Regeln in PostUp
und PostDown
mit untergebracht werden:
PostUp = iptables -I INPUT 1 -i %i -j ACCEPT; iptables -I FORWARD 1 -i %i -j ACCEPT; iptables -I FORWARD 1 -o %i -j ACCEPT; iptables -t nat -I POSTROUTING 1 -o enp0s3 -j MASQUERADEPostDown = iptables -D INPUT -i %i -j ACCEPT; iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp0s3 -j MASQUERADE
Wichtig ist die Angabe einer Regelposition (-I <chain> 1
), da bei den Images der Oracle Cloud die letzte Regel per default das Zurückweisen des Paketes ist (reject-with icmp-host-prohibited
).Alternativ können die Regeln auch in /etc/iptables/rules.v4
eingetragen werden.
Performance-Fix durch Anpassung der MTU
Ich hatte beim Testen feststellen müssen, dass die Wireguard-Tunnel Performance nicht so hoch war wie erwartet.Bei Speedtests kamen so gerade einmal rund 50Mbit/s über meine 250Mbit/s DSL-Leitung durch.Schuld daran war die maximale Paketgröße (MTU), die standardmäßig etwas zu hoch eingestellt war.Dadurch werden die Pakete unnötig oft auf der Strecke aufgeteilt und wieder zusammengepuzzelt, was einen sichtbar großen Overhead erzeugt.
Mit dem Tool iperf3
lässt sich der Durchsatz testen.Auf dem Server wird es einfach mit iperf3 -s
ausgeführt.Auf dem Client mit iperf3 -c <Server-IP>
.Gemessen wird damit der Durchsatz vom Client zum Server (upstream).Die Richtung lässt sich tauschen, indem zusätzlich der Client-Parameter -R
verwendet wird oder der iperf3-Server auf dem VPN-Client gestartet wird.
Mit einer MTU von 1360 Bytes konnte ich fast die volle Bandbreite nutzen.Bei Problemen mit der Performance kann also mit der MTU etwas experimentiert werden.Das Ziel sollte sein die MTU so hoch wie möglich zu wählen, um den Overhead gering zu halten.Durch die gravierenden Performanceunterschiede sollte die Grenze schnell gefunden werden können.Wenn der Durchsatz in Richtung Gbit/s geht, sollte außerdem die CPU-Last beobachtet werden, nicht dass hier der Flaschenhals sitzt.