WireGuard: minimaler VPN-Tunnel zwischen zwei Hosts
Ein lauffähiges Minimalbeispiel – Schlüsselpaare erzeugen, wg0.conf für beide Seiten, wg-quick up, Verbindung prüfen – plus die drei Klassiker AllowedIPs, UDP-Port und PersistentKeepalive.
Zwei Server sollen sich unterhalten – ein Datenbank-Port, ein Metrics-Endpunkt, ein Backup-Ziel –, aber nicht über das offene Internet. Der klassische Weg wäre ein IPsec- oder OpenVPN-Aufbau mit Zertifikaten, Daemon-Konfiguration und einem Nachmittag Lesezeit. WireGuard macht daraus zwei Dateien mit je fünf Zeilen. Es steckt seit Linux 5.6 im Kernel, spricht ausschließlich UDP und kennt keine Cipher-Suite-Verhandlung: Wer den passenden Schlüssel hat, ist drin, wer nicht, bekommt nicht einmal eine Antwort. Dieser Beitrag baut einen vollständigen Punkt-zu-Punkt-Tunnel zwischen zwei Hosts auf – kein Deep Dive in Cryptokey Routing, sondern das lauffähige Minimum plus die Kernidee dahinter.
Zur Abgrenzung: Der Beitrag Cloudflare Tunnel: Services veröffentlichen ohne offene Ports löst ein anderes Problem. Dort geht es darum, einen Web-Dienst über einen fremden Anbieter öffentlich erreichbar zu machen. Hier bauen wir ein privates Netz zwischen Maschinen, die Ihnen gehören, ohne Dritten im Datenpfad.
Was WireGuard macht – und wann Sie es nehmen
WireGuard erzeugt eine virtuelle Netzwerkschnittstelle, üblicherweise wg0. Was in diese Schnittstelle hineingeht, kommt auf der Gegenseite verschlüsselt wieder heraus. Für Ihre Anwendungen sieht das aus wie ein ganz normales lokales Netz mit eigenen IP-Adressen – nur dass die beiden Enden über das Internet verteilt sind.
Die eine Sache, die Sie verstehen müssen, ist die Zuordnung von öffentlichem Schlüssel zu IP-Bereich. Jeder Peer hat ein Schlüsselpaar. In der Konfiguration steht pro Gegenstelle ein [Peer]-Block mit deren PublicKey und einer Liste AllowedIPs. Diese Liste ist beides zugleich: Filter für eingehende und Wegweiser für ausgehende Pakete. Die man-Page formuliert es als „a comma-separated list of IP (v4 or v6) addresses with CIDR masks from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed". Es gibt keine getrennte Routing-Konfiguration, keine Firewall-Regeln für den Tunnel selbst – nur diese eine Liste.
WireGuard passt überall dort, wo Sie eine dauerhafte, verschlüsselte Verbindung zwischen wenigen bekannten Maschinen brauchen: Datenbank-Replikation zwischen zwei Rechenzentren, ein Monitoring-Host, der interne Exporter abfragt, ein Backup-Server im Büro, der einen VPS erreichen soll. Bei Dutzenden wechselnden Clients wird die Handpflege der Peers mühsam – dann lohnt ein Overlay-Werkzeug obendrauf.
Schlüsselpaare erzeugen
Nach der Installation – unter Ubuntu und Debian sudo apt install wireguard, unter Fedora sudo dnf install wireguard-tools – erzeugen Sie auf jedem Host ein eigenes Schlüsselpaar. Das umask 077 davor ist kein Schmuck: Ohne es liegt der private Schlüssel weltlesbar herum.
umask 077
wg genkey | tee private.key | wg pubkey > public.key
Der private Schlüssel verlässt seinen Host nie. Ausgetauscht wird nur der Inhalt von public.key. Beide Hosts brauchen also am Ende: den eigenen privaten und den öffentlichen Schlüssel der Gegenseite.
Das Minimalbeispiel: wg0.conf auf beiden Seiten
Unser Aufbau: Host A ist ein Server mit fester öffentlicher Adresse (vpn.example.com), Host B steht hinter einem NAT-Router ohne eingehende Erreichbarkeit. Das Tunnelnetz ist 10.10.0.0/24, Host A bekommt darin die .1, Host B die .2.
Host A (öffentlich) Host B (hinter NAT)
+--------------------+ +--------------------+
| wg0 10.10.0.1/24 | | wg0 10.10.0.2/24 |
| ListenPort 51820 | | (Port zufaellig) |
+---------+----------+ +----------+---------+
| |
| <=== UDP 51820, verschluesselt ===> |
| |
[ eth0, feste IP ] [ NAT-Router ]
vpn.example.com keine Freigabe
Auf Host A legen Sie /etc/wireguard/wg0.conf an:
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <PRIVATE_KEY_HOST_A>
[Peer]
PublicKey = <PUBLIC_KEY_HOST_B>
AllowedIPs = 10.10.0.2/32
Auf Host B dieselbe Datei, spiegelbildlich:
[Interface]
Address = 10.10.0.2/24
PrivateKey = <PRIVATE_KEY_HOST_B>
[Peer]
PublicKey = <PUBLIC_KEY_HOST_A>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.10.0.1/32
PersistentKeepalive = 25
Die Platzhalter in spitzen Klammern ersetzen Sie durch die tatsächlichen Base64-Schlüssel. Drei Unterschiede sind wichtig. Host A setzt einen festen ListenPort, damit Host B ihn ansprechen kann; ohne die Angabe wählt WireGuard einen zufälligen Port. Nur Host B kennt einen Endpoint, denn nur er weiß, wo die Gegenseite steht – Host A lernt die Adresse von B automatisch aus dem ersten authentifizierten Paket. Und AllowedIPs steht hier auf einer einzelnen /32-Adresse: Es soll ausschließlich Verkehr für die jeweilige Tunnel-IP durch die Leitung, nicht der gesamte Internetverkehr.
Anschließend die Rechte setzen – die Datei enthält einen privaten Schlüssel:
sudo chmod 600 /etc/wireguard/wg0.conf
Tunnel starten und prüfen
wg-quick liest die Datei, legt die Schnittstelle an, weist die Adresse zu und trägt die Routen ein. Auf beiden Hosts:
sudo wg-quick up wg0
Auf Host A muss der UDP-Port erreichbar sein, bei ufw etwa so:
sudo ufw allow 51820/udp
Jetzt der Test. Von Host B aus die Gegenseite anpingen und den Status ansehen:
ping -c 3 10.10.0.1
sudo wg show
wg show ist Ihr wichtigstes Diagnosewerkzeug. Es zeigt pro Peer den öffentlichen Schlüssel, endpoint, allowed ips, latest handshake und transfer. Entscheidend ist die Zeile latest handshake: Steht dort eine Zeitangabe, funktioniert die Kryptografie und der Tunnel steht. Fehlt sie komplett, ist kein Handshake zustande gekommen – dann liegt das Problem auf dem Weg dorthin (UDP-Port, Firewall, Endpoint) oder an einer falschen Schlüsselzuordnung. Für den Dauerbetrieb aktivieren Sie die mitgelieferte systemd-Unit, die den Tunnel beim Booten hochzieht:
sudo systemctl enable --now wg-quick@wg0
Drei typische Stolperfallen
1. AllowedIPs als reinen Filter missverstehen. Der Name legt eine Zugriffsliste nahe, tatsächlich ist es zusätzlich die Routing-Entscheidung. wg-quick leitet aus allen AllowedIPs die Systemrouten ab: „It infers all routes from the list of peers' allowed IPs, and automatically adds them to the system routing table." Wer hier reflexhaft 0.0.0.0/0 einträgt, weil das „alles erlauben" bedeutet, routet damit den kompletten ausgehenden Verkehr in den Tunnel – und sperrt sich bei einem Server ohne Gegenstelle die SSH-Sitzung aus. Für einen Punkt-zu-Punkt-Tunnel gehören dort nur die /32-Adressen der Gegenseite hinein. Umgekehrt gilt: Tragen Sie dieselbe IP nicht bei mehreren Peers ein – beim Senden wirkt die Liste wie eine Routing-Tabelle, eine Ziel-IP gehört also immer genau einem Peer.
2. Der UDP-Port ist zu. WireGuard spricht ausschließlich UDP. Freigaben, die nur tcp erlauben, greifen nicht, und in Cloud-Umgebungen kommt zur Host-Firewall noch die Security Group des Anbieters. Erschwerend: WireGuard antwortet auf unauthentifizierte Pakete grundsätzlich nicht – es gibt kein „connection refused", keinen Timeout mit Fehlertext, keine Log-Zeile. Ein blockierter Port sieht exakt aus wie ein falscher Schlüssel. Deshalb ist wg show und dort das fehlende latest handshake der einzig verlässliche Indikator.
3. PersistentKeepalive hinter NAT vergessen. Der Wert ist laut man-Page standardmäßig ausgeschaltet. Host B baut die Verbindung von innen auf; der NAT-Router merkt sich diese Zuordnung aber nur für einige Minuten Untätigkeit. Läuft sie ab, kann Host A den Host B nicht mehr von sich aus erreichen – der Tunnel wirkt „manchmal tot", bis B wieder etwas sendet. Ein Intervall von 25 Sekunden auf der NAT-Seite hält das Mapping offen; genau diesen Wert nennt die Dokumentation als Beispiel. Auf dem öffentlich erreichbaren Host A ist die Angabe unnötig.
Und ein Sicherheitshinweis, der zu oft fehlt: Private Schlüssel gehören nicht in ein Git-Repository, nicht im Klartext ins Konfigurationsmanagement und nicht in eine Chat-Nachricht. Die wg0.conf enthält einen davon – also chmod 600 auf die Datei und umask 077 beim Erzeugen, sonst ist der ganze Aufwand hinfällig.
Wie es weitergeht
Damit steht ein verschlüsselter Punkt-zu-Punkt-Tunnel, der einen Neustart übersteht. Der naheliegende nächste Schritt ist, nicht nur die beiden Tunnel-IPs, sondern ein ganzes Subnetz hinter einem Peer erreichbar zu machen. Dafür erweitern Sie dessen AllowedIPs um das Netz und aktivieren auf dem weiterleitenden Host net.ipv4.ip_forward, das im Kernel standardmäßig auf 0 steht. Ebenfalls sinnvoll: ein PresharedKey als zusätzliche symmetrische Schicht für Post-Quanten-Resistenz und weitere [Peer]-Blöcke für einen dritten und vierten Host. Tiefer geht es im WireGuard-Quickstart, in wg(8) und in wg-quick(8).
Hinweis zur Veröffentlichung: Dieser Beitrag war für den 30. Juli 2026 geplant. Wegen einer technischen Störung der automatischen Veröffentlichung ist er erst am 11. August 2026 online gegangen. Alle Angaben wurden vor der Veröffentlichung erneut auf Aktualität geprüft.
Hinweis: Die Beiträge dieses Blogs werden unter Einsatz von KI erstellt und vor der Veröffentlichung redaktionell geprüft. Die redaktionelle Verantwortung trägt Emre Yurtbay (siehe Impressum).