Heimserver über einen VPS veröffentlichen, wenn CGNAT Ports blockiert
Der Weg vom Browser zum Heimrechner: nachweisen, dass die Leitung hinter CGNAT liegt, DNS auf den VPS zeigen lassen, einen Port per DNAT weiterleiten, den Rückweg reparieren und von außen prüfen.
Ich begann von außen, so wie ein Nutzer es tun würde: den Namen im Handy-Browser über Mobilfunk eingetippt und zugesehen, wie die Verbindung hing, bis sie in einen Timeout lief. Der Name wurde aufgelöst, DNS war also in Ordnung. Auch die Port-Forwarding-Seite des Heimrouters war eine Sackgasse, denn ihre WAN-Adresse lautete 100.64.31.42, und das ist keine Adresse, zu der ein Anbieter mir aus dem Internet weiterleitet. Diese Nummer erklärt den ganzen Fehler: Die Leitung liegt hinter Carrier-Grade-NAT, die öffentliche Adresse auf meiner Seite des Tunnels gehört also dem Anbieter. Was folgt, ist der Weg von jenem hängenden Tab zu einem Dienst, der antwortet.
Beim Client anfangen und nach innen arbeiten
Bevor du irgendeine Regel änderst, beweise, wo die Kette endet. Auf dem Laptop führte ich dig +short files.example.net aus und bekam die VPS-Adresse zurück, was veraltetes DNS auf meiner Maschine ausschloss. Danach lief curl -v --connect-timeout 5 files.example.net:5022 in einen Timeout, statt abgelehnt zu werden. Dieser Unterschied zählt: Ein Timeout heißt meist, dass Pakete unterwegs verworfen werden, während eine Ablehnung heißt, dass etwas geantwortet und Nein gesagt hat.
Der nächste Hop nach innen war der VPS selbst. ss -ltnp zeigte nichts, das auf 5022 lauschte, was vor der DNAT-Regel zu erwarten war. Auf derselben Maschine bestätigte wg show (oder ip -br a show tun0 bei OpenVPN), dass der Heim-Peer oben war und einen frischen Handshake hatte. Ist der Peer unten, hilft keine NAT-Regel, und das grüne Licht auf der VPN-Seite des Heimrouters bedeutet nichts.
Bestätigen, dass die Heimleitung wirklich hinter CGNAT liegt
Vergleiche die Adresse, die der Router auf seinem WAN-Interface meldet, mit dem, was ein externer Dienst sieht. Alles in 100.64.0.0/10, also 100.64.x.x bis 100.127.x.x, ist der Shared-Address-Bereich aus RFC 6598 und aus dem Internet nicht erreichbar. Zum selben Schluss kommst du, wenn der Router eine gewöhnliche private Adresse behauptet und eine öffentliche Abfrage etwas Unzusammenhängendes zurückgibt. Eine CGNAT-Leitung kann außerdem eine öffentliche Adresse mitbringen, die nur bis zum nächsten Lease hält, sodass eine Portweiterleitung eine Stunde zu funktionieren scheint und dann still stirbt.
Manche Anbieter vergeben tatsächlich eine echte öffentliche Adresse, blocken aber eingehende 80 und 443 bei Privatkundenanschlüssen. Ist dein Dienst reines Web und die WAN-Adresse wirklich öffentlich, frag den Support, bevor du ein Relay baust. Ist sie es nicht, gilt das folgende Design für jeden Port gleichermaßen, und es lohnt sich, das Experiment auf der Routerseite zu beenden: Der Translator im Anbieternetz beendet die Verbindung, bevor sie je dein WAN-Interface erreicht.
Den Namen auf die VPS-Adresse zeigen lassen
DNS leitet keinen Verkehr weiter, also muss der A-Record für den Dienst die öffentliche Adresse des VPS enthalten. Ein A-Record auf 192.168.1.20 löst nur für Clients nützlich auf, die schon im VPN sind; aus dem Internet löst er auf eine Adresse auf, die niemand routen kann. Für den Admin-Zugang des VPS selbst halte ich einen eigenen Namen bereit, damit ich nie verwechsle, auf welchen Host sich ein Record bezieht.
Wenn sowohl der VPS als auch die Heimbox HTTP ausliefern, gib jedem seinen eigenen Namen und lass den VPS entscheiden, wer antwortet. Beides in einen Namen zu falten und zu hoffen, der Proxy wähle das richtige Backend, endet damit, dass du einen Nachmittag lang den falschen Host debuggst.
Einen Port per DNAT, SSH bleibt auf 22
Die direkte Variante ist Destination-NAT auf dem VPS. Aktiviere Forwarding für den laufenden Kernel und halte die Tunneladresse des Heimservers in der Regel, nicht seine LAN-Adresse:
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A PREROUTING -p tcp -d 198.51.100.10 --dport 5022 -j DNAT --to-destination 10.8.0.2
Lass SSH zum VPS selbst auf 22 und leite einen anderen Port, etwa 5022, an die Heimmaschine weiter. Leite 22 an eine Kiste weiter, die unten ist, und du verlierst den einzigen Pfad, der das hätte reparieren können. Dieselbe Logik gilt für den Proxy: Lass den Verwaltungsport des VPS außerhalb der Relay-Regeln.
Mach die Regeln dauerhaft, sobald sie funktionieren. Unter Debian oder Ubuntu schreibt apt install iptables-persistent und danach netfilter-persistent save sie nach /etc/iptables/rules.v4. Auf einem firewalld-Host ist das Gegenstück ein permanenter Forward-Port auf der Zone, der vor dem Wirksamwerden firewall-cmd --reload braucht. Regeln, die nur im Speicher leben, verschwinden beim ersten Wartungs-Neustart des Anbieters, meist zu einer schlechten Stunde.
Den Rückweg reparieren oder ihn überflüssig machen
DNAT schreibt nur das Ziel um. Der Heimserver sieht eine Verbindung von der ursprünglichen Client-Adresse, und wenn seine Default-Route auf den Heimrouter zeigt, verlässt die Antwort das Haus über das WAN, wo der Translator des Anbieters sie fallen lässt. Diese Asymmetrie war der zweite Fehler, auf den ich stieß, nachdem die DNAT-Regel völlig korrekt aussah.
Zwei Korrekturen funktionieren. Gib der Heimbox eine Policy-Route, damit Verkehr aus dem Tunnel wieder durch den Tunnel geht:
ip rule add from 10.8.0.2 table 100
ip route add default dev wg0 table 100
Oder lass den VPS die Quelle umschreiben, damit die Antwort eine Adresse hat, die er erreichen kann, und lass conntrack sie auf dem Weg nach draußen zurückdrehen:
iptables -t nat -A POSTROUTING -d 10.8.0.2 -p tcp --dport 5022 -j SNAT --to-source 10.8.0.1
Die SNAT-Variante verbirgt die echte Client-Adresse vor dem Heimserver, in den Anwendungslogs steht als Peer also 10.8.0.1. Gib die Originaladresse auf der Anwendungsebene weiter, oder akzeptiere, dass das Logging pro Client verloren geht, aber entscheide es bewusst, statt es während eines Vorfalls zu entdecken.
Den ganzen Pfad von außen prüfen
Prüfe aus einem Netz, das weder der VPS noch das Haus ist: ein Handy über Mobilfunk oder eine billige Shell anderswo. nc -vz files.example.net 5022 sollte eine erfolgreiche Verbindung melden, und openssl s_client -connect files.example.net:443 -servername files.example.net sollte das Zertifikat für den öffentlichen Namen zeigen. Dann prüfe die Security Group oder Cloud-Firewall des Anbieters. Eine DNAT-Regel, die nie ein Paket sieht, wird meist eine Schicht über dem Host blockiert.
Zuletzt: entscheide, was passiert, wenn der Tunnel wegfällt. Eine DNAT-Regel nimmt weiter Verbindungen an und füttert sie still in einen toten Tunnel, was genau wie ein kaputter Dienst aussieht. Ein Health Check auf dem VPS, der den Port abweist, solange der Peer unten ist, kostet ein paar Zeilen und spart eine Stunde Raten. Danach hör auf, Regeln zu bearbeiten, und beobachte es einen Tag lang, bevor du ihm traust.