Wer als Unternehmen mehrere Dutzend eigenständige Server und Anwendungen betreibt, sammelt zwangsläufig auch mehrere Dutzend interne Verwaltungsoberflächen an: ein Grafana-Dashboard für die Systemüberwachung, einen Uptime-Kuma-Status für die Erreichbarkeit einzelner Dienste, dazu Datenbank-Admin-Oberflächen und interne Programmierschnittstellen (APIs), die nie für die Öffentlichkeit gedacht waren. Jede dieser Oberflächen offen ins Internet zu stellen und nur mit einem Passwort zu schützen, reicht als Absicherung nicht aus. Ein einziges erratenes oder abgegriffenes Passwort öffnet sonst den vollen Zugriff. Die naheliegende Alternative, für jedes Projekt ein eigenes virtuelles privates Netzwerk (VPN) einzurichten, verteilt das Verwaltungsproblem nur, statt es zu lösen, denn jedes Teammitglied müsste dann mehrere separate VPN-Verbindungen parallel pflegen.
WireGuard löst dieses Problem mit einem einzigen gemeinsamen VPN, das mehrere interne Netzsegmente gleichzeitig erreichbar macht, ohne für jede Anwendung eine eigene Zugangslösung zu brauchen. Jedes Teammitglied bekommt genau eine WireGuard-Konfiguration auf seinem Gerät, die exakt die internen Netze freischaltet, für die diese Person berechtigt ist. Seit Kernel-Version 5.6 ist WireGuard fest im Linux-Kernel enthalten und läuft dadurch ohne zusätzliche Software auf jedem aktuellen Linux-Server; für Windows, macOS, iOS und Android stehen offizielle Anwendungen bereit, die den Tunnel im Anwendungsbereich (Userspace) statt im Kernel aufbauen.
Systemvoraussetzungen
Der Aufbau eines gemeinsamen WireGuard-Hubs für viele interne Dienste braucht wenige, aber aktuelle Bausteine. Die folgende Tabelle nennt sie und wofür sie im Ablauf stehen.
| Komponente | Version oder Anforderung | Zweck |
|---|---|---|
| WireGuard-Kernelmodul | Fest enthalten ab Linux-Kernel 5.6, für ältere Kernel als DKMS-Modul nachrüstbar | Verschlüsselter Tunnel zwischen Hub-Server und allen Peers |
| wireguard-tools (CLI) | Stellt die Befehle wg und wg-quick bereit | Schlüsselerzeugung, Konfiguration, Start und Stopp der Verbindung |
| wg-easy (Web-UI) | Aktuell Hauptversion 15, Docker-Image ghcr.io/wg-easy/wg-easy:15 | Peer-Verwaltung über eine Weboberfläche statt über Konfigurationsdateien von Hand |
| Docker Engine | Ab Version 20.10, mit Docker Compose | Betrieb von wg-easy als Container |
| Offener UDP-Port | Standardmäßig UDP 51820, über ListenPort frei wählbar | Eingehende WireGuard-Handshakes an der Firewall des Hub-Servers |
| Client-Betriebssystem | Linux, Windows, macOS, iOS oder Android mit offizieller WireGuard-App | Aufbau der Tunnelverbindung auf dem Endgerät des Teammitglieds |
Systemarchitektur
Ein Peer bezeichnet in WireGuard jeden Teilnehmer des Netzwerks, ob Server oder einzelnes Notebook. Jeder Peer erhält ein eigenes Schlüsselpaar aus einem öffentlichen und einem privaten Schlüssel, anstatt sich wie bei einer klassischen Anmeldung mit Benutzername und Passwort auszuweisen. Der Hub-Server ist in diesem Aufbau der zentrale Peer, den jedes Teammitglied ansteuert, und kennt seinerseits die öffentlichen Schlüssel aller berechtigten Geräte. Beim Verbindungsaufbau, dem Handshake, weisen sich Server und Client gegenseitig anhand ihrer Schlüssel aus, ohne dass ein Passwort über die Leitung geht.
Die Konfiguration jedes Peers legt über den Eintrag AllowedIPs fest, welche IP-Bereiche über diesen Peer erreichbar sind. Auf dem Server steuert AllowedIPs, aus welchen Absenderadressen ein Peer Pakete schicken darf und wohin der Server dessen Antworten weiterleitet; auf dem Client bestimmt derselbe Eintrag, welcher Datenverkehr überhaupt durch den Tunnel läuft. Genau darüber lässt sich der Zugriff eines einzelnen Teammitglieds gezielt auf ein einziges internes Subnetz begrenzen, während ein anderes Mitglied mit einer breiteren AllowedIPs-Liste mehrere Netzsegmente gleichzeitig erreicht.
flowchart TD
Z[Verwaltung ueber wg-easy Weboberflaeche] --> C[WireGuard-Hub-Server]
A[Notebook eines Teammitglieds] --> B[Verschluesselter Tunnel ueber UDP Port 51820]
B --> C
C --> D[Server prueft Public Key und AllowedIPs des Peers]
D --> E[Monitoring-Subnetz mit Grafana und Uptime Kuma]
D --> F[Admin-Subnetz mit internen Verwaltungsoberflaechen]
D --> G[API-Subnetz mit internen Diensten]
Der Ablauf läuft für jede Anfrage gleich. Das Notebook eines Teammitglieds baut über den WireGuard-Client eine verschlüsselte Verbindung zum Hub-Server auf, standardmäßig über UDP-Port 51820. Der Server prüft den öffentlichen Schlüssel des anfragenden Geräts gegen seine eigene Peer-Liste und ordnet ihm anhand der hinterlegten AllowedIPs genau die Subnetze zu, für die er berechtigt ist. Erst danach leitet der Server den Datenverkehr an das jeweilige interne Segment weiter, etwa an das Monitoring-Subnetz mit Grafana und Uptime Kuma oder an das Subnetz mit den internen Verwaltungsoberflächen. Die Zuordnung von Schlüssel zu Subnetz übernimmt bei größeren Teams am einfachsten eine Weboberfläche wie wg-easy, weil sich sonst jede neue Peer-Konfiguration von Hand in eine wachsende Textdatei eintragen müsste.
Schritt-für-Schritt-Umsetzung
Hub-Server einrichten und Peers mit begrenztem Zugriff anlegen
Der Server bekommt eine eigene Konfigurationsdatei unter /etc/wireguard/wg0.conf. Ein [Interface]-Block trägt die eigenen Angaben des Servers, für jedes berechtigte Gerät folgt ein eigener [Peer]-Block. Jeder Peer-Block bekommt in AllowedIPs nur die Subnetze, auf die diese Person tatsächlich zugreifen darf: Das Monitoring-Team im Beispiel unten erreicht ausschließlich das Netz mit Grafana und Uptime Kuma, während das Operations-Team zusätzlich das Netz mit den administrativen Oberflächen und den internen APIs sieht.
[Interface]
PrivateKey = <privater-Schluessel-des-Servers>
Address = 10.10.0.1/24
ListenPort = 51820
PostUp = sysctl -w net.ipv4.ip_forward=1; 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: Monitoring-Team, nur Zugriff auf das Monitoring-Subnetz
[Peer]
PublicKey = <oeffentlicher-Schluessel-Monitoring-Team>
AllowedIPs = 10.10.0.2/32, 10.10.20.0/24
# Peer: Operations-Team, zusaetzlich Admin- und API-Subnetz
[Peer]
PublicKey = <oeffentlicher-Schluessel-Operations-Team>
AllowedIPs = 10.10.0.3/32, 10.10.10.0/24, 10.10.20.0/24, 10.10.30.0/24
PostUp und PostDown sind Skript-Zeilen, die WireGuard beim Start beziehungsweise beim Beenden der Verbindung ausführt. Im Beispiel schalten sie die Weiterleitung von Netzwerkpaketen auf dem Server ein und richten die Firewall- und NAT-Regeln (Network Address Translation) ein, die den Tunnelverkehr in die internen Subnetze durchlassen. Ohne aktivierte Paketweiterleitung nimmt der Server zwar Verbindungen an, leitet aber keine Antworten aus den internen Netzen zurück.
Client-Konfiguration je Teammitglied erstellen
Jedes Endgerät bekommt eine eigene, kleine Konfigurationsdatei, die nur den eigenen privaten Schlüssel und die Adresse des Hub-Servers als Gegenstelle (Endpoint) enthält. Der Eintrag PersistentKeepalive hält die Verbindung offen, wenn das Gerät hinter einem NAT-Router sitzt, also einer gemeinsam genutzten öffentlichen Adresse für mehrere Geräte im selben lokalen Netz. Ohne regelmäßige Keepalive-Pakete verwirft ein solcher Router die Portzuordnung nach einigen Minuten Inaktivität, und der Server kann das Gerät danach nicht mehr von sich aus erreichen.
[Interface]
PrivateKey = <privater-Schluessel-Monitoring-Team>
Address = 10.10.0.2/32
DNS = 10.10.0.1
[Peer]
PublicKey = <oeffentlicher-Schluessel-des-Servers>
AllowedIPs = 10.10.0.1/32, 10.10.20.0/24
Endpoint = vpn.beispiel-firma.de:51820
PersistentKeepalive = 25
Die AllowedIPs-Zeile auf dem Client muss zur Zeile auf dem Server passen: Nur was hier eingetragen ist, schickt der Client überhaupt durch den Tunnel. Fehlt ein Subnetz auf dieser Seite, versucht das Gerät, den betreffenden Dienst am Tunnel vorbei über die normale Internetverbindung zu erreichen, und die Anfrage läuft ins Leere.
Peer-Verwaltung mit wg-easy vereinfachen
Ab einer Handvoll Teammitglieder wird das manuelle Pflegen von Schlüsseln und Konfigurationsdateien schnell fehleranfällig, weil jede neue Person zwei neue Schlüssel, einen neuen Peer-Block auf dem Server und eine neue Client-Datei braucht. wg-easy ist ein quelloffenes Projekt, das WireGuard und eine Weboberfläche zur Peer-Verwaltung in einem Docker-Container bündelt. Es legt neue Clients per Klick an, zeigt die fertige Konfiguration direkt als QR-Code für die offizielle WireGuard-App auf dem Smartphone und listet, welche Geräte gerade verbunden sind.
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
environment:
- WG_HOST=vpn.beispiel-firma.de
- PASSWORD_HASH=$$2a$$12$$Beispielhash
- PORT=51821
- WG_PORT=51820
volumes:
- ./config:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
Der bcrypt-Hash für das Passwort der Weboberfläche entsteht über den Befehl docker run --rm -it ghcr.io/wg-easy/wg-easy wgpw 'Passwort' und wird danach in PASSWORD_HASH eingetragen. Jedes Dollarzeichen im Hash muss dabei verdoppelt werden, weil Docker Compose ein einzelnes Dollarzeichen sonst als eigene Variable liest und den Hash unbrauchbar macht.
Häufige Fehlerquellen
- UDP-Port 51820 wird von einer Firewall blockiert. WireGuard meldet einen blockierten Port nicht als Fehler, der Handshake bleibt schlicht aus. Der Befehl
wg showzeigt auf dem Server, ob für den betroffenen Peer überhaupt ein Handshake-Zeitpunkt vermerkt ist; fehlt er, ist die Portfreigabe am Perimeter der erste Verdächtige. - AllowedIPs auf dem Client deckt das Zielsubnetz nicht ab. Der Tunnel steht, aber ein bestimmter interner Dienst bleibt trotzdem unerreichbar, weil der Client den entsprechenden Datenverkehr gar nicht erst durch den Tunnel schickt. Das Subnetz muss auf beiden Seiten, Client und Server, in
AllowedIPseingetragen sein. - Überlappende AllowedIPs zwischen zwei Peers am selben Interface. WireGuard lässt für ein Subnetz nur eine eindeutige Zuordnung zu; überschneiden sich zwei Peer-Einträge, geht der Verkehr nur an den zuletzt konfigurierten Peer. Subnetze zwischen Peers deshalb eindeutig und ohne Überlappung halten.
- IP-Forwarding auf dem Hub-Server ist deaktiviert. Der Handshake gelingt, aber kein Paket kommt aus den internen Netzen zurück, weil der Server eingehenden Tunnelverkehr nicht an andere Netzwerkschnittstellen weiterreicht.
net.ipv4.ip_forward=1persysctloder alsPostUp-Zeile behebt das. - PersistentKeepalive fehlt bei Geräten hinter NAT. Die Verbindung funktioniert zunächst, bricht aber nach einigen Minuten Leerlauf ab, weil der NAT-Router die Portzuordnung verwirft. Ein Wert von 25 Sekunden im Client-Peer-Block hält die Zuordnung aktiv.
- Dollarzeichen im bcrypt-Hash von wg-easy nicht verdoppelt. Die Weboberfläche startet, aber der Login schlägt fehl, weil Docker Compose das einzelne Dollarzeichen als Variablenreferenz interpretiert und einen Teil des Hashes verschluckt. Jedes
$im Hash wird zu$$.
Häufig gestellte Fragen
Wie unterscheidet sich WireGuard technisch von OpenVPN und IPsec?
WireGuard unterscheidet sich von OpenVPN und IPsec vor allem durch einen wesentlich kleineren Code-Umfang und ein festes, nicht verhandelbares Verschlüsselungsverfahren. Nach Angaben des WireGuard-Projekts selbst ist die Implementierung bewusst auf sehr wenige Zeilen Code ausgelegt, damit einzelne Personen den sicherheitsrelevanten Code vollständig prüfen können, während die Codebasis von IPsec-Implementierungen oder von OpenVPN mit OpenSSL für ein Prüfteam kaum vollständig zu überblicken ist. Anstatt wie IPsec oder OpenVPN aus mehreren austauschbaren Verfahren eines auszuhandeln, nutzt WireGuard für jede Verbindung dieselbe feste Kombination aus dem Noise-Protokoll-Framework, Curve25519, ChaCha20, Poly1305 und BLAKE2. Seit Kernel-Version 5.6 ist WireGuard außerdem fest im Linux-Kernel enthalten und läuft dadurch ohne zusätzliches Kernelmodul auf jedem aktuellen Linux-System.
Welchen Netzwerk-Port braucht WireGuard, und was ist zu tun, wenn die Verbindung nicht zustande kommt?
WireGuard verwendet standardmäßig den UDP-Port 51820, der sich in der Serverkonfiguration über ListenPort auf einen beliebigen anderen Port ändern lässt. Kommt keine Verbindung zustande, liegt die Ursache meist an einer Firewall oder einem Router, der eingehende UDP-Pakete auf diesem Port blockiert, denn WireGuard zeigt einen blockierten Port nicht als Fehlermeldung, sondern der Handshake bleibt einfach aus. Der Befehl wg show auf dem Server zeigt, ob für den betroffenen Peer überhaupt ein Handshake-Zeitpunkt vermerkt ist; fehlt er vollständig, ist die Portfreigabe am Perimeter der erste Ansatzpunkt für die Fehlersuche.
Was passiert, wenn der private Schlüssel eines Teammitglieds verloren geht oder kompromittiert wird?
Ein kompromittierter privater Schlüssel lässt sich bei WireGuard nicht wie ein Passwort einfach zurücksetzen, weil das Protokoll keinen Mechanismus zum Widerrufen eines einzelnen Schlüssels kennt. Für das betroffene Gerät muss stattdessen ein komplett neues Schlüsselpaar erzeugt, der zugehörige Peer-Eintrag auf dem Hub-Server durch den neuen öffentlichen Schlüssel ersetzt und die neue Konfiguration an das Gerät verteilt werden, während der alte Peer-Eintrag entfernt wird. WireGuard rotiert die kurzlebigen Sitzungsschlüssel zwar ohnehin automatisch alle paar Minuten, um eine nachträgliche Entschlüsselung bereits übertragener Daten zu erschweren, das betrifft aber nur die einzelne Sitzung und nicht das langfristige, für die Anmeldung genutzte Schlüsselpaar selbst.
Kann ein Teammitglied nur auf ein einzelnes internes Subnetz zugreifen statt auf das gesamte interne Netzwerk?
Ein Teammitglied lässt sich bei WireGuard gezielt auf ein einzelnes Subnetz beschränken, weil der Eintrag AllowedIPs im jeweiligen Peer-Block auf dem Hub-Server genau festlegt, für welche Adressbereiche dieser eine Peer zuständig ist. Trägt der Server für den Peer des Monitoring-Teams beispielsweise nur das Subnetz mit Grafana und Uptime Kuma in AllowedIPs ein, leitet er ausschließlich Datenverkehr zu und von diesem einen Netz an dieses Gerät weiter, unabhängig davon, welche weiteren internen Netze über denselben Server erreichbar wären. Andere Teammitglieder mit einer umfangreicheren AllowedIPs-Liste im eigenen Peer-Block erreichen dagegen mehrere Netzsegmente gleichzeitig, ohne dass sich die beiden Konfigurationen gegenseitig beeinflussen.