Hoe werkt WireGuard cryptokey routing?
Begrijp hoe WireGuard werkt door cryptokey routing en AllowedIPs te analyseren. Leer hoe deze instelling tegelijkertijd als routeringstabel en toegangscontrolelijst fungeert.
Hoe WireGuard werkt, in één idee
WireGuard werkt door elk pakket te koppelen aan een publieke sleutel. Dit mechanisme heet cryptokey routing en vormt het volledige ontwerp: de AllowedIPs regel naast een peer is de routeringstabel voor pakketten die uw machine verlaten en de toegangscontrolelijst voor pakketten die van die peer binnenkomen. Eén instelling, twee taken. Lees AllowedIPs op die manier en elk WireGuard configuratiebestand wordt begrijpelijk.
Er is geen sessietabel op basis van IP-adressen en geen gebruikersdatabase. Een peer is een publieke sleutel plus de set adressen die die sleutel mag gebruiken. De handshake en de timers bestaan om die binding geldig te houden terwijl het onderliggende netwerk verandert. Als u een werkende tunnel wilt voordat u de theorie bestudeert, bouw er dan een met een zelfgehoste WireGuard VPN op uw eigen VPS, en keer hier terug wanneer een configuratieregel u verrast.
AllowedIPs is een routeringstabel en een toegangslijst
Kijk eerst naar de uitgaande richting. Uw kernel routeert een pakket op de gebruikelijke wijze naar het wg0-apparaat via de hoofdrouteringstabel. WireGuard vergelijkt vervolgens het bestemmingsadres van dat pakket met een tabel die alle toegestane prefixen van elke peer bevat, waarbij de langste prefix eerst wordt gecontroleerd. Een overeenkomst wijst een peer aan, die een publieke sleutel aanwijst, die op zijn beurt een sessiesleutel en een UDP-eindpunt aanwijst. Het pakket wordt voor die peer versleuteld en daarheen verzonden.
Als geen enkele AllowedIPs van een peer de bestemming dekt, wordt er niets verzonden, omdat er geen sleutel is om het mee te verzenden.
ping: sendmsg: Required key not availableDie foutmelding betekent één ding: het adres dat u probeerde te bereiken, staat niet vermeld onder een peer. Een andere foutmelding, ping: sendmsg: Destination address required, betekent dat er wel een peer overeenkwam, maar dat WireGuard geen eindpunt voor deze peer heeft, omdat er geen was geconfigureerd en er nog geen was geleerd.
Kijk nu naar de inkomende richting. Een UDP-pakket komt aan op de luisterpoort. WireGuard vindt de sessie op basis van de ontvangerindex in de header, controleert de teller tegen een sliding replay window en ontsleutelt en authenticeert vervolgens de payload. Pas daarna leest het het interne pakket, en het bronadres van dat interne pakket moet binnen de AllowedIPs van de verzendende peer vallen. Als dat niet zo is, wordt het pakket genegeerd. Met dynamische debug ingeschakeld, print de kernel de reden in een regel zoals deze:
wg0: Packet has unallowed src IP (10.8.0.9) from peer 2 (203.0.113.10:51820)Dit is de reden waarom een peer aan de serverzijde een /32 krijgt. Een peer die is geconfigureerd met AllowedIPs = 10.8.0.2/32 mag pakketten verzenden vanaf 10.8.0.2 en vanaf geen enkel ander adres. Schrijf daar in plaats daarvan 0.0.0.0/0 en die specifieke client krijgt toestemming om pakketten te injecteren die elk bronadres binnen uw tunnel claimen, inclusief dat van een andere client.
Overlappende prefixen worden opgelost op basis van specificiteit, aangezien de opzoeking verloopt via de langste prefix-overeenkomst. Identieke prefixen bij twee peers gedragen zich anders: het item verplaatst zich naar de peer die als laatste is geconfigureerd, en de eerste peer stopt met het ontvangen van dat verkeer zonder dat er ergens een foutmelding wordt geprint. wg show wg0 allowed-ips print de tabel die daadwerkelijk in de kernel staat; dit is wat telt wanneer het bestand op schijf en de actieve status van elkaar zijn gaan afwijken.
Een configuratiebestand lezen met cryptokey-routing in gedachten
De serverzijde:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <server private key>
[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32De clientzijde:
[Interface]
Address = 10.8.0.2/32
PrivateKey = <laptop private key>
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25Hetzelfde trefwoord heeft aan beide zijden een tegengestelde betekenis. Op de client betekent het "stuur elke bestemming naar deze peer". Op de server betekent het "accepteer alleen dit ene adres van deze peer". De asymmetrie zit in de waarden, niet in een specifieke rol.
De helft van deze sleutels maakt geen deel uit van het protocol. Address, DNS, MTU, PostUp en SaveConfig behoren tot wg-quick, het shell-script dat de interface activeert. De kernel ziet deze nooit. wg-quick strip wg0 toont de gereduceerde configuratie die de wg-tool daadwerkelijk inlaadt; dit is de snelste manier om dat onderscheid te zien.
Wat de handshake daadwerkelijk doet
De handshake van WireGuard is Noise_IKpsk2, afkomstig uit het Noise Protocol Framework. Het IK-gedeelte is het relevante deel voor een systeembeheerder: de statische publieke sleutel van de responder is al bekend bij de initiator, aangezien dit de PublicKey is in uw [Peer]-blok. De initiator verstuurt zijn eigen statische publieke sleutel versleuteld in het eerste bericht. Er vindt dus geen certificaatuitwisseling of identiteits-round-trip plaats. Een passieve waarnemer kan niet zien welke sleutel de verbinding initieert, tenzij deze beschikt over de privésleutel van de responder.
De kosten hiervan bedragen één round-trip. Het initiatiebericht is 148 bytes groot, het antwoord is 92 bytes, en daarna vloeit het dataverkeer direct. Elke zijde genereert per handshake een vers, efemeer Curve25519-sleutelpaar. De sessiesleutels worden afgeleid uit een keten van Diffie-Hellman-resultaten die de statische en efemere sleutels combineert. De efemere privésleutels worden daarna vernietigd, wat zorgt voor forward secrecy: iemand die vandaag uw verkeer opneemt en volgend jaar de privésleutel van de server steelt, kan de opgenomen data alsnog niet ontsleutelen.
Een handshake-initiatie bevat een TAI64N-tijdstempel. Elke peer onthoudt de hoogste tijdstempel die hij van de andere partij heeft ontvangen, waardoor een herhaalde (replayed) initiatie wordt geweigerd. Datapakketten bevatten een 64-bit teller die als nonce dient. De ontvanger houdt een schuifvenster bij van recent geziene tellers, waardoor replays en sterke pakket-herschikking worden afgehandeld zonder TCP-achtige verbindingsstatus.
Sessiesleutels hebben een korte levensduur en de timers zijn in de code gecompileerd in plaats van configureerbaar.
The data behind this chart
[
{
"label": "REKEY_TIMEOUT",
"seconds": 5,
"notes": "resend a handshake initiation that got no answer"
},
{
"label": "KEEPALIVE_TIMEOUT",
"seconds": 10,
"notes": "send a keepalive after receiving data and sending none back"
},
{
"label": "REKEY_ATTEMPT_TIME",
"seconds": 90,
"notes": "give up on the handshake and report the peer as down"
},
{
"label": "REKEY_AFTER_TIME",
"seconds": 120,
"notes": "sender begins a fresh handshake for a new session key"
},
{
"label": "REJECT_AFTER_TIME",
"seconds": 180,
"notes": "the old session key is refused and traffic stops"
}
]Dit zijn constanten uit de protocolspecificatie, geen metingen. De 5 timers sturen de volledige levenscyclus van de sessie aan. Na 120 seconden gebruik start de zender een nieuwe handshake. Na 180 seconden wordt de oude sleutel direct geweigerd, waardoor het verkeer stopt totdat een nieuwe handshake is voltooid. Een initiatie die geen antwoord krijgt, wordt elke 5 seconden opnieuw verzonden en na 90 seconden afgebroken. Dit is de reden waarom wg show de waarde latest handshake weergeeft als een relatieve leeftijd, en waarom een actieve, gezonde tunnel die leeftijd laag houdt. Een leeftijd die oploopt terwijl u actief verkeer verstuurt, betekent dat handshakes mislukken, niet dat de tunnel inactief is.
Waarom een peer geen client- of serverrol heeft
Beide zijden draaien identieke code en gebruiken hetzelfde configuratieformaat. Er is geen servermodus. De asymmetrie die u ervaart, komt voort uit Endpoint, en Endpoint is optioneel.
Een peer met een geconfigureerd endpoint kan een handshake starten. Een peer zonder endpoint wacht af en leert het adres en de poort van de tegenpartij kennen via het eerste pakket dat correct wordt geverifieerd. Dit geleerde endpoint wordt opgeslagen en bijgewerkt zodra er een geldig pakket vanaf een nieuw adres binnenkomt. Zo werkt roaming: een laptop die overschakelt van wifi naar een mobiel netwerk behoudt dezelfde tunnel, omdat een sessie wordt geïdentificeerd op basis van sleutel en index in plaats van op IP-adres. Er hoeft niets opnieuw verbonden te worden, omdat er in de TCP-zin nooit sprake was van een verbinding.
Hetzelfde mechanisme zorgt voor een belangrijk feit: de peer met het publieke adres beschikt altijd over het laatst bekende publieke IP-adres van de tegenpartij, en wg show toont dit.
Vaste primitieven, zonder onderhandelingsfase
WireGuard bevat geen lijst met ciphersuites. Er wordt gebruikgemaakt van ChaCha20-Poly1305 voor geauthenticeerde encryptie, Curve25519 voor sleuteluitwisseling, BLAKE2s voor hashing en HKDF voor sleutelafleiding. Elke implementatie gebruikt deze standaarden, waardoor er geen onderhandelingsfase is om te analyseren en geen mogelijkheid bestaat om terug te vallen op een zwakkere optie. Dit is een bewuste keuze: als een van deze primitieven wordt gekraakt, is de oplossing een nieuwe versie van het volledige protocol en een update aan beide zijden, in plaats van een configuratiewijziging. Deze beslissing elimineert het merendeel van de code en de faalmodi die inherent zijn aan een TLS-gebaseerde tunnel, wat in essentie het belangrijkste punt is van de vergelijking in WireGuard versus OpenVPN.
Waarom de poort niet reageert op een scanner
Elk handshake-bericht bevat een veld genaamd mac1. Dit is een MAC (message authentication code) die over het bericht wordt berekend met een sleutel die is afgeleid van de statische publieke sleutel van de responder. Een zender die die publieke sleutel niet kent, kan geen geldige mac1 produceren, en de ontvanger negeert een dergelijk pakket zonder enig antwoord. Geen foutmelding, geen reset, geen ICMP-bericht.
Het zichtbare resultaat is een UDP-scan die niets terugkrijgt.
sudo nmap -sU -p 51820 vpn.example.comnmap rapporteert open|filtered, wat hetzelfde antwoord is als voor een poort die door een firewall stilzwijgend wordt genegeerd. De poort gedraagt zich hetzelfde, ongeacht of WireGuard luistert of niet, althans voor iedereen die niet al over uw publieke sleutel beschikt.
Een tweede veld, mac2, vangt druk door denial of service-aanvallen op. Wanneer de ontvanger onder belasting staat, beantwoordt deze een geldige initiatie met een cookie-antwoord van 64 bytes dat gekoppeld is aan het bronadres van de zender. De ontvanger weigert kostbaar rekenwerk met publieke sleutels uit te voeren totdat de zender die cookie terugstuurt. Dit bewijst dat het bronadres echt is voordat er CPU-capaciteit aan wordt besteed, en dit mechanisme wordt alleen geactiveerd onder belasting.
Waarom 0.0.0.0/0 van een peer uw default route maakt
Omdat AllowedIPs de routeringstabel is, claimt AllowedIPs = 0.0.0.0/0, ::/0 elke bestemming voor die peer. Dat is de volledige instelling voor een full tunnel.
De routering die dit mogelijk maakt, is interessanter dan de regel zelf. Een standaard default route via wg0 zou een loop veroorzaken, omdat het versleutelde UDP-pakket dat uw verkeer vervoert ook de machine moet verlaten en zou matchen met zijn eigen default route. wg-quick voorkomt dit met policy routing. Het markeert de uitgaande pakketten van WireGuard met een fwmark, plaatst de default route van de tunnel in een aparte routeringstabel en voegt regels toe zodat alleen ongemarkeerd verkeer deze bereikt. Voer ip rule show uit om het resultaat te zien:
32764: from all lookup main suppress_prefixlength 0
32765: not from all fwmark 0xca6c lookup 51820
32766: from all lookup main0xca6c is 51820 in hexadecimale notatie, en 51820 is ook het tabelnummer. De suppress_prefixlength 0 regel zorgt ervoor dat de main tabel zijn eigen default route overslaat, waardoor specifieke routes zoals uw lokale subnet voorrang behouden, terwijl al het overige verkeer naar de tunneltabel wordt verwezen. Een split tunnel heeft dit niet nodig: een beperktere lijst zoals AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 wordt simpelweg opgenomen als normale routes in de main tabel.
Eén ding dat een full tunnel niet uit zichzelf oplost, is naamresolutie. De resolver die uw client heeft verkregen van het lokale netwerk blijft meestal actief en de route daarheen is specifieker. Dat is een afzonderlijke taak, die wordt behandeld in DNS die buiten een WireGuard tunnel lekt.
Waar PersistentKeepalive werkelijk voor dient
WireGuard verstuurt niets wanneer er geen verkeer is. Geen heartbeat, geen sessieverversing, niets op de lijn. Die stilte is gunstig voor de batterijduur en helpt in het hierboven genoemde scenario met scanners, maar het verstoort één specifieke configuratie.
Een peer achter NAT (network address translation) of achter een stateful firewall is alleen van buitenaf bereikbaar zolang er een mapping bestaat in dat apparaat, en die mapping is gecreëerd door een uitgaand pakket. De levensduur van gangbare UDP-mappings begint bij ongeveer 30 seconden. Zodra de mapping verloopt, worden pakketten vanaf de publieke zijde door de middlebox geweigerd en lijkt de tunnel dood totdat de peer achter de NAT zelf iets verstuurt. PersistentKeepalive = 25 verstuurt elke 25 seconden een leeg, geauthenticeerd pakket. Dit valt binnen de kortste gangbare levensduur, waardoor de mapping open blijft.
Stel dit in op de peer die zich achter de NAT bevindt. Een server met een publiek adres en een open UDP-poort heeft dit niet nodig; het daar instellen zorgt alleen voor extra verkeer. Verwar dit niet met de automatische keepalive, die wordt geactiveerd 10 seconden nadat een peer gegevens heeft ontvangen en zelf niets terug te sturen heeft. Die functie staat altijd aan en kan niet worden geconfigureerd.
Het routeren van uw LAN over de tunnel is geen WireGuard-functionaliteit
Stel dat peer B zich op een thuisnetwerk 192.168.50.0/24 bevindt en peer A moet dit kunnen bereiken. Twee afzonderlijke systemen moeten op elkaar worden afgestemd, en slechts één daarvan is WireGuard.
Het deel van WireGuard: voeg 192.168.50.0/24 toe aan de AllowedIPs van B op A. Hierdoor routeert A het prefix naar B en accepteert A pakketten met die bronadressen van B. Zonder dit heeft cryptokey-routing geen sleutel voor de bestemming en geen toestemming voor de bron.
Het deel van de kernel: op B moet net.ipv4.ip_forward gelijk zijn aan 1, anders verwijdert de kernel elk ontsleuteld pakket dat niet aan B zelf is geadresseerd. De forward-chain van de firewall op B moet het verkeer toestaan. De hosts op het LAN hebben een route terug naar 10.8.0.0/24 nodig, of B moet source NAT toepassen zodat antwoorden via B terugkeren.
De taak van WireGuard eindigt zodra het de ontsleutelde pakketten aan de kernel overdraagt. Alles daarna valt onder normale Linux-routing en -filtering. Daarom verschijnt deze fout in de tellers van nft list ruleset of in ip -s link show wg0, en niet in wg show. Als u peers liever via een webinterface beheert, maakt wg-easy in Docker uitvoeren de peerconfiguraties voor u aan. De forwardingregels blijven echter bij de host horen. Deze scheiding blijft ook bestaan wanneer een coördinatielaag het prefix voor u distribueert. Een privénetwerk vanaf een VPS adverteren met een Tailscale-subnetrouter vervangt de handmatige bewerking van AllowedIPs op elke peer. De forwarding-sysctl en de firewallregels op de router zelf moet u echter nog steeds instellen.
Waarom WireGuard in de kernel draait
wg0 is een netwerk-device driver. Pakketten bereiken deze via de normale routing-stack, worden versleuteld in de softirq-context en verlaten het systeem via een UDP-socket zonder ooit naar userspace te gaan. Dat is waar de doorvoersnelheid vandaan komt, en het is ook de reden waarom de module uit ongeveer vierduizend regels code bestaat; klein genoeg om te controleren en in maart 2020 in de mainline Linux 5.6 te mergen. Ubuntu 24.04 en Debian 13 leveren het standaard mee, dus alleen het wireguard-tools-pakket ontbreekt nog.
Het zijn van een normale interface heeft praktische gevolgen. tcpdump -ni wg0 toont de onversleutelde innerlijke pakketten, terwijl tcpdump -ni eth0 udp port 51820 de versleutelde buitenste pakketten laat zien; door beide te vergelijken ziet u direct welke richting defect is. netfilter en traffic shaping behandelen wg0 als elke andere link. Waar de kernelmodule niet beschikbaar is, zoals bij container-virtualisatie die de kernel van de host deelt, implementeert wireguard-go hetzelfde protocol in userspace via een TUN-device, wat ten koste gaat van de doorvoersnelheid omdat elk pakket de kernelgrens twee keer moet passeren.
Waar WireGuard u niet tegen beschermt
Het dreigingsmodel is bewust beperkt gehouden, en een protocol dat zo stil is, nodigt uit tot wensdenken. Hieronder volgen de feiten.
- Het verbergt niet dat u WireGuard gebruikt. Handshake-berichten hebben vaste groottes, de eerste byte geeft het berichttype aan en het transport verloopt via UDP. Deep packet inspection herkent dit eenvoudig, en een netwerk dat VPN's niet toestaat, kan het verkeer blokkeren. Obfuscatie is bewust weggelaten.
- Het verbergt het volume of de timing van het verkeer niet. Payloads worden alleen opgevuld tot een grens van 16 bytes, waardoor een waarnemer nog steeds ziet wanneer u gegevens verstuurt en ongeveer hoeveel.
- Het houdt het laatst bekende eindpunt bij. De peer met het publieke adres slaat het huidige publieke IP-adres van de andere kant op, en
wg showgeeft dit weer. Samen met een tunneladres dat vaststaat in de configuratie, vormt dit een stabiele identificator die een gebruiker volgt tussen netwerken. Op uw eigen VPS is dit geen probleem. Dit is ook de reden waarom commerciële diensten een extra laag boven op het protocol toevoegen. - Het authenticeert een sleutel, geen persoon. Wie het private key-bestand bezit, is de peer. Houd
/etc/wireguardop mode 700 en de sleutelbestanden op 600. - Er is geen lijst met ingetrokken sleutels en geen verloopdatum. De toegang eindigt pas wanneer u de peer-vermelding verwijdert van elke server die deze bevat, en statische sleutels blijven geldig totdat u ze verwijdert.
Niets hiervan maakt WireGuard zwak. Het maakt het compact, en compactheid is het doel: het authenticeert en versleutelt, en het laat identiteitsbeheer en adresallocatie over aan wat u er zelf bovenop bouwt. Een coördinatielaag zoals beschreven in WireGuard vergeleken met Tailscale bestaat om precies dat gat te dichten, gebruikmakend van hetzelfde datavlak waar u zojuist over heeft gelezen. Zodra een dergelijke laag aanwezig is, is de volgende beslissing wie een service mag bereiken die u binnen de tunnel draait, wat in de keuze tussen Tailscale serve en funnel wordt bepaald voor een enkele poort.
FAQ
Wat is cryptokey routing in WireGuard?
Cryptokey routing is de regel die elk pakket koppelt aan een publieke sleutel. Elke peer-vermelding bevat een lijst met prefixen in AllowedIPs. Bij uitgaand verkeer kiest WireGuard de peer door het bestemmingsadres van het pakket te vergelijken met de lijst van elke peer, waarbij de langste prefix voorrang krijgt; de lijst fungeert dus als routeringstabel. Bij inkomend verkeer moet het bronadres van het ontsleutelde en geauthenticeerde pakket binnen diezelfde lijst vallen, anders wordt het pakket genegeerd; de lijst fungeert dus als toegangscontrolelijst. WireGuard heeft geen afzonderlijke routeringsconfiguratie of interne firewall, omdat deze ene lijst beide taken uitvoert.
Heb ik PersistentKeepalive nodig op beide peers?
Nee. Configureer dit aan de zijde die zich achter NAT (network address translation) of een stateful firewall bevindt, meestal de client. WireGuard verstuurt niets wanneer het inactief is, waardoor de mapping die de andere zijde nodig heeft om de peer te bereiken vaak binnen een minuut verloopt; de tunnel lijkt dan in één richting dood. PersistentKeepalive = 25 verstuurt elke 25 seconden een leeg geauthenticeerd pakket om de mapping open te houden. Een peer met een publiek adres en een open UDP-poort heeft dit niet nodig.
Waarom geeft ping over de tunnel de melding "Required key not available"?
Omdat het bestemmingsadres niet voorkomt in de AllowedIPs van een peer. Cryptokey routing vond daardoor geen sleutel om het pakket mee te versleutelen en de kernel weigerde het pakket te verzenden. Voer wg show wg0 allowed-ips uit en vergelijk de uitvoer met het adres dat u probeert te pingen. De vergelijkbare foutmelding Destination address required duidt op een ander probleem: er is wel een peer gevonden, maar WireGuard heeft geen endpoint voor deze peer omdat er geen is geconfigureerd en er nog geen geauthenticeerd pakket van die peer is ontvangen.
Kan een firewall WireGuard detecteren en blokkeren?
Ja. WireGuard authenticeert en versleutelt uw verkeer, maar doet geen poging om zichzelf te verhullen. Handshake-berichten hebben een vaste lengte van 148 en 92 bytes, de eerste byte van elk bericht identificeert het type, en het transportprotocol is UDP. Deep packet inspection herkent het protocol daarom zonder problemen. Netwerken die UDP blokkeren of protocollen herkennen aan hun vingerafdruk, zullen het verkeer stoppen. Het verbergen van de tunnel vereist het inpakken van het verkeer in een ander protocol; dit is een taak voor externe tools en geen instelling binnen WireGuard.