Das Problem
Der Aufbau: pfSense als Gateway, WireGuard-Tunnel zu einem VPN-Provider als primärer Internet-Gateway, FritzBox als Fallback. Das ganze verwaltet über eine Gateway-Group – Tier 1 WireGuard, Tier 2 WAN.
In der Theorie: Wenn WireGuard ausfällt, wechselt die Gateway-Group auf WAN. Kein Ausfall, kein Problem.
In der Praxis: WireGuard reconnected, alle Clients sind offline. Minutenlang. Bis man WireGuard manuell neustartet.
pfSense-Web-UI zeigt dabei fröhlich:
WireguardVPN 10.5.0.2 2.1ms 0.0% online
Online. Kein Paketverlust. Alles prima. Nur dass kein einziges Paket ankommt.
TL;DR
Auf pfSense pingt dpinger bei WireGuard-Gateways ohne explizit gesetztes Monitor-Target die eigene Interface-IP (10.5.0.2). Das Interface antwortet immer auf Pings – unabhängig davon ob der Tunnel zum Peer aktiv ist. Ergebnis: Gateway-Group erkennt den Tunnelausfall nie, failover findet nie statt, Traffic geht ins Nichts.
Fix: Monitor-Target auf eine IP setzen, die nur durch den aktiven Tunnel erreichbar ist (z.B. 10.5.0.1 als VPN-Provider-internes Gateway).
Diagnose
Der Blick auf die laufenden dpinger-Prozesse zeigt das Problem sofort:
ps aux | grep dpinger
/usr/local/bin/dpinger -i WireguardVPN -B 10.5.0.2 [...] 10.5.0.2
Die letzte Adresse ist das Ping-Ziel. Und das ist 10.5.0.2 – also die eigene Interface-IP. dpinger schickt ICMP-Pakete an sich selbst. Natürlich kommen die zurück.
Zum Vergleich der WAN-Gateway-Eintrag:
/usr/local/bin/dpinger -i WAN_PPPOE -B 192.168.1.1 [...] 1.1.1.1
Hier pingt dpinger 1.1.1.1 – eine externe IP, die wirklich nur erreichbar ist, wenn der WAN-Uplink funktioniert. Genau so soll es sein.
Ursache
pfSense setzt das Monitor-Target standardmäßig auf die Gateway-IP. Bei klassischen Gateways (WAN, VLAN) ist das eine IP beim ISP oder Router – also außerhalb. Bei WireGuard-Tunneln ist die Gateway-IP (10.5.0.2) aber die lokale Tunnel-Interface-Adresse. Die gehört dem eigenen System.
Resultat:
- WireGuard-Tunnel bricht ab (Handshake-Timeout, Server-Wechsel beim Provider, NAT-Session abgelaufen)
- dpinger pingt
10.5.0.2→ eigenes Interface antwortet → 0% Loss, 2ms Latenz - Gateway-Group sieht: Tier 1 online → kein Failover
- Default Route bleibt auf
tun_wg0 - Alle Clients senden Pakete in den toten Tunnel
Das ist kein Bug – es ist ein Konfigurationsproblem. Das Monitor-Target ist einfach falsch.
Lösung
Schritt 1: Richtiges Monitor-Target finden
Gesucht: Eine IP, die ausschließlich durch den aktiven WireGuard-Tunnel erreichbar ist.
VPN-Provider bieten intern meist ein Gateway an. Testen:
# Aus der pfSense-Shell (SSH)
ping -c 3 10.5.0.1
Antwortet 10.5.0.1 mit 9-10ms? Dann ist das das interne Gateway des VPN-Providers und ein perfektes Monitor-Target.
Alternativ: Eine beliebige IP, die nur durch den Tunnel routet, z.B. eine IP im VPN-internen Subnetz.
Schritt 2: Monitor-Target in pfSense setzen
Web-UI: System → Routing → Gateways → WireGuard-Gateway bearbeiten → Monitor IP → 10.5.0.1
Oder direkt in config.xml:
# Backup
cp /cf/conf/config.xml /cf/conf/config.xml.bak.$(date +%Y%m%d_%H%M%S)
# Relevanter Abschnitt in config.xml
grep -n "WireguardVPN\|monitor" /cf/conf/config.xml | head -20
Den <monitor>10.5.0.1</monitor> Tag im Gateway-Block setzen.
Schritt 3: dpinger neu starten
# Laufenden dpinger für diesen Gateway stoppen
PID=$(ps aux | grep "dpinger.*WireguardVPN" | grep -v grep | awk '{print $2}')
kill $PID
# pfSense startet dpinger automatisch neu, oder manuell:
/etc/rc.gateway_alarm # nicht notwendig, dpinger startet selbst
Verifikation:
ps aux | grep dpinger | grep WireguardVPN
# Erwartete Ausgabe:
# /usr/local/bin/dpinger -i WireguardVPN -B 10.5.0.2 [...] 10.5.0.1
# ^^^^^^
# Jetzt 10.5.0.1!
# Gateway-Status prüfen
# Delay jetzt ~10ms statt 2ms = pingt durch den Tunnel, nicht sich selbst
Bonus-Fixes
Solange man schon dabei ist:
PersistentKeepalive zu niedrig oder zu hoch
Hinter NAT (z.B. FritzBox) läuft die NAT-Session für UDP ab – je nach Router nach 30-120 Sekunden ohne Traffic. Standard-Empfehlung für WireGuard hinter NAT:
PersistentKeepalive = 25
Wert von 115 ist zu hoch für viele NAT-Implementierungen.
gw_down_kill_states aktivieren
Wenn das Gateway als down erkannt wird, hängen bestehende TCP-States noch am alten Gateway. Mit gw_down_kill_states = down werden die States beim Gateway-Wechsel gekillt – Clients stellen sofort neue Verbindungen her.
Web-UI: System → Routing → Gateways → Gateway bearbeiten → “Flush States when Gateway goes down”
Verifikation
Der Beweis: WireGuard-Tunnel manuell beenden und beobachten.
# Tunnel kurz stoppen (ACHTUNG: kurze Unterbrechung!)
wg-quick down tun_wg0 2>/dev/null || ifconfig tun_wg0 down
# dpinger sollte jetzt Loss melden (nach ~20-30 Sekunden)
# Gateway-Status:
Wenn dpinger jetzt Loss > 20% meldet und der Gateway-Status auf “down” wechselt, funktioniert alles korrekt. Dann Tunnel wieder hochfahren und Gateway-Group wechselt zurück.
Lessons Learned
Für alle WireGuard-Gateways in pfSense gilt: Das Monitor-Target muss explizit gesetzt werden. Die Standardeinstellung (eigene Interface-IP) ist für WireGuard-Tunnel unbrauchbar.
Beim nächsten pfSense-Audit prüfen:
ps aux | grep dpinger– wohin pingt jeder Gateway wirklich?- Ist der Monitor-Target eine externe IP (gut) oder die eigene Interface-IP (schlecht)?
Das gilt übrigens auch für OpenVPN-Gateways in pfSense – gleiche Falle.