← Archief / Netwerk

Een eigen CGNAT-exit met Hetzner en Wireguard

Als je ISP je achter een carrier-grade NAT zet en je toch inkomend verkeer wilt. €5 per maand, één avond werk.

Een eigen CGNAT-exit met Hetzner en Wireguard

Een eigen CGNAT-exit met Hetzner en WireGuard

Als je ISP je achter een carrier-grade NAT zet en je toch inkomend verkeer wilt. €5 per maand, één avond werk.

Je wil een dienst bereikbaar maken vanuit het internet — Jellyfin, een kleine webserver, een Minecraft-server voor vrienden. Je opent de juiste poort op je router, test via je gsm op 4G, en... niets. Je externe IP blijkt ergens in de reeks 100.64.x.x te zitten. Dat is het kenteken van CGNAT: carrier-grade NAT, waarbij je ISP één publiek IPv4-adres deelt over tientallen tot honderden klanten. Jij bezit dat adres niet — je deelt het, en inkomende verbindingen worden nooit tot bij jou doorgestuurd.

In België is CGNAT geen randfenomeen. Proximus paste het lange tijd toe op mobiele abonnementen en experimenteerde er tijdelijk mee op vaste lijnen. Telenet gebruikt het standaard op mobiel internet — een IP uit de 100.64.0.0/10-range is het verraderlijke teken. Uitschakelen kan soms via een supportticket, maar het lukt niet altijd, en op een MVNO of prepaid kaart is het quasi altijd hard ingesteld. De echte oplossing is een eigen exit-punt: een goedkope VPS met een publiek IP, en een WireGuard-tunnel die je thuismachine via dat VPS bereikbaar maakt.

Wat je nodig hebt en hoe het werkt

De topologie is eenvoudig: je huurt een kleine VPS bij Hetzner — de CX22 op het moment van schrijven rond de €4–5 per maand, met 2 vCPU's, 4 GB RAM en 20 TB bandbreedte. Op die VPS installeer je WireGuard als server. Je thuismachine draait WireGuard als client en maakt outbound verbinding naar de VPS. Dat kan altijd, ook vanachter CGNAT — het is de thuismachine die de handshake initieert. Vervolgens stuur je via iptables inkomend verkeer op de VPS door via de tunnel naar je thuismachine. Voor de buitenwereld is het VPS-IP het publieke adres; de VPS weet dankzij de tunnel je thuismachine te bereiken.

Vereisten
VPSHetzner CX22 of vergelijkbaar — Debian 12 of Ubuntu 24.04 LTS
ThuisElke Linux-machine (ook Raspberry Pi of router met OpenWrt)
WireGuardKernel 5.6+ (ingebouwd) of wireguard-dkms op oudere kernels
Root/sudoOp VPS én op thuismachine
Kosten~€4–5/mnd (Hetzner CX22, excl. btw) + optioneel domein

Je hebt geen domein nodig om dit te laten werken — het publieke IP van de VPS is voldoende. Een domein maakt het enkel makkelijker te onthouden.

Stap 1: WireGuard installeren en keypairs genereren

Begin met een verse Debian 12 of Ubuntu 24.04 instantie op Hetzner. Zorg dat je via SSH bent ingelogd als root of een sudo-gebruiker.

VPS — WireGuard installeren
apt update && apt install wireguard -y

# IP-forwarding permanent inschakelen
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p

Genereer daarna keypairs op beide machines afzonderlijk. De private key verlaat de machine nooit.

VPS én thuismachine — keypair genereren
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
chmod 600 /etc/wireguard/private.key

# Toon public key — noteer deze, je hebt hem nodig aan de andere kant
cat /etc/wireguard/public.key

Je eindigt met vier strings: een private en een public key per machine. Noteer de public key van de VPS en de public key van de thuismachine — die wisselen de peers onderling uit in de configuratie.

Stap 2: WireGuard configureren

We gebruiken het subnet 10.200.200.0/24 voor de tunnel. De VPS krijgt 10.200.200.1, je thuismachine 10.200.200.2. Vervang PRIVATE_KEY_VPS en PUBLIC_KEY_THUIS door de sleutels die je in stap 1 hebt gegenereerd. De interface-naam op Hetzner is doorgaans eth0 — controleer met ip -br link.

/etc/wireguard/wg0.conf — VPS
[Interface]
Address    = 10.200.200.1/24
ListenPort = 51820
PrivateKey = PRIVATE_KEY_VPS

# Inkomend verkeer op poort 8080 doorsturen naar thuismachine
PostUp   = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.200.200.2:8080
PostUp   = iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.200.200.2:8080
PostDown = iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE

[Peer]
PublicKey           = PUBLIC_KEY_THUIS
AllowedIPs          = 10.200.200.2/32
PersistentKeepalive = 25

Op je thuismachine is de configuratie iets anders: die verbindt actief naar de VPS en stuurt al zijn verkeer niet door de tunnel — enkel het tunnel-subnet. Endpoint is het publieke IP van je VPS-instantie.

/etc/wireguard/wg0.conf — Thuismachine
[Interface]
Address    = 10.200.200.2/24
PrivateKey = PRIVATE_KEY_THUIS
DNS        = 1.1.1.1

[Peer]
PublicKey            = PUBLIC_KEY_VPS
Endpoint             = 95.217.X.X:51820
AllowedIPs           = 10.200.200.0/24
PersistentKeepalive  = 25

De PersistentKeepalive = 25 is cruciaal bij CGNAT. Zonder keepalive sluit de NAT-tabel in de infrastructuur van je ISP de verbindingsstate na een paar minuten stilte. Resultaat: de handshake is intact maar pakketten bereiken je machine niet meer. Elke 25 seconden een keepalive-pakket sturen houdt de state levend.

PersistentKeepalive = 25 is niet optioneel achter CGNAT. Zonder die instelling lijkt de tunnel te werken maar valt hij na enkele minuten stilte onzichtbaar dood.

Stap 3: tunnel starten en DNAT-regels controleren

Start WireGuard op beide machines en zorg dat het automatisch opkomt bij herstart.

VPS én thuismachine
# Tunnel starten
wg-quick up wg0

# Automatisch starten bij boot
systemctl enable wg-quick@wg0

# Status controleren
wg show wg0

Na een paar seconden zou je bij wg show onder de [Peer]-sectie een latest handshake moeten zien. Als die er niet is, controleer dan of poort 51820 UDP bereikbaar is op de VPS. Hetzner laat standaard alle verkeer door, maar als je de Hetzner firewall (via de Cloud Console) hebt ingeschakeld, voeg je een UDP-regel toe voor poort 51820.

Verifieer daarna de DNAT-regels. Na wg-quick up wg0 voert WireGuard de PostUp-regels uit. Controleer ze:

VPS — iptables-regels inspecteren
# NAT-tabel bekijken
iptables -t nat -L -n -v

# Verwachte output voor PREROUTING:
# DNAT  tcp  --  eth0  *  0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:10.200.200.2:8080

# Verwachte output voor POSTROUTING:
# MASQUERADE  all  --  *  wg0  0.0.0.0/0  0.0.0.0/0

Test vervolgens: zet een simpele webserver op je thuismachine op poort 8080, en surf vanuit een externe verbinding naar het publieke IP van je VPS op poort 8080.

Thuismachine — snelle testserver
# Python HTTP-server op poort 8080
python3 -m http.server 8080

# Testen vanaf een externe machine of je gsm op 4G/5G:
curl http://VPS_PUBLIEK_IP:8080

Werkt het? Dan is je CGNAT-exit operationeel. Meerdere poorten doorsturen is simpelweg meer PostUp-regels toevoegen. Voor UDP (game server, VoIP) vervang je -p tcp door -p udp in de DNAT-regel.

/etc/wireguard/wg0.conf (VPS) — meerdere poorten
# Jellyfin (TCP 8096)
PostUp   = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8096 -j DNAT --to-destination 10.200.200.2:8096
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 8096 -j DNAT --to-destination 10.200.200.2:8096

# Game server (UDP 25565 — Minecraft)
PostUp   = iptables -t nat -A PREROUTING -i eth0 -p udp --dport 25565 -j DNAT --to-destination 10.200.200.2:25565
PostDown = iptables -t nat -D PREROUTING -i eth0 -p udp --dport 25565 -j DNAT --to-destination 10.200.200.2:25565

Waarom geen Cloudflare Tunnel?

Cloudflare Tunnel is gratis en werkt ook vanachter CGNAT. Het nadeel is dat Cloudflare je TLS-verkeer termineert — de tunnel loopt via Cloudflare's infrastructuur, en Cloudflare decodeert het langs zijn kant. Voor een publieke blog maakt dat weinig uit; voor een privé Jellyfin-instantie of een VPN naar je homelab wil je dat misschien niet. Bovendien ondersteunt Cloudflare Tunnel enkel HTTP en HTTPS — geen raw TCP of UDP, dus geen game server, geen RTSP-stream, geen willekeurig protocol.

De eigen WireGuard-exit geeft je volledige controle: elk protocol, geen derde partij die meekijkt, en je betaalt een vaste €4–5 per maand voor zoveel poorten als je wil. Het enige wat je inlevert tegenover Cloudflare is het gratis gedeelte en de ingebouwde DDoS-bescherming. Voor een homelab die geen publiek profiel heeft, is dat zelden een probleem.

Vergelijking: eigen WireGuard-exit vs Cloudflare Tunnel
WireGuard + VPS Cloudflare Tunnel
Kosten~€5/mndGratis (free tier)
ProtocollenAlles (TCP, UDP, ICMP)Alleen HTTP/HTTPS
PrivacyEnd-to-end versleuteldCloudflare termineert TLS
DDoS-beschermingGeen (eigen verantwoordelijkheid)Ingebouwd
ComplexiteitMatig — één avondLaag — tien minuten
AfhankelijkheidAlleen je eigen VPSCloudflare-platform

Veelgestelde vragen

Mijn tunnel komt up maar ik kan poort 8080 niet bereiken van buitenaf.
Controleer drie dingen: (1) of de DNAT-regel in iptables aanwezig is (iptables -t nat -L PREROUTING -n), (2) of de Hetzner firewall in de Cloud Console geen TCP 8080 blokkeert, en (3) of de dienst op je thuismachine ook echt luistert op 0.0.0.0:8080 en niet alleen op 127.0.0.1:8080.
Na een tijdje valt de verbinding weg en moet ik wg-quick down/up doen.
Bijna zeker een keepalive-probleem. Controleer of PersistentKeepalive = 25 in de [Peer]-sectie van je thuismachine staat. Vergeet ook niet dat als je thuis een reboot doet, systemctl enable wg-quick@wg0 de tunnel automatisch herstart.
Kan ik meerdere thuismachines via dezelfde VPS bereikbaar maken?
Ja. Voeg voor elke thuismachine een extra [Peer]-sectie toe op de VPS met een uniek tunnel-IP (bv. 10.200.200.3, 10.200.200.4). Per machine stel je afzonderlijke DNAT-regels in die naar het correcte tunnel-IP verwijzen. Poorten mogen niet overlappen tenzij je verschillende publieke poorten koppelt aan dezelfde dienst-poort op verschillende machines.
Werkt dit ook met IPv6?
WireGuard ondersteunt IPv6 volledig. Als je VPS een publiek IPv6-adres heeft, kun je naast de IPv4 DNAT-regels ook ip6tables-regels toevoegen. Veel Belgische ISP's bieden ondertussen native IPv6 aan — controleer met ip -6 addr — maar CGNAT is per definitie een IPv4-probleem. Wie een publiek IPv6-adres heeft, bereikt zijn diensten al direct zonder tunnel.
Is er een MTU waar ik rekening mee moet houden?
WireGuard voegt 60 byte overhead toe aan IPv4-pakketten. Op een standaard Ethernet-verbinding met MTU 1500 stel je de WireGuard-interface-MTU in op 1420 om fragmentatie te vermijden. Voeg MTU = 1420 toe aan de [Interface]-sectie van je configuratie als je vreemde verbrekingen ziet bij grotere bestandsoverdrachten.

Verder lezen

Uit de categorie Netwerk
ESC
Recent geopend