Drei Nächte in Folge meldet Zabbix no recent backup für einen VM-Host, der nachts über einen WireGuard-Hub sein vzdump nach Hause schickt. Du öffnest wg show auf dem Hub, erwartest einen toten Tunnel — und siehst einen Handshake, der vor Sekunden war. Alles grün. Nur: Es kommt nichts an.
TL;DR:
- Ein frischer WireGuard-Handshake sagt nichts über IP-Konnektivität — nur, dass die Crypto-Schicht funktioniert
ifconfig <interface>ohneinet-Zeile ist der eigentliche Befund — nicht der Handshake, nicht die Logs- Nach dem Fix nicht einfach alle versäumten Backups auf einmal nachfahren, wenn der Hub physisch auf derselben Maschine hängt wie der Uplink — das sättigt die Leitung tagsüber
Das Problem
Setup: ein Proxmox-Host (pve-dagobah) schickt sein nächtliches vzdump über einen WireGuard-Site-to-Site-Tunnel an einen Hub (hub-obiwan), der auf einer Hetzner-Maschine als VM läuft. Seit drei Nächten läuft das Backup-Freshness-Monitoring auf Rot.
Erster Blick auf den Hub:
wg show
interface: tun_wg0
public key: (hidden)
private key: (hidden)
listening port: 51820
peer: (hidden)
endpoint: <peer-ip>:51820
allowed ips: 10.77.77.0/30, 192.168.66.0/24
latest handshake: 42 seconds ago
transfer: 0 B received, 0 B sent
Handshake vor 42 Sekunden. Sieht gesund aus. Aber: null Transfer in drei Nächten ist kein vzdump-Transfer — das sind GiB-große Jobs. Und verwirrend dazu: Ein bereits bestehendes LAN-zu-LAN-Forwarding über denselben Tunnel läuft in dem Moment problemlos weiter. Wäre der Tunnel wirklich tot, dürfte da auch nichts durchkommen. Ist er aber nicht — jedenfalls nicht komplett.
Die Diagnose
Ping-Test vom Hub auf die Gegenseite:
ping -c 5 192.168.66.10
PING 192.168.66.10 (192.168.66.10): 56 data bytes
--- 192.168.66.10 ping statistics ---
5 packets transmitted, 0 packets received, 100.0% packet loss
100 % Loss — in beide Richtungen, auch für selbst generierte Pakete. Gleichzeitig zeigt das pfSense-Gateway-Monitoring (dpinger) seinen eigenen Zielhost ebenfalls als nicht erreichbar, obwohl der Handshake frisch bleibt.
Der Befehl, der die Sache aufklärt, ist nicht wg show — es ist der banalste Netzwerk-Check überhaupt:
ifconfig tun_wg0
Bad (so sah es aus):
tun_wg0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1420
groups: tun wg
Keine inet-Zeile. Keine IP auf dem Interface. Punkt.
Good (nach dem Fix):
tun_wg0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1420
inet 10.77.77.1 --> 10.77.77.2 netmask 0xfffffffc
groups: tun wg
(Linux-Äquivalent für den gleichen Check auf einem Linux-Hub: ip -4 addr show tun_wg0 — selbe Frage, andere Syntax.)
Eine Ablenkung auf dem Weg dahin: In den Logs lief parallel ein pvestatd-Fingerprint-Fehler zu einem angebundenen Backup-Storage auf. Sah nach Zusammenhang aus, war keiner — reine Koinzidenz, kostete trotzdem Zeit, bis klar war, dass beide Fehler nichts miteinander zu tun haben.
Exkurs: Auf die Konsole kommen, wenn das SSH-Passwort nirgends steht
Bevor überhaupt ifconfig auf dem Hub laufen konnte, musste erstmal draufgekommen werden — das pfSense-SSH-Passwort für hub-obiwan war nirgends dokumentiert (Credential-Store negativ geprüft). Zugang geht trotzdem, weil der Hub als VM auf pve-dagobah läuft:
# Auf pve-dagobah: VGA-Framebuffer der VM als PPM abziehen
printf "screendump /tmp/gw.ppm\n" | qm monitor <vmid>
Das PPM dann nach PNG konvertieren und von einem vision-fähigen Modell transkribieren lassen, Befehle Zeichen für Zeichen per sendkey eintippen. Drei Fallen dabei:
- Keine Großbuchstaben als Key-Namen:
sendkey Ewird still verworfen.grep -Ealso alsegreptippen, oder den Großbuchstaben explizit alssendkey shift-esenden. - Sonderzeichen brauchen eigene Namen:
|=sendkey shift-backslash,_=sendkey shift-minus, Enter =sendkey ret, Space =sendkey spc. - Vor dem Tippen zweimal screendumpen und per md5 vergleichen — sonst tippst du blind in eine Session, in der gerade jemand anderes sitzt.
Kein serielles Device vorhanden (qm terminal 108 scheitert mit „unable to find a serial interface"), kein QEMU-Guest-Agent installiert — Screendump + sendkey war der einzige Weg rein.
Die Ursache
Das Perfide: Ein WireGuard-Handshake läuft komplett auf der Crypto-Schicht (UDP-Pakete, Diffie-Hellman, Key-Rotation) — völlig unabhängig davon, ob das Tunnel-Interface überhaupt eine IP-Adresse trägt. wg show beantwortet nur die Frage „ist die Verschlüsselung gesund?" — nicht „kann hier IP-Verkehr fließen?". Beides sauber getrennt zu sehen, braucht zwei verschiedene Tools.
Ohne inet-Adresse auf tun_wg0 fehlt die Connected-Route fürs Tunnel-Subnetz (10.77.77.0/30). Die Folge trifft drei Dinge gleichzeitig, aber unterschiedlich sichtbar:
- dpinger-Pings (Gateway-Monitoring) kommen beim Hub an, werden aber nie beantwortet — es gibt keinen Rückweg, weil die Quelladresse für die Antwort fehlt.
- Lokal generierte Pings verlassen das Interface gar nicht erst — ohne eigene Adresse hat der Stack keine Quelle, die er einsetzen könnte.
- Bereits geroutetes LAN-zu-LAN-Forwarding läuft weiter, weil WireGuard Pakete anhand der Peer-
AllowedIPsund bestehender Routing-Table-Einträge weiterleitet — das braucht keine eigene Interface-Adresse, solange die Route für das Ziel-Subnetz schon vorher existierte.
Genau Punkt 3 ist der Teil, der am längsten in die Irre führt: Der Tunnel sieht „halb lebendig" aus, weil ein Teil des Traffics tatsächlich durchkommt.
Die Lösung
Sofort-Fix zur Laufzeit:
ifconfig tun_wg0 inet 10.77.77.1/30 alias
Dauerhaft gehört die Adresse unters Interface, nicht unter die WireGuard-Tunnel-Konfiguration selbst — ein Muster, das auch in pfSense WireGuard Client-VPN: Drei Pitfalls die deinen Tunnel killen schon auftauchte: Interfaces → dein zugewiesenes WireGuard-Interface → IPv4 Configuration Type: Static IPv4 → 10.77.77.1/30. Nicht unter VPN → WireGuard → Tunnels — dort steht nur die Krypto-Config, die pfSense bei zugewiesenen Interfaces schlicht ignoriert.
Beweis, dass es sitzt, direkt in der Config:
grep -n "ipaddr" /cf/conf/config.xml | grep "10.77.77.1"
<ipaddr>10.77.77.1</ipaddr>
Danach lief die nächste Nacht sauber durch.
Die Hairpin-Uplink-Bombe beim Nachholen
Drei Nächte Backup-Rückstand wollen nachgeholt werden — und genau hier liegt die zweite Falle. hub-obiwan läuft als VM physisch auf pve-dagobah. Heißt: Traffic von pve-dagobah zum eigenen Hub verlässt die Maschine über den Hetzner-Uplink nach draußen, trifft den WireGuard-Endpoint, und kommt über denselben Uplink wieder rein. Jedes Byte Backup-Daten kostet den Uplink doppelt — einmal raus, einmal rein — plus zstd-CPU auf dem erzeugenden Host.
Ein Tagsüber-Catch-up-Lauf (unter anderem ein ~340-GiB-Job für cloud-leia) sättigte die Leitung so weit, dass extern erreichbare Dienste hinter dem NAT von hub-obiwan instabil wurden. Mitten am Tag, nicht nachts, wo es niemand gemerkt hätte.
Fix: Nachhol-Läufe ausschließlich ins bestehende Nachtfenster legen (23:50–08:00), nicht tagsüber reinpressen, egal wie sehr die Zabbix-Alarme drängen.
Und wenn du einen laufenden Nachhol-Job stoppen musst:
pkill -f "[v]zdump"
Nicht pkill -f vzdump — das matcht auch die eigene SSH-Kommandozeile (die den String vzdump enthält) und killt sich selbst, statt den Zielprozess zu treffen. Der eckige-Klammer-Trick ([v]zdump statt vzdump) entschärft genau das. Gleicher Grund, warum du nie tail auf eine Datei namens vzdump-*.log im selben pgrep/pkill-Aufruf laufen lässt — der Dateiname matcht sich selbst als Prozess.
Und der eigentliche Haken: Ein hart abgebrochener vzdump-Lauf hinterlässt am Gast gern ein lock: backup. Danach prüfen und aufräumen — sonst endet der nächste Lauf mit job errors, und du hast dir genau den stillen Backup-Ausfall gebaut, der im vzdump-Stale-Lock-Artikel zehn Tage unbemerkt blieb:
pct status <vmid> # bei QEMU: qm status <vmid>
# steht dort "lock: backup" → freigeben
pct unlock <vmid> # bei QEMU: qm unlock <vmid>
Dazu ein zweiter, belegter Punkt: Manuelles vzdump bei einem ZFS-Pool über ~80 % Belegung hat auf demselben Hypervisor schon alle Gäste eingefroren. Vor einem Tagsüber-Nachhol-Lauf also erst die Pool-Belegung prüfen — und im Zweifel auf das Nachtfenster warten.
Lessons Learned
- Ein Handshake ist kein Beweis für Konnektivität. WireGuard trennt Crypto-Layer und IP-Layer vollständig.
wg showgrün heißt nur „Schlüssel okay" — für die IP-Frage brauchst duifconfig/ip azusätzlich, immer. - Teilweise funktionierender Traffic ist der gefährlichste Zustand beim Debuggen. Dass bestehende LAN-Routen weiterliefen, kostete mehr Zeit als ein komplett totes Interface gekostet hätte — ein sauberer Totalausfall wäre schneller gefunden gewesen.
- Parallele, unabhängige Fehler sind keine Korrelation. Der
pvestatd-Fingerprint-Fehler lief zufällig gleichzeitig — ohne Beweis für einen kausalen Zusammenhang nicht weiterverfolgen, auch wenn die Zeitnähe verlockend danach aussieht. - Physische Topologie schlägt logische Topologie. Ein Hub, der als VM auf derselben Maschine läuft, die ihn auch per Backup befüllen soll, bedeutet Hairpin-Traffic — jedes Byte zweimal über den Uplink. Nachholen von Backup-Rückständen braucht dieselbe Zeitfenster-Disziplin wie der reguläre Lauf.
- Credential-Lücken auf kritischen Hosts sind ein eigenes Risiko. Kein dokumentiertes SSH-Passwort für einen Hub, der Backups trägt, bedeutet im Ernstfall Screendump+sendkey statt eines normalen Logins — funktioniert, kostet aber Zeit, die man beim nächsten Incident nicht hat.
Checkliste
-
wg showzeigt frischen Handshake, aber nur minimalen Transfer → sofortifconfig/ip aauf dem Interface prüfen, nicht weiter in WireGuard-Logs suchen - Fehlt die
inet-Zeile → Adresse unter Interfaces (nicht unter VPN→WireGuard→Tunnels) als Static IPv4 setzen - Nach dem Fix: Beweis in
config.xmlziehen (grep ipaddr), nicht nur auf die GUI vertrauen - dpinger-Gateway-Status auf dem betroffenen Tunnel separat prüfen — „online" kann trotzdem komplett tot sein
- Vor dem Nachholen versäumter Backups: Topologie checken — läuft der Zielhub physisch auf derselben Uplink-Maschine, die die Backup-Daten sendet?
- Nachhol-Jobs ins bestehende Nachtfenster legen, nicht tagsüber durchdrücken, auch wenn Zabbix drängt
- Zum Stoppen:
pkill -f "[v]zdump"mit Bracket-Trick, nie blankpkill -f vzdump - Nach dem Abbruch
pct status/qm statusauflock: backupgeprüft und ggf.unlock— sonst stiller Backup-Ausfall beim nächsten Lauf - Vor Tagsüber-Nachhol-Läufen die ZFS-Pool-Belegung geprüft (über ~80 % → Freeze-Risiko)
- Für kritische Hosts ohne dokumentierte SSH-Credentials: Alternativzugang (z. B. Hypervisor-Konsole) vorher testen, nicht erst im Incident