Wireguard ist ein schlankes, schnelles und sicheres VPN-Protokoll, welches mehr und mehr die älteren Standards wie OpenVPN ablöst. In dem heutigen Tutorial möchte ich zeigen, wie man zwei oder mehr Netzwerke (also beispielsweise zwei Familien) via Wireguard „verbinden“ kann. Wieso gerade Alpine Linux? Weil diese Distribution extrem schlank und klein ist, kaum Speicherplatz und Ressourcen benötigt und dennoch alles mitbringt, was wir für das Projekt brauchen. Trotzdem benötigen wir allerdings einige Vorraussetzungen:
- Einer der Teilnehmer besitzt eine statische öffentliche IP-Adresse ODER die Möglichkeit für dynamisches DNS
- Beide Teilnehmer haben die Möglichkeit einen kleinen (virtuellen) Server mit Alpine Linux zu installieren
- Kenntnisse im Umgang mit Linux und dem Texteditor „vi“
- Beide Teilnehmer müssen in der Lage sein, den heimischen Router (z.B. Fritz!Box) konfigurieren zu können
- Die Subnetze beider Netzwerke müssen unterschiedlich sein (also beispielsweise hat Netzwerk-A 192.168.100.0/24 und Netzwerk-B hat 192.168.30.0/24)
Als Ausgangssituation nehme ich an, dass bei beiden Teilnehmern ein (virtueller) Server mit einer frischen Alpine Linux-Installation vorhanden ist. Folgende Hardware sollte dafür übrigens mehr als ausreichen:
- 1x x86-x64 CPU
- 512 MiB RAM
- 2 GiB Disk Space
- 1 Gbit Netzwerkanschluss
Software-Installation
Der Server läuft und du bist mit der Shell verbunden (SSH oder direkt)? Dann kanns losgehen! Als allererstes führen wir ein update des Package Managers durch:
doas apk update
Anschließend installieren wir die Pakete wireguard-tools
und iptables
.
doas apk add wireguard-tools iptables
Erledigt! Damit sind alle notwendigen Softwarepakete auch schon installiert!
Systemkonfiguration
Nun müssen wir das Betriebssystem vorbereiten und es darauf einstellen, dass es IPv4-Pakete weiterleiten kann/darf/soll. Dazu führen wir die nächsten zwei Zeilen aus, um eine neue Datei mit der gewünschten Konfiguration zu erstellen und anschließend die Konfigurationsdatei via sysctl
zu laden.
doas echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/ip_forwarding.conf
doas sysctl -p /etc/sysctl.d/ip_forwarding.conf
Wireguard-Konfiguration
Wireguard verwendet zur Authentifizerung Private keys und Public keys. Der Private Key ist wie eine Art Password, das nur dir selbst bekannt sein darf und das du niemals irgendjemanden geben solltest. Damit werden ankommende Netzwerkdaten entschlüsselt, damit sie verarbeitet werden können.
Der Public Key macht genau das Gegenteil: Dieser verschlüsselt die Daten für einen bestimmten Empfänger. Daher muss der Public Key von Netzwerk-A der Wireguard-Konfig von Netzwerk-B bekannt sein (und umgekehrt), damit Netzwerk-B ausgehende Daten korrekt verschlüsseln kann.
Private Key und Public Key generieren
Im ersten Schritt lassen wir uns von Wireguard einen Private Key und einen Public Key erstellen. Dazu müssen wir uns erstmal vorher zum root-User machen und in das Wireguard-Konfig-Verzeichnis wechseln:
doas su
cd /etc/wireguard
Nun werden wir uns die Keys generieren und zeitgleich in Dateien abspeichern:
umask 077; wg genkey | tee privatekey | wg pubkey > publickey
Damit haben wir zwei neue Dateien erstellt: privatekey
und publickey
.
/etc/wireguard # ls -al
total 20
drwx------ 2 root root 4096 Apr 28 20:01 .
drwxr-xr-x 33 root root 4096 Apr 28 19:38 ..
-rw------- 1 root root 45 Apr 28 19:40 privatekey
-rw------- 1 root root 45 Apr 28 19:40 publickey
Den Inhalt beider Dateien können wir uns mit cat
auslesen. Diese beiden Keys brauchen wir auch gleich, wenn wir unsere Wireguard-Konfigurationsdatei schreiben:
cat privatekey
cat publickey
Anschließend können wir wieder zu unserem normalen User werden, mit exit
.
Interface-Konfiguration
Die gesamte Wireguard-Konfiguration für einen Server/Client wird in eine einzelne Datei geschrieben: /etc/wireguard/wg0.conf
. Wir öffnen/erstellen diese Datei nun mit `vi` und schreiben darin unsere Konfig – Hier nun für Netzwerk-A, der als Wireguard-Server dient:
[Interface]
PrivateKey = <PRIVATEKEY von Netzwerk-A>
Address = 10.0.0.1/32
SaveConfig = false
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820
# Netzwerk-B erlauben, sich zu diesem Wireguard-Server verbinden zu dürfen
[Peer]
PublicKey = <Publick Key Netzwerk-B>
AllowedIPs = 10.0.0.2/32, 192.168.30.0/24
PersistentKeepalive = 25
Achtung: In den Zeilen „PostUp“ und „PostDown“ ist eine physikalische/emulierte Netzwerkschnittstelle angegeben, hier heißt sie eth0
. Auf eurem Server kann sie möglicherweise anders heißen. Mit dem Befehl „ip
a“ könnt ihr eure Netzwerkschnittstellen herausfinden.
Für Netzwerk-B sieht die Konfiguration sehr ähnlich aus, nur das wir hier keinen „ListenPort“ benötigen, stattdessen im [Peer]-Bereich einen „Endpoint“-Eintrag (Wireguard-Client):
[Interface]
PrivateKey = <Private Key Netzwerk-B>
Address = 10.0.0.2/32
SaveConfig = false
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <Public Key Netzwerk-A>
Endpoint = vpn.familiemueller.de:51820
AllowedIPs = 10.0.0.0/32, 192.168.100.0/24
PersistentKeepalive = 25
Der eben erwähnte „Endpoint“-Eintrag kann sowohl eine statische öffentliche IP-Adresse sein, oder eine Domain (wie z.b. vpn.familiemueller.de) die über ein DynDNS-Service immer wieder aktualisiert wird. Wichtig ist nach der Domain/IP auch den Port mit anzugeben. Dies ist der selbe Port, der in der Netzwerk-A-Konfiguration in der „ListenPort“-Zeile eingetragen wurde.
Auch erwähnenswert ist, dass in den Zeilen „AllowedIPs“ zwei IP-Adressbereiche angegeben wurden. Der erste Bereich ist eine interne, ausschließlich für Wireguard verwendete IP-Range. Der zweite Bereich ist das Subnetz des jeweiligen anderen Heimnetzwerkes. Im Falle vom Wireguard-Server Netzwerk-A haben wir statt des Adressbereiches 10.0.0.0/32
eine explizite IP-Adresse 10.0.0.2/32
angegeben, um den Wireguard-Client zu zwingen, diese eine IP-Adresse verwenden zu müssen.
VPN-Autostart
Damit die Wireguard-Verbindung beim Start des Linux-Servers hergestellt wird, können wir noch ein sogenanntes OpenRC-Script schreiben, dass vom System dann automatisch ausgeführt wird, sobald es hoch- bzw. heruntergefahren wird.
Dazu öffnen/erstellen wir wieder eine neue Datei mit „vi“: /etc/init.d/wg-quick-wg0
#!/sbin/openrc-run
description="Familie Müller VPN"
depend() {
need net
need localmount
}
start() {
wg-quick up /etc/wireguard/wg0.conf
}
stop() {
wg-quick down wg0
}
```
Das Script lässt sich sehr einfach erklären:
– Bei Systemstart führt es das Programm wg-quick
aus und sagt es, das es ein neues Netzwerk-Interface mit der von uns geschriebenen Konfigurationsdatei starten soll
– Beim Herunterfahren wird ebenfalls wg-quick
ausführt, allerdings soll es nun das Interface wieder schließen.
Damit Alpine Linux weiß, dass es es dieses OpenRC-Script benutzen soll, müssen wir jetzt noch die folgenden Befehle eingeben:
doas chmod a+x /etc/init.d/wg-quick-wg0
doas rc-update add wg-quick-wg0 default
doas rc-service wg-quick-wg0 start
Zur Erklärung: Die neue Script-Datei wird ausführbar gemacht, anschließend mit rc-update
im System registriert und zu guter letzt wird der neue Service schon mal manuell gestartet, damit man sich einen reboot sparen kann.
Statische Route im DSL-Router konfigurieren
Eine statische Route ist ein Vermerk für die Computer und Server, über welchen Weg sie gehen müssen, um ein bestimmtes IP-Netz erreichen zu können. Eine statische Route besteht dabei meistens aus drei Teilen:
- Ziel-Netzwerk
- Subnet-Mask des Ziel-Netzwerkes
- IP-Addresse des Gateways (Wireguard-Linux-Maschine in unserem Fall)
Soweit mir bekannt ist, unterstützt jeder handelsübliche Router statische Routen. Im Falle der Fritz!Box erreiche ich die Einstellungen über „Heimnetz -> Netzwerk -> Netzwerkeinstellungen -> Tabelle für statische Routen -> IPv4 Routen“.
Hier klicke ich auf den Button „Neue IPv4-Route“ und fülle die drei Zeilen entsprechend aus. Für unser Beispiel im Netzwerk-A sähe das so aus:
Anschließend dann noch auf den Button zum Speichern klicken. Dasselbe muss dann natürlich noch im Router von Netzwerk-B gemacht werden, hier aber darauf achten, dass als IPv4-Netzwerk dass Subnetz von Netzwerk-A eingetragen wird und bei Gateway die IP-Adresse des Alpine Linux-Servers von Netzwerk-B.
Port-Forwarding am DSL-Router (Nur Netzwerk-A)
Da unser Wireguard-Server einkommenden Traffic aktzeptieren und verarbeiten muss, benötigen wir eine Port-Weiterleitung von unserem Router (also z.B. die Fritz!Box) zu dem Alpine Linux-Rechner. Das Wireguard-Protokoll verwendet UDP für die VPN-Verbindungen und anhand unserer wg0-Konfig wissen wir, dass die Portnummer 51820 verwendet wird.
Also erstellen wir im heimischen Router eine neue Portfreigabe für den Port 51820 mit dem UDP-Protokoll und verweisen dann auf unseren Wireguard-Server.
Randnotizen
Nahezu alle Schritte sind im Netzwerk-A und Netzwerk-B identisch. Die einzigen wenigen Unterschiede zwischen den beiden Netzwerken sind:
- Die Wireguard-Konfigurationsdatei
/etc/wireguard/wg0.conf
ist unterschiedlich - Die statischen Routen im DSL-Router sind unterschiedlich
- Nur der Wireguard-Server hat eine Portfreigabe im DSL-Router.
Fazit
Sind beide Wireguard-Server korrekt konfiguriert und die wg0-Interfaces gestartet, so ist der Site-to-Site-VPN-Tunnel erfolgreich hergestellt. Wenn ich mich im Netzwerk-B befinde und dort z.B. im Webbrowser die URL http://192.168.100.1
eingebe, erreiche ich den DSL-Router von Netzwerk-A!
Damit lassen sich verschiedene neue Möglichkeiten sicher und privat realisieren, wie zum Beispiel Medien- und Dateiaustausch zwischen zwei Standorten. Jellyfin-Streaming über das eigene Heimnetzwerk hinweg, automatisierte Offsite-Backups, und … und … und!
Viel Spaß beim tüfteln und tunneln!