Ik begon van buitenaf, zoals een gebruiker het zou doen: de naam in de browser van een telefoon op mobiele data getypt en gekeken hoe de verbinding bleef hangen tot ze in een time-out liep. De naam werd opgelost, dus DNS was in orde. Ook de port-forward-pagina van de thuisrouter was een dood spoor, want het WAN-adres was 100.64.31.42, en dat is geen adres waar een provider mij vanuit het internet naartoe routeert. Dat nummer verklaart de hele storing: de lijn zit achter carrier-grade NAT, dus het publieke adres aan mijn kant van de tunnel is van de provider. Wat volgt is de weg die ik liep van dat hangende tabblad naar een dienst die antwoordt.

Begin bij de client en werk naar binnen

Bewijs eerst waar de keten stopt, voordat je een regel wijzigt. Op de laptop voerde ik dig +short files.example.net uit en kreeg het adres van de VPS terug, wat verouderde DNS op mijn machine uitsloot. Daarna liep curl -v --connect-timeout 5 files.example.net:5022 in een time-out in plaats van te worden geweigerd. Dat verschil is belangrijk: een time-out betekent meestal dat pakketten onderweg worden weggegooid, terwijl een weigering betekent dat er iets antwoordde en nee zei.

De volgende hop naar binnen was de VPS zelf. ss -ltnp liet niets zien dat op 5022 luisterde, wat te verwachten was voordat de DNAT-regel bestond. Op dezelfde machine bevestigde wg show (of ip -br a show tun0 bij OpenVPN) dat de thuispeer online was met een recente handshake. Staat de peer uit, dan helpt geen enkele NAT-regel, en het groene lampje op de VPN-pagina van de thuisrouter betekent niets.

Bevestig dat de thuisleiding echt achter CGNAT zit

Vergelijk het adres dat de router op zijn WAN-interface meldt met wat een externe dienst ziet. Alles in 100.64.0.0/10, dus 100.64.x.x tot en met 100.127.x.x, is het shared-address-bereik uit RFC 6598 en is niet vanaf het internet bereikbaar. Je komt tot dezelfde conclusie wanneer de router een gewoon privéadres claimt en een publieke lookup iets ongerelateerds teruggeeft. Een CGNAT-lijn kan ook een publiek adres krijgen dat alleen tot de volgende lease geldt, zodat een port-forward een uur lijkt te werken en daarna stil sterft.

Sommige providers geven wel een echt publiek adres uit, maar blokkeren inkomende 80 en 443 op consumentenabonnementen. Is je dienst alleen web en is het WAN-adres echt publiek, vraag het dan aan de support voordat je een relay bouwt. Is dat niet zo, dan geldt het ontwerp hieronder voor elke poort evenzeer, en het loont om het experiment aan de routerkant te stoppen: de translator in het netwerk van de provider beëindigt de verbinding voordat ze ooit je WAN-interface bereikt.

Wijs de naam naar het adres van de VPS

DNS stuurt geen verkeer door, dus het A-record van de dienst moet het publieke adres van de VPS bevatten. Een A-record naar 192.168.1.20 lost alleen nuttig op voor clients die al in de VPN zitten; vanaf het internet lost het op naar een adres dat niemand kan routeren. Voor de beheerderstoegang van de VPS zelf houd ik een aparte naam aan, zodat ik nooit verwar op welke host een record wijst.

Als zowel de VPS als de thuisbak HTTP serveert, geef elk zijn eigen naam en laat de VPS beslissen wie antwoordt. Ze in één naam proppen en hopen dat de proxy de juiste backend kiest, is precies hoe je een middag lang de verkeerde host debugt.

DNAT één poort, laat SSH op 22

De directe optie is destination NAT op de VPS. Zet forwarding aan voor de draaiende kernel en houd het tunneladres van de thuisserver in de regel, niet zijn LAN-adres:

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

Laat SSH naar de VPS zelf op 22 en forward een andere poort, bijvoorbeeld 5022, naar de thuismachine. Forward je 22 naar een bak die uit staat, dan verlies je het enige pad dat het had kunnen repareren. Dezelfde logica geldt voor de proxy: houd de beheerpoort van de VPS buiten de relay-regels.

Maak de regels permanent zodra ze werken. Op Debian of Ubuntu schrijft apt install iptables-persistent en daarna netfilter-persistent save ze naar /etc/iptables/rules.v4. Op een firewalld-host is het equivalent een permanente forward-port op de zone, die firewall-cmd --reload nodig heeft voordat hij geldt. Regels die alleen in het geheugen leven, verdwijnen bij de eerste onderhoudsherstart van de provider, meestal op een slecht uur.

Repareer de retourroute, of maak hem overbodig

DNAT herschrijft alleen de bestemming. De thuisserver ziet een verbinding van het oorspronkelijke clientadres, en als zijn default route naar de thuisrouter wijst, verlaat het antwoord het huis via de WAN, waar de translator van de provider het laat vallen. Die asymmetrie was de tweede storing die ik trof, nadat de DNAT-regel er volkomen correct uitzag.

Twee oplossingen werken. Geef de thuisbak een beleidsroute zodat verkeer uit de tunnel weer door de tunnel teruggaat:

ip rule add from 10.8.0.2 table 100
ip route add default dev wg0 table 100

Of laat de VPS de bron herschrijven zodat het antwoord een adres heeft dat hij kan bereiken, en laat conntrack het op de weg naar buiten terugdraaien:

iptables -t nat -A POSTROUTING -d 10.8.0.2 -p tcp --dport 5022 -j SNAT --to-source 10.8.0.1

De SNAT-variant verbergt het echte clientadres voor de thuisserver, dus in de applicatielogs staat 10.8.0.1 als peer. Geef het originele adres door op de applicatielaag, of accepteer dat logging per client verloren gaat, maar beslis het bewust in plaats van het tijdens een incident te ontdekken.

Controleer het hele pad van buitenaf

Controleer vanuit een netwerk dat niet de VPS en niet het huis is: een telefoon op mobiele data, of een goedkope shell ergens anders. nc -vz files.example.net 5022 zou een geslaagde verbinding moeten melden, en openssl s_client -connect files.example.net:443 -servername files.example.net zou het certificaat voor de publieke naam moeten tonen. Controleer daarna de security group of cloudfirewall van de provider. Een DNAT-regel die nooit een pakket ziet, wordt meestal één laag boven de host geblokkeerd.

Ten slotte: beslis wat er gebeurt als de tunnel wegvalt. Een DNAT-regel blijft verbindingen aannemen en voert ze stil een dode tunnel in, wat er precies uitziet als een kapotte dienst. Een health check op de VPS die de poort weigert zolang de peer uit staat, kost een paar regels en bespaart een uur gokken. Stop daarna met regels bewerken en kijk er een dag naar voordat je het vertrouwt.

Gerelateerde artikelen