Springe zum Hauptinhalt

Fritz!Box WireGuard und systemd-networkd

Vor Jahren schon habe ich einfache WireGuard-Verbindungen zwischen meinem Heimserver und meinem virtuellen Server, damals bei Contabo, eingerichtet. Ich erinnere mich düster, dass ich den WireGuard-Schnittstellen eigene IP-Adressen zuweisen musste, über die dann das Routing eingerichtet wurde.

In diesem Eintrag werde ich Verbindungsschlüssel entfernen und IP-Adressen austauschen bzw. durch Labels ersetzen, die ihre Funktion beschreiben, damit das ggf. auf vergleichbare, aber nicht identische Situationen angewendet werden kann.

Die Situation

Bei mir liegen zwei Spezialfälle vor: Ich habe auf meinem Server ein paar Systemcontainer mit systemd-nspawn am Laufen. Diese hängen dort an einer Network Zone, die von systemd-nspawn gemanagt wird. Für vorhersagbare IP-Adressen läuft auf der Bridge-Schnittstelle der DHCP-Server von systemd-networkd. Der wird einfach mittels einer Drop-in-Datei aktiviert und konfiguriert.

# /etc/systemd/network/80-container-vz.network.d/00-dhcp.conf
[Network]
Address=192.168.1.1/24
Address=fd00:1::1/64
DHCPServer=yes
IPMasquerade=both

[DHCPServer]
ServerAddress=192.168.1.1/24
EmitDNS=yes
DNS=9.9.9.9
EmitRouter=yes
EmitTimezone=yes
Timezone=Europe/Berlin
PoolOffset=99
PoolSize=150

[DHCPServerStaticLease]
# nspawn 1
MACAddress=12:34:56:00:00:01
Address=192.168.1.101

[DHCPServerStaticLease]
# nspawn 2
MACAddress=12:34:56:00:00:02
Address=192.168.1.102

[DHCPServerStaticLease]
# qemu 1
MACAddress=12:34:56:00:01:01
Address=192.168.1.201

Die einzelnen statischen Leases könnten jeweils auch in eigenen Dateien stecken.

Die andere Besonderheit ist, dass die Fritzbox bei mir nicht für das DHCP zuständig ist. Das macht ein dnsmasq auf meinem Heimserver in einem systemd-nspawn-Container.

Wichtig ist, dass am Ende aus dem lokalen Netz hinter der Fritzbox via WireGuard auf die einzelnen Container zugegriffen werden kann.

## Konfiguration auf der Fritzbox Der wichtige Teil findet statt auf der Seite "Internet > Freigaben > VPN (Wireguard)" (Fritz-OS 8.21). Der Assistent wird mit der Schaltfläche "WireGuard®-Verbindung hinzufügen" aufgerufen.

Effektiv musste ich in dem Assistenten immer die rechte Option wählen:

Der Assistent fragt, ob ein einzelnes Gerät oder ein komplettes Netzwerk verbunden werden soll. Der Assistent möchte wissen, ob die Verbindung hier gerade neu erstellt wird oder importiert werden soll. Jetzt will der Assistent wissen, ob dies die erste oder eine zusätzliche Verbindung auf der Gegenstelle ist. Der Assistent will (nochmal?) wissen, ob ein einzelnes Gerät oder ein Router mit einem Netzwerk verbunden werden soll.
  1. Welche Art Verbindung möchten Sie erstellen? → Netzwerke koppeln.

  2. Ist diese WireGuard®-Verbindung auf der Gegenstelle bereits eingerichtet? → Nein.

  3. Soll die Verbindung gleichzeitig zu einer bestehenden WireGuard®-Verbindung der Gegenstelle genutzt werden? → Nein. Ich habe nicht geschaut, was "ja" an der Stelle bewirkt. Ich nehme an, davon hängt ab, ob ich eine vollständige Konfiguration kriege oder nur einen Schnipsel zur Ergänzung.

  4. Was für ein Gerät befindet sich auf der Gegenstelle? → WireGuard®-fähiger Router. Keine Ahnung, warum das abgefragt wird, wenn ich am Anfang "Netzwerke koppeln" ausgewählt habe. Ja natürlich muss da ein WireGuard®-fähiger Router sein.

Der Assistent warnt mich dann:

Das manuelle Hinzufügen der neuen Verbindungseinstellungen wird nur erfahrenen Benutzern empfohlen.

Naja, wir sind ja erfahren, nicht wahr?

Der Assistent fragt nach einem Namen für die Verbindung.

Als nächstes vergeben wir einen Namen, unter dem die Verbindung geführt wird.

Der Assistent fragt nach einer DNS-Domain für die Verbindung.

Der nächste Schritt ist ein wenig unklar. Der Assistent möchte eine Angabe der "DNS-Domains der WireGuard®-Gegenstelle". Nirgends scheint die Angabe eine Auswirkung zu haben. Ich kann nur mutmaßen, dass dieser Wert via DHCP als search domain verteilt wird. Wie eingangs erwähnt, kümmert sich ein anderes System hier um DHCP.

Der Assistent fragt nach der IPv4-Adresse und -Subnetzmaske sowie nach der IPv6-Adresse (hier Subnetz per Default /64).

Im vorletzten Schritt fragt der Assistent nach den "IP-Einstellungen der Verbindung". Hier musste ich ein wenig experimentieren, um herauszufinden, was der Assistent damit meint. Letzten Endes funktionierte es mit der IP-Adresse der systemd-nspawn-Zone, deren Netzmaske (CIDR zu Netzmaske) und der ULA-IPv6-Adresse ebendieser Schnittstelle. Bis zu dieser Konfiguration hatte ich der Zonen-Schnittstelle überhaupt keine IPv6-Adresse vergeben, was zu ein bisschen Recherche-Arbeit geführt hat.

Der Assistent fragt nach verschiedenen Einstellungen, zum Durchleiten des gesamten Netzverkehr, nach NetBIOS-Unterstützung und ob die Verbindung auf einzelne lokale Geräte beschränkt werden soll.

Im letzten Schritt gibt es ein paar Kästchen anzukreuzen oder auch nicht. In diesem Fall wird nichts davon gebraucht.

Die Konfiguration ist bereit zum Herunterladen, damit sie an der Gegenstelle importiert werden könne.

Wenn man zu lange brauchte seit der letzten Bestätigung, dass man berechtigt sei, Änderungen an der Konfiguration vorzunehmen, muss das jetzt nochmal bestätigt werden. Im Anschluss arbeitet die Fritzbox ein wenig und bietet dann den Download der erstellten Konfiguration an, was wir auch annehmen. Nach dem Download muss noch auf "Ok" geklickt werden. Der Assistent weist ein letztes Mal darauf hin, dass die geheimen Verbindungsdaten aus der Datei nicht wieder eingesehen werden können.

Eine letzte Warnung, dass die Konfiguration gesichert werden muss. Man kann nochmal zurückgehen. Bei anderer Auswahl weiter am Anfang hätte es auch einen QR-Code im Schritt davor gegeben, den die Fritzbox nicht hätte kontrollieren können. So macht die Warnung wenig Sinn.
[Interface]
PrivateKey = <privater Schlüssel des Servers>
Address = 192.168.1.1/24,fd12:3456:7891::/64
DNS = <private IPv4 und IPv6 der Fritzbox>
DNS = fritz.box

[Peer]
PublicKey = <öffentlicher Schlüssel der Fritzbox>
PresharedKey = <geteilter Schlüssel der Verbindung>
AllowedIPs = <private IPv4- und IPv6-Netze der Fritzbox>
Endpoint = foobar.myfritz.net:52008
PersistentKeepalive = 25

Eigentlich brauchen wir die Datei gar nicht, aber sie beinhaltet die Werte, die wir für die Konfiguration mit systemd-networkd brauchen.

## Konfiguration von systemd-networkd auf dem Server Die Konfigurationsdateien von systemd-networkd liegen üblicherweise in /etc/systedm/network/.

Die Verbindung wird auf drei Dateien aufgeteilt:

wg0.netdev

Die Datei legt das Netzwerkgerät an. Sie beinhaltet die Verbindung zur Gegenstelle sowie alle für die Verbindung notwendigen Geheimnisse bzw. verweist auf entsprechende Dateien (vgl. man-Page systemd.netdev, Abschnitt Wireguard).

wg0.key

Die Datei beinhaltet den base64-kodierten privaten Schlüssel des Servers für diese Verbindung.

wg0.network

Hier wird ganz normal das Netzwerk auf der Schnittstelle wg0 konfiguriert – IP-Adressen, Routen und was sonst noch so anfällt (vgl. man-Page systemd.network).

Die Namen der Dateien sind beliebig wählbar. Allein bei dem privaten Schlüssel muss drauf geachtet werden, dass die Datei in der .netdev-Datei korrekt referenziert wird.

# wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard
Description=WireGuard tunnel wg0

[WireGuard]
# Vermutlich unnötig, ist einfach auf den gleichen Port gesetzt, den die Fritzbox auch nutzt.
ListenPort=52008
PrivateKeyFile=/etc/systemd/network/wg0.key

[WireGuardPeer]
AllowedIPs=192.168.178.1/24,fd12:3456:7890::/64
PublicKey=<öffentlicher Schlüssel der Fritzbox>
PresharedKey=<geteilter Schlüssel der Verbindung>
Endpoint=foobar.myfritz.net:52008
PersistentKeepalive=25

Die Werte für PublicKey und PresharedKey können einfach aus der heruntergeladenen Konfigurationsdatei kopiert werden. Tatsächlich benutzen WireGuard und systemd-networkd die gleichen Namen für die Werte, so dass sogar die Zeilen komplett kopiert werden können.

systemd-networkd möchte nicht, dass diese Datei weltlesbar ist, weswegen wir die Berechtigungen und Eigentümerschaft anpassen:

chmod 640 /etc/systemd/network/wg0.netdev
chown root:systemd-network /etc/systemd/network/wg0.netdev

Das PrivateKeyFile beinhaltet einfach nur den Wert <privater Schlüssel des Servers> aus der Konfigurationsdatei ohne PrivateKey= oder ähnlichem. Netterweise ist der Wert bereits base64-kodiert, so dass dahingehend nichts weiter getan werden muss. Für die Datei verlangt systemd-networkd aber sogar, dass sie nicht weltlesbar ist:

chmod 640 /etc/systemd/network/wg0.key
chown root:systemd-network /etc/systemd/network/wg0.key

Bei der letzten Datei, der wg0.network, ist systemd-networkd entspannter. Sie kann ruhig von allen gelesen werden.

# wg0.network
[Match]
Name=wg0

[Network]
Address=192.168.1.1/24
Address=fd00:1::1/64
IPMasquerade=both

[Route]
Destination=<IPv4-Netzwerk der Fritzbox>

[Route]
Destination=<IPv6-Netzwerk der Fritzbox>

Aufmerksamen Leser*innen ist es sicher aufgefallen, und mir bereitet es auch Bauchschmerzen, aber es funktioniert tatsächlich: Die Schnittstelle wg0 erhält die gleichen Adressen und Netzmasken wie die Schnittstelle der systemd-nspawn-Zone.

Mit einem finalen networkctl reload wird die Schnittstelle aktiviert und der WireGuard-Tunnel aufgebaut.

Da die systemd-nspawn-Container als Default-Route die Adresse der Zonen-Schnittstelle konfiguriert haben, funktionieren Verbindungen von Seiten der Container sofort. Auch die Geräte hinter der Fritzbox können die Container ohne Probleme erreichen, da die Fritzbox –wie in den meisten Fällen– als Gateway konfiguriert ist.

Dieser Blog-Eintrag wurde ohne Unterstützung generativer KI erstellt.

Kommentare

With an account on the Fediverse or Mastodon, you can respond to this post. Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one. Known non-private replies are displayed below.