Homelab WireGuard VPN
WireGuard ist ein schnelles, modernes VPN-Protokoll. Es ist die richtige Wahl für den Fernzugriff auf ein Heimnetzwerk – einfacher zu konfigurieren als OpenVPN und schneller als die meisten Alternativen.
Alle Konfigurationsbeispiele zeigen gängige Einrichtungen. Überprüfen Sie jeden Befehl – insbesondere die iptables-Weiterleitungsregeln und Dateiberechtigungen für Schlüssel – bevor Sie sie auf Ihrem System anwenden, und führen Sie Änderungen in einem Wartungsfenster durch.
Wann verwenden
- Einrichten eines WireGuard-Servers auf einem Raspberry Pi, Linux-Host, pfSense oder Router
- Generieren von WireGuard-Schlüsselpaaren und Schreiben von Peer-Konfigurationsdateien
- Konfigurieren des Fernzugriffs von einem Telefon oder Laptop auf ein Heimnetzwerk
- Erklären von Split-Tunneling (nur Heimverkehr routen) vs. Full-Tunnel (gesamten Verkehr routen)
- Fehlerbehebung bei WireGuard-Verbindungen, die nicht hochkommen
- Automatisieren der Peer-Konfiguration für mehrere Clients
Wie WireGuard funktioniert
Ihr Telefon (WireGuard-Client)
│
│ Verschlüsselter UDP-Tunnel (Port 51820)
│
Ihr Heimrouter (WireGuard-Server – benötigt eine öffentliche IP oder DDNS)
│
Ihr Heimnetzwerk (192.168.1.0/24, NAS, Pi, usw.)
Jedes Gerät hat ein Schlüsselpaar (öffentlicher + privater Schlüssel).
Der Server kennt den öffentlichen Schlüssel jedes Clients.
Der Client kennt den öffentlichen Schlüssel des Servers + Endpunkt (IP:Port).
Daten werden Ende-zu-Ende verschlüsselt, ohne zentralen Server oder Zertifizierungsstelle.
Server-Einrichtung (Linux)
# WireGuard installieren
sudo apt update && sudo apt install wireguard -y
# Server-Schlüsselpaar generieren – Dateien von Anfang an mit privaten Berechtigungen erstellen
sudo mkdir -p /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key'
# Server-Konfiguration schreiben – den tatsächlichen privaten Schlüsselwert einfügen
# Speichern Sie private Schlüssel nicht in der Versionskontrolle und teilen Sie sie nicht
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.8.0.1/24 # VPN-Subnetz – Server bekommt .1
ListenPort = 51820
PrivateKey = <privaten_Server_Schlüssel_hier_einfügen>
# Bereichsbezogene Weiterleitungsregeln: VPN-Verkehr ein-/auslassen, kein pauschales FORWARD ACCEPT
PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Telefon – durch den tatsächlichen öffentlichen Telefonschlüssel ersetzen
PublicKey = <öffentlicher_Telefon_Schlüssel>
AllowedIPs = 10.8.0.2/32
[Peer]
# Laptop – durch den tatsächlichen öffentlichen Laptop-Schlüssel ersetzen
PublicKey = <öffentlicher_Laptop_Schlüssel>
AllowedIPs = 10.8.0.3/32
EOF
sudo chmod 600 /etc/wireguard/wg0.conf
# eth0 durch den tatsächlichen Namen der ausgehenden Schnittstelle ersetzen
# Überprüfen mit: ip route show default
# IP-Weiterleitung aktivieren (erforderlich für das Routen von Datenverkehr durch den Server)
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
# WireGuard starten und beim Booten aktivieren
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
Client-Konfiguration
# Für jedes Client-Gerät ein eindeutiges Schlüsselpaar generieren
# Auf dem Client oder auf dem Server ausführen und den privaten Schlüssel sicher übertragen – niemals im Klartext
umask 077
wg genkey | tee phone_private.key | wg pubkey > phone_public.key
# Client-Konfigurationsdatei (phone_wg0.conf):
[Interface]
PrivateKey = <privater_Telefon_Schlüssel>
Address = 10.8.0.2/32
DNS = 192.168.1.2 # Optional: Pi-hole für DNS über den Tunnel verwenden
[Peer]
PublicKey = <öffentlicher_Server_Schlüssel>
Endpoint = your-home-ip.ddns.net:51820 # Ihre öffentliche IP oder DDNS-Hostname
AllowedIPs = 192.168.1.0/24 # Split-Tunnel: nur Heimnetzwerkverkehr
# AllowedIPs = 0.0.0.0/0, ::/0 # Full-Tunnel: gesamter Verkehr über VPN
PersistentKeepalive = 25 # NAT-Loch offen halten (erforderlich für mobile Clients)
Split-Tunnel vs. Full-Tunnel
# Split-Tunnel: AllowedIPs = 192.168.1.0/24
Nur Datenverkehr, der für Ihr Heimnetzwerk bestimmt ist, geht über das VPN.
Internetverkehr (YouTube, Spotify) geht direkt – bessere Leistung auf Mobilgeräten.
Am besten für: "Ich möchte nur von überall auf mein NAS und meinen Pi zugreifen."
# Full-Tunnel: AllowedIPs = 0.0.0.0/0, ::/0
GESAMTER Datenverkehr geht über Ihre Heim-Internetverbindung.
Nützlich für: Nutzung des heimischen DNS/Pi-hole-Werbeblockers.
Nachteil: Die heimische Upload-Geschwindigkeit wird überall zum Engpass.
# Multi-Subnetz-Split-Tunnel (häufigster Homelab-Anwendungsfall):
AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24
Routet alle Ihre VLANs durch den Tunnel; Internet bleibt direkt.
Schlüsselerzeugung und Peer-Verwaltung
import subprocess
def keypair_generieren() -> tuple[str, str]:
"""Erzeugt ein WireGuard-Schlüsselpaar. Gibt (privater_schlüssel, öffentlicher_schlüssel) zurück."""
privat = subprocess.check_output(["wg", "genkey"]).decode().strip()
öffentlich = subprocess.run(
["wg", "pubkey"], input=privat.encode(), capture_output=True
).stdout.decode().strip()
return privat, öffentlich
def preshared_key_generieren() -> str:
return subprocess.check_output(["wg", "genpsk"]).decode().strip()
def client_konfiguration_erstellen(
client_privater_schlüssel: str,
client_vpn_ip: str, # z.B. "10.8.0.3"
server_öffentlicher_schlüssel: str,
server_endpunkt: str, # z.B. "home.example.com:51820"
erlaubte_ips: str = "192.168.1.0/24",
dns: str = "",
) -> str:
dns_zeile = f"DNS = {dns}\n" if dns else ""
return f"""[Interface]
PrivateKey = {client_privater_schlüssel}
Address = {client_vpn_ip}/32
{dns_zeile}
[Peer]
PublicKey = {server_öffentlicher_schlüssel}
Endpoint = {server_endpunkt}
AllowedIPs = {erlaubte_ips}
PersistentKeepalive = 25
"""
def server_peer_block_erstellen(
client_öffentlicher_schlüssel: str,
client_vpn_ip: str,
kommentar: str = "",
) -> str:
kommentar_zeile = f"# {kommentar}\n" if kommentar else ""
return f"""
{kommentar_zeile}[Peer]
PublicKey = {client_öffentlicher_schlüssel}
AllowedIPs = {client_vpn_ip}/32
"""
Halten Sie private Schlüssel aus der Versionskontrolle fern. Wenn Sie dieses Skript verwenden, schreiben Sie Schlüsselmaterial in Dateien mit Modus 600 und protokollieren oder drucken Sie es niemals aus.
pfSense / OPNsense WireGuard
# pfSense: VPN → WireGuard → Tunnel hinzufügen
Schnittstellenschlüssel: Generieren (erstellt automatisch Schlüsselpaar)
Listen-Port: 51820
Schnittstellenadresse: 10.8.0.1/24
# Peer hinzufügen (einen pro Client):
Öffentlicher Schlüssel: <client öffentlicher schlüssel>
Erlaubte IPs: 10.8.0.2/32
# WireGuard-Schnittstelle zuweisen:
Schnittstellen → Zuweisungen → Hinzufügen (wg0 auswählen)
Schnittstelle aktivieren, keine IP erforderlich (wird in der Tunnelkonfiguration festgelegt)
# Firewall-Regeln:
WAN → eingehenden UDP-Port 51820 erlauben (damit Clients den Server erreichen können)
WireGuard-Schnittstelle → Datenverkehr zu LAN-Netzwerken erlauben, die erreichbar sein sollen
DDNS (Dynamisches DNS) für Heimserver
Die meisten Heim-Internetverbindungen haben eine dynamische IP. Verwenden Sie DDNS, damit Ihr VPN-Endpunkt nach einem IP-Wechsel erreichbar bleibt.
# Option 1: Cloudflare DDNS – speichern Sie Anmeldeinformationen in einer Secrets-Datei, nicht inline
# docker-compose-Eintrag mit einer env-Datei:
ddns-updater:
image: qmcgaw/ddns-updater
env_file: ./ddns.env # zone_id und token hier speichern, nicht im compose
restart: unless-stopped
# ddns.env (chmod 600, nicht in Git eingecheckt):
# SETTINGS_CLOUDFLARE_ZONE_ID=your_zone_id
# SETTINGS_CLOUDFLARE_TOKEN=your_api_token
# Option 2: DuckDNS (kostenlos, einfach)
Registrieren Sie sich bei duckdns.org → erhalten Sie einen Token und eine Subdomain (myhome.duckdns.org)
Speichern Sie den Token in /etc/ddns.env (Modus 600), verwenden Sie dann ein kleines root-eigenes Skript:
# /usr/local/bin/update-duckdns
#!/bin/sh
set -eu
. /etc/ddns.env
curl --fail --silent --show-error --max-time 10 \
--get "https://www.duckdns.org/update" \
--data-urlencode "domains=myhome" \
--data-urlencode "token=${DUCKDNS_TOKEN}" \
--data-urlencode "ip="
# Cron-Job:
*/5 * * * * /usr/local/bin/update-duckdns >/dev/null 2>&1
Fehlerbehebung
# WireGuard-Status und letzten Handshake überprüfen
sudo wg show
# Wenn "latest handshake" nie oder sehr alt ist, ist der Tunnel nicht verbunden.
# Überprüfen Sie:
# 1. Ist UDP-Port 51820 auf dem Router/der Firewall geöffnet?
sudo ufw status # oder pfSense/UniFi-Firewall-Regeln prüfen
# 2. Ist der öffentliche Schlüssel des Servers in der Client-Konfiguration korrekt?
sudo wg show wg0 public-key # Mit dem in der Client-Konfiguration vergleichen
# 3. Ist die IP-Weiterleitung auf dem Server aktiviert?
cat /proc/sys/net/ipv4/ip_forward # Sollte 1 sein
# 4. Deckt AllowedIPs des Clients die IP ab, die Sie erreichen möchten?
# Wenn AllowedIPs = 192.168.1.0/24 und Sie versuchen, 192.168.3.5 zu erreichen, wird es nicht geroutet.
# Kernel-Logs auf WireGuard-Fehler überprüfen
dmesg | grep wireguard
# WireGuard neu starten
sudo wg-quick down wg0 && sudo wg-quick up wg0
Anti-Patterns
# SCHLECHT: Private Schlüssel in der Versionskontrolle speichern oder teilen
# Private Schlüssel sind gleichwertig mit Passwörtern – niemals in Git einchecken
# SCHLECHT: Verwendung von AllowedIPs = 0.0.0.0/0 auf Mobilgeräten ohne Berücksichtigung der Auswirkungen
# Full-Tunnel routet den gesamten mobilen Datenverkehr über Ihren Heim-Upload – normalerweise langsam
# SCHLECHT: Kein PersistentKeepalive auf mobilen Clients setzen
# Mobile Clients hinter NAT lassen Leertunnel ohne dies fallen
# SCHLECHT: Port 51820 in der Firewall öffnen, aber IP-Weiterleitung auf dem Server vergessen
# Tunnel verbindet, aber kein Datenverkehr wird geroutet – verwirrend bei der Fehlersuche
# SCHLECHT: Ein Schlüsselpaar auf mehreren Client-Geräten teilen
# Jedes Gerät muss sein eigenes eindeutiges Schlüsselpaar haben – geteilte Schlüssel brechen das Sicherheitsmodell
# SCHLECHT: Verwendung einer breiten "FORWARD ACCEPT" iptables-Regel
# Bereichsbezogene Weiterleitungsregeln nur für die wg0-Schnittstelle und Richtung verwenden
Best Practices
- Generieren Sie ein eindeutiges Schlüsselpaar pro Client-Gerät – verwenden Sie Schlüssel niemals wieder
- Verwenden Sie Split-Tunneling (
AllowedIPs = <Heimsubnetze>) für Mobilgeräte - Setzen Sie
PersistentKeepalive = 25auf allen mobilen Clients - Verwenden Sie DDNS, wenn Ihr ISP eine dynamische IP zuweist; speichern Sie Anmeldeinformationen in Umgebungsdateien, nicht inline
- Verwenden Sie bereichsbezogene iptables-Weiterleitungsregeln (eingehend nur auf wg0) anstelle eines pauschalen FORWARD ACCEPT
- Fügen Sie die IP des Pi-hole als
DNS =in Client-Konfigurationen hinzu, um Werbeblocker über das VPN zu erhalten - Rotieren Sie das Server-Schlüsselpaar regelmäßig und aktualisieren Sie alle Client-Konfigurationen
Verwandte Skills
- homelab-network-setup
- homelab-vlan-segmentation
- homelab-pihole-dns