Wireguard auf einfach

Wireguard, dieses VPN kann einen ganz schön beschäftigen. Zunächst vermutet man die Funktionalität anders gelagert, ehe man später herausfindet, wie es ist und es korrekt macht. Um diesen Weg zu vereinfachen, habe ich mir gedacht, dass ich mal einige klärende Sätze los werde und meine paar Skripte dazu gebe, mit denen ich das Ganze Management ziemlich einfach gestaltet habe.

Wireguard das Wesen

Wireguard ist toll simpel und doch in der Konfiguration sehr komplex. Zumindest, wenn man unbedarft ran geht, versteht man erst mal nicht was eine gute Konfiguration ist und welche Randparameter einzuhalten sind.

Erkenntnis

Die Erkenntnisse dazu sind:

  • Wireguard ist eine Punkt-zu-Punkt VPN-Verbindung. Das heißt, es werden keine Netze verbunden sondern immer nur zwei Interfaces miteinander.
  • Daraus leitet sich die Folge ab, dass der Verkehr also über das einzurichtende Zwischennetz geroutet wird.
  • Ein Paket wandert also von einem Netz durch den Forwarder in das VPN-Netz und auf der anderen Seite in das ferne Netz, wieder durch den Forwarder des VPN-Routers.
  • Wireguard kann nur immer einen Gegner pro Interface und Port haben
  • Man muss also viele kleine Netze anlegen und jeweils eigene Ports nutzen
  • Es gibt Konfigs für Clients und für Server
  • Die Schlüssel darin sind anitsymmetrisch darin verteilt (perfekt zur Automatisierung)
  • Die IP-Adressen sind ebenfalls antisymmetrisch
  • Andere Dinge sind gleich oder nur hier oder dort anzuwenden

Anwendungsfall

Ich beschränke mich hier auf den Anwendungsfall Netzkopplung mit einem zentralen Router. Sprich es gibt mehrere Clients und diese bringen entweder sich selbst oder zusätzlich ein ganzes Netz herein. Ab da funktioniert das Routing auch zwischen den Netzen (Voraussetzung sind allerdings gepflegte statische Routen auf dem Standardgateway der gekoppelten Netze = war immer so).

Skripte

Dieses wurde berücksichtigt, um die folgenden Skripte zu erstellen. Als einzigstes Ding muss man sich einen Namen ausdenken für den neuen Client. Und natürlich muss die dabei entstandene Client-Konfig auf den Client verbracht werden.

Die Skripte sind dazu gedacht, im /etc/wireguard-Verzeichnis zu residieren und dort lokal Änderungen zu machen und als root systemctl aufzurufen.

Als Infrastruktur kommt ein _-Verzeihnis mit. Darin sind die Vorlagedateien für die Server- und Client-Konfig drin mit Platzhalter. Das mk_-client.sh-Skript macht dann mit sed einen Such- und Ersetzenlauf. Weiterhin sind in diesem Verzeichnis .txt-Dateien, die die jeweils zuletzt vergebenene IP/Port enthält. Diese Dateien sind bei Bedarf/zu Beginn zu pflegen.

_/last-ip.txt

10.254.0.4

Anpassen bei Bedarf – das 10er Netz scheint gut. Es wird immer 2 hochgezählt, da ja immer Point-to-Point zwei Adressen gebraucht werden.

_/last-port.txt
14264

Hier wird der letzte Port gemerkt und weiter hochgezählt. alles uter 64k ist gut.

_/client.conf
[Interface]
# set address to next address
Address = :CLIENT_IP:/32
PrivateKey = :CLIENT_KEY:
#DNS = 8.8.8.8

[Peer]
PublicKey = :SERVER_PUB_KEY:
Endpoint = :SERVER_ADDRESS:::PORT:
PresharedKey = :PSK:
# Route only vpn trafic through vpn
AllowedIPs = 10.254.0.0/24, 192.168.88.0/24, 192.168.22.0/24
# Route ALL traffic through vpn
#AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 21ds

Hier sind Platzhalter mit :PLH:-Notation drin, die beim Erzeugen ersetzt werden. Bei AllowedIPs kann der geneigte Admin all seine Netze hinzufügen. Da dieser Teil kopiert wird, müssen alle erstellten client.confs angepasst werden, wenn neue Netze hinzukommen. In diesem Fall sind es /24-Netze.

_/server.conf
[Interface]
Address = :SERVER_IP:/32
MTU = 1420
ListenPort = :PORT:
PrivateKey = :SERVER_KEY:
PostUp = /etc/wireguard/wg-iptables-updown.sh :IF_NAME: up
PostDown = /etc/wireguard/wg-iptables-updown.sh :IF_NAME: down

[Peer]
PublicKey = :CLIENT_PUB_KEY:
PresharedKey = :PSK:
AllowedIPs = :CLIENT_IP:/32

Dies ist die Vorlage für neue Server-Konfigs. Interessant dabei, dass die eigene und Gegen-IP des VPN-Netzes /32-Adressen sind. Also genau je eine Adresse. Zudem ist hier der wg-iptables-updown.sh – Aufruf drin, der das Routing auf dem zentralen Router aktualisiert und entsprechende Forwarding-Regeln einfügt oder entfernt. Diese Datei ist auch mit dabei. Siehe hier:

wg-iptables-updown.sh
#!/bin/sh

iptables="/usr/sbin/iptables"

if [ -z "$1" ]; then
	echo "No interface!"
	echo "Usage: $0 [interface] [action]"
	exit 0

fi

if [ -z "$2" ]; then
	echo "No action!"
	echo "Usage: $0 [interface] [action]"
	echo "Actions:"
	echo "* up"
	echo "* down"
	exit 0

elif [ "$2" = "up" ]; then
	action="-A"

elif [ "$2" = "down" ]; then
	action="-D"

else
	echo "Unknown action!"
	echo "Usage: $0 [interface] [action]"
	echo "Actions:"
	echo "* up"
	echo "* down"
	exit 0

fi

$iptables $action FORWARD -i $1 -j ACCEPT
$iptables $action FORWARD -o $1 -j ACCEPT

Hauptteil

Den Hauptteil bilden die zwei Skripte mk-client.sh und rm-client.sh

Damit wird ein neuer VPN-Entpunkt hinzugefügt bzw entfernt.

mk-client.sh

Einzig der Name für diese Verbindung wird als Parameter gebraucht. Es wird dafür ein öffentlicher und Privater Schlüssel und ein neues Geheimnis ausgewürfelt und in entsprechenden Dateien im ./clients/-Verzeichnis gespeichert. Von dort kann man die Dateien (eigentlich nur die .cofig) für den Client extrahieren und weitergeben. Die Server-.config wird im /etc/wireguard-Verzeichnis abgelegt und ist somit direkt verfügbar. Das wird auch gleich genutzt und wireguard damit konfiguriert. Sowohl die client- als auch die server-Konfig sind Kopien der Vorlagedateien. Die Platzhalter (wie z.B. Schlüssel und IPs) werden durch sed-Aufrufe ersetzt. So einfach.
Am Ende kommt noch eine Frage, ob man denn die Konfig gleich in systemd und beim Systemstart aktivieren möchte.

#!/bin/bash
VPN_HOST=vpn.flinkebits.de

if [ $# -eq 0 ]
then
	echo "must pass a client name as an arg: mk-client.sh new-client"
else
	umask 077
	echo "Creating client config for: $1"
	mkdir -p clients/$1
	wg genkey | tee clients/$1/$1.priv | wg pubkey > clients/$1/$1.pub
	CLIENT_KEY=$(cat clients/$1/$1.priv)
	CLIENT_PUB_KEY=$(cat clients/$1/$1.pub)
        infix=$(cat _/last-ip.txt | tr "." " " | awk '{print $4}')
	ips="10.254.0."$(expr $infix + 1)
	ipc="10.254.0."$(expr $infix + 2)
        lastport=$(cat _/last-port.txt)
        port=$(expr $lastport + 1)
	wg genpsk > clients/$1/$1.psk
	PSK=$(cat clients/$1/$1.psk)

	wg genkey | tee clients/$1/server.priv | wg pubkey > clients/$1/server.pub
        SERVER_KEY=$(cat clients/$1/server.priv)
        SERVER_PUB_KEY=$(cat clients/$1/server.pub)


  cat _/server.conf | sed -e 's|:PSK:|'"$PSK"'|' | sed -e 's/:SERVER_IP:/'"$ips"'/' | sed -e 's/:CLIENT_IP:/'"$ipc"'/' | sed -e 's|:SERVER_KEY:|'"$SERVER_KEY"'|' | sed -e 's|:CLIENT_PUB_KEY:|'"$CLIENT_PUB_KEY"'|' | sed -e 's|:PORT:|'"$port"'|' | sed -e 's|:IF_NAME:|'"wg-$1"'|' > wg-$1.conf

  cat _/client.conf | sed -e 's|:PSK:|'"$PSK"'|' | sed -e 's/:CLIENT_IP:/'"$ipc"'/' | sed -e 's|:CLIENT_KEY:|'"$CLIENT_KEY"'|' | sed -e 's|:SERVER_PUB_KEY:|'"$SERVER_PUB_KEY"'|' | sed -e 's|:PORT:|'"$port"'|' | sed -e 's|:SERVER_ADDRESS:|'"$VPN_HOST"'|' > clients/$1/$1.conf

	echo "Erzeuge in clients/$1 $1.priv, $1.pub, server.priv, server.pub"
	echo "Erzeuge clients/$1/$1.conf"
	echo "Erzeuge wg-$1.conf"
	echo "Speichere zuletzt verwendete IP, Port: $ipc : $port"
	echo $ipc > _/last-ip.txt
	echo $port > _/last-port.txt
	echo "Konfig fertig!"

	read -p "Aktivieren von $1 in systemctl? (y/n) " yn

	case $yn in 
		[yY] ) echo ok, we will proceed;
			systemctl enable wg-quick@wg-$1.service
			systemctl start wg-quick@wg-$1
			;;
		* ) echo exiting...;
		exit;;
	esac
fi

rm-client.sh

Die rm-client macht es recht einfach. Fährt das interface ordentlich runter, entfernt es aus systemd und löscht die Dateien:

#!/bin/bash

if [ $# -eq 0 ]
then
        echo "must pass a client name as an arg: $0 aclient"
else
	wg-quick down wg-$1
	systemctl stop wg-quick@wg-$1
	systemctl disable wg-quick@wg-$1.service
	rm -rfv "/etc/wireguard/clients/$1/"
	rm -v "/etc/wireguard/wg-$1.conf"
fi

GIT-Repo

Das Ganze könnt ihr auch in einem Git-Repo auf einmal herunterladen und in euer /etc/wireguard-Vz werfen. https://github.com/ChaosChemnitz/Wireguard-einfach