VPN mit WireGuard

Die neue VPN-Technologie WireGuard ist eine völlig neue Softwarelösung, die effizient und von Altlasten befreit ist. Linus Torvalds lobte das Projekt, es sei gegegenüber dem Horror von OpenVPN und IPSec ein Kunstwerk.

Im einfachsten Falle kann als Testsszenario das eigene LAN ohne Routing oder Subnetting herhalten.

Dabei haben wir dann insgesamt folgende IP-Konfiguration vorliegen:

  1. VPN-Server (Debian 10)

  • Reale NIC enp0s3: 10.21.21.233 (manuell hinterlegt, muss der Client später angeben!)

  • Virtuelles Interface wg0: 10.20.40.1 (wird beim VPN-Start automatisch zugewiesen)

  1. VPN-Client (Artix Linux)

  • Reale NIC enp0s3: 10.21.21.201 (eher unwichtig)

  • Virtuelles Interface wg0: 10.20.40.2 (wird beim VPN-Start automatisch zugewiesen)

Das folgende Tutorial basiert auf https://www.ericlight.com/wireguard-part-one-installation.html

Installation

Für Debian 10 gibt es noch keine fertigen Pakete, es ist aber gut möglich, die Sid-Quellen anzuzapfen, wobei ‚buster‘ die höhere Priorität behalten soll:

root@d10:~# echo 'deb http://ftp.tu-chemnitz.de/debian/ sid main' >> /etc/apt/sources.list
root@d10:~# PS2=""
cat > /etc/apt/preferences.d/mypinnings <<\EOF
Package: *
Pin: release n=buster
Pin-Priority: 700

Package: *
Pin: release n=sid
Pin-Priority: 600
EOF
root@d10:~#
root@d10:~# apt-get update
...
root@d10:~# apt-get install wireguard
...

Bei Ubuntu 20.04 LTS sind die betreffenden Pakete schon vorhanden, also einfach apt-get install wireguard ausführen, genauso sieht es bei Artix/Arch Linux aus, dort reicht pacman -Sy wireguard-tools aus. Auch bei SuSE Leap 15.2 gibt es ein Paket, das mit zypper insgesamt wireguard-tools auf die Platte gelangt.

Konfiguration

Die Konfiguration geht recht einfach von statten. Es muss im Prinzip auf beiden Seiten dasselbe getan werden, nur dass der Client zusätzlich noch die reale IP-Adresse des VPN-Servers mitttels Endpoint spezifizieren muss.

VPN-Server (-Gateway)

Zuerst nehmen wir uns den Server vor, hier ist das speziell Debian 10:

root@d10:~# cd /etc/wireguard/
root@d10:/etc/wireguard# umask 077
root@d10:/etc/wireguard#
root@d10:/etc/wireguard# # Dateiheader erzeugen:
root@d10:/etc/wireguard# printf "[Interface]\nPrivateKey = " > wg0.conf
root@d10:/etc/wireguard#
root@d10:/etc/wireguard# # Privaten Schlüssel erzeugen, an die Config anhängen sowie den öffentlichen extrahieren:
root@d10:/etc/wireguard# wg genkey | tee -a wg0.conf | wg pubkey > publickey
root@d10:/etc/wireguard#

Damit steht in der Datei wg0.conf erst einmal dies drin:

[Interface]
PrivateKey = MJG6qhHVtLfWNe45Okmnr95SZKQQpuExA+5D9FgM71g=

Wir kontrollieren nun das Bisherige und geben schon mal den publickey aus, den wir später auf der anderen Seite beim Peer einfügen müssen:

root@d10:/etc/wireguard# ls -l
insgesamt 8
-rw------- 1 root root 45 Okt 20 11:52 publickey
-rw------- 1 root root 70 Okt 20 11:52 wg0.conf
root@d10:/etc/wireguard#
root@d10:/etc/wireguard# cat publickey
XBhqgxfvLdnMQy9333zAVqofi1OamozZIUz6ueyuXAo=
root@d10:/etc/wireguard#

Schließlich editieren wir die Datei /etc/wireguard/wg0.conf und ergänzen sie, dass sie wie folgt aussieht (PublicKey bleibt vorerst leer):

[Interface]
PrivateKey = MJG6qhHVtLfWNe45Okmnr95SZKQQpuExA+5D9FgM71g=
ListenPort = 12345
Address = 10.20.40.1/24

[Peer]
PublicKey =
AllowedIPs = 10.20.40.0/24

Dabei geben wir mit Address = ... an, welche IP-Adresse die virtuelle VPN-Schnittstelle erhalten soll, mit ListenPort = ... legen wir die Portnummer fest, die für den Socket benutzt werden soll und konfigurieren bei [Peer] die Eigenschaften des Gegenübers, also unseres Clients, dessen öffentlicher Schlüssel uns noch nicht vorliegt. Und mit AllowedIPs = ... definieren wir das Netzwerk, aus dem die Anfragen akzeptiert und geroutet werden sollen.

VPN-Client

Nun ist Artix mit der Konfiguration dran. Vorher müssen wir natürlich die Software mittels pacman -Sy wireguard-tools installiert haben:

[artix-wks ~]# cd /etc/wireguard/
[artix-wks wireguard]# umask 077
[artix-wks wireguard]#
[artix-wks wireguard]# # Dateiheader erzeugen:
[artix-wks wireguard]# printf "[Interface]\nPrivateKey = " > wg0.conf
[artix-wks wireguard]#
[artix-wks wireguard]# # Privaten Schlüssel erzeugen, an die Config anhängen sowie den öffentlichen extrahieren:
[artix-wks wireguard]# wg genkey | tee -a wg0.conf | wg pubkey > publickey
[artix-wks wireguard]#
[artix-wks wireguard]# # Öffentlichen Schlüssel ausgeben (für späteres Einbinden weiter unten beim Peer):
[artix-wks wireguard]# cat publickey
+IgQQFYdJi5e467bp1c6Tv9Uvp+I2e+Gn62u2v6oDEY=
[artix-wks wireguard]#

Wir editieren wieder die Datei /etc/wireguard/wg0.conf, so dass sie schließlich folgenden Inhalt aufweist (den öffentlichen Schlüssel des Servers haben wir ja weiter oben extrahiert und können ihn gleich bei [Peer] -> PublicKey eintragen):

[Interface]
PrivateKey = gK4Zsam/sKiHlMuK7/8N3+1TEGH5t6ocPO9aVNnhO0s=

ListenPort = 12345
Address = 10.20.40.2/24

[Peer]
PublicKey = XBhqgxfvLdnMQy9333zAVqofi1OamozZIUz6ueyuXAo=
Endpoint = 10.21.21.233:12345
AllowedIPs = 10.20.40.0/24

In diesem letzten Abschnitt ist neben den oben beschriebenden Dingen der wichtige Eintrag Endpoint = ... hinzugekommen. Damit wird dem Client mitgeteilt, unter welcher realen (öffentlichen) IP-Adresse der VPN-Server zu erreichen ist (Zum Vergleich: OpenVPN verwendet hierfür remote ...).

Server-Konfiguration vervollständigen

Was auf dem Debian-System noch fehlt, ist der soeben erzeugte, clientseitige publickey von Artix, der in der Datei /etc/wireguard/wg0.conf unter [Peer] eingetragen werden muss. Diese Datei sieht schlussendlich so aus:

[Interface]
PrivateKey = MJG6qhHVtLfWNe45Okmnr95SZKQQpuExA+5D9FgM71g=

ListenPort = 12345
Address = 10.20.40.1/24

[Peer]
PublicKey = +IgQQFYdJi5e467bp1c6Tv9Uvp+I2e+Gn62u2v6oDEY=
AllowedIPs = 10.20.40.0/24

Damit haben wir nun die PublicKeys des jeweils anderen an der passenden Stelle hinterlegt.

Inbetriebnahme

Zuerst bringen wir den Server an den Start:

root@d10:~# wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.20.40.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
root@d10:~#
root@d10:~# ip -4 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
    valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 10.21.21.233/24 brd 10.21.21.255 scope global enp0s3
    valid_lft forever preferred_lft forever
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 10.20.40.1/24 scope global wg0
    valid_lft forever preferred_lft forever
root@d10:~#

Und jetzt ist der Client dran:

[artix-wks wireguard]# wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.20.40.2/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[artix-wks wireguard]#
[artix-wks wireguard]# ip -4 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 brd 127.255.255.255 scope host lo
    valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    inet 10.21.21.201/24 brd 10.21.21.255 scope global enp0s3
    valid_lft forever preferred_lft forever
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 10.20.40.2/24 scope global wg0
    valid_lft forever preferred_lft forever
[artix-wks wireguard]#
[artix-wks wireguard]#

Der Aufbau des verschlüsselten Kanals muss von Client initiiert werden; wenn der Server den Tunnelaufbau anschieben möchte, kommt es zum Fehler:

root@d10:~# ping -c2 10.20.40.2
PING 10.20.40.2 (10.20.40.2) 56(84) bytes of data.
From 10.20.40.1 icmp_seq=1 Destination Host Unreachable
ping: sendmsg: Es ist eine Zieladresse notwendig
From 10.20.40.1 icmp_seq=2 Destination Host Unreachable
ping: sendmsg: Es ist eine Zieladresse notwendig

--- 10.20.40.2 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 23ms

root@d10:~#

Wenn aber wie üblich der Client als Initiator auftritt, funktioniert es:

[artix-wks wireguard]# ping -c2 10.20.40.1
PING 10.20.40.1 (10.20.40.1) 56(84) bytes of data.
64 Bytes von 10.20.40.1: icmp_seq=1 ttl=64 Zeit=2.45 ms
64 Bytes von 10.20.40.1: icmp_seq=2 ttl=64 Zeit=0.503 ms

--- 10.20.40.1 ping statistics ---
2 Pakete übertragen, 2 empfangen, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.503/1.478/2.454/0.975 ms
[artix-wks wireguard]#

Und nun klappt der Ping auch vom Server aus:

root@d10:~# ping -c2 10.20.40.2
PING 10.20.40.2 (10.20.40.2) 56(84) bytes of data.
64 bytes from 10.20.40.2: icmp_seq=1 ttl=64 time=0.613 ms
64 bytes from 10.20.40.2: icmp_seq=2 ttl=64 time=0.476 ms

--- 10.20.40.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.476/0.544/0.613/0.072 ms
root@d10:~#

Fehlersuche

Wenn eine Analyse der Verschlüsselung im physischen Netzwerk erfolgen soll, es also keinen Router als „Man-In-The-Middle“ gibt, ist das Mitlesen schwieriger. Das Hauptproblem: Switche schalten 2 Teilnehmer direkt durch, ein 3. sieht nichts! Siehe dazu:

Ein weiteres Problem taucht auf, wenn Logging erforderlich wird. Unter https://serverfault.com/questions/1020279/how-to-see-authentication-logs-for-wireguard wird als Lösung für Kernel > 5.6 mit Unterstützung von Dynamic Debugging folgendes empfohlen (ungetestet):

modprobe wireguard
echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control

Danach sollte dmesg oder journalctl Aufschluss über die Vorgänge geben.

Have 'a lot of fun…