WireGuard is a layer 3 VPN protocol that carries encrypted IP packets inside UDP. Peers authenticate with static Curve25519 public keys through a one round trip Noise_IKpsk2 handshake, then encrypt traffic with ChaCha20Poly1305. Each peer's key is bound to a list of allowed tunnel addresses that works as both routing table and access list.
There is no RFC for WireGuard. The protocol is defined by its author's whitepaper and the wireguard.com protocol page, with the handshake taken from the Noise Protocol Framework: one fixed design, one cipher suite, four message types and no negotiation. This post covers the internals from those primary documents. For installation or a product comparison, see our posts on setting up a VPN with WireGuard, WireGuard vs OpenVPN and how a VPN works, one packet at a time.
Key takeaways
- WireGuard is defined by the whitepaper "WireGuard: Next Generation Kernel Network Tunnel" (draft revision e2da747, 1 June 2020; a version appeared at NDSS 2017) and the wireguard.com protocol page. The handshake follows the Noise Protocol Framework, revision 34, 11 July 2018.
- The construction is
Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s. There is no cipher or version negotiation; if a primitive breaks, every endpoint must update. - There are four messages: handshake initiation (type 1), response (type 2), cookie reply (type 3) and transport data (type 4), all over UDP. There is no TCP mode.
- AllowedIPs choose which peer encrypts an outbound packet, and drop any inbound packet whose inner source address does not map back to the peer that sent it.
- Peers roam: the endpoint follows the outer source address of the latest authenticated packet. An active attacker can redirect it but cannot read or alter the payload.
- Sessions rekey about every 120 seconds, are refused after 180 seconds, and all keys are wiped after 540 seconds without a new handshake. IP bound cookies resist handshake floods.
- Documented limits: identity hiding without forward secrecy, no post-quantum secrecy without a pre-shared key, dependence on a monotonic clock, no obfuscation, and no user authentication, address assignment or logging.
What WireGuard is for, where it sits and which documents define it
WireGuard gives two machines a private, authenticated path for IP packets across an untrusted network, with as little configuration and code as possible. The whitepaper describes a secure network tunnel operating at layer 3, implemented as a Linux kernel virtual interface, and positions it as a replacement for IPsec in most uses and for user space VPNs built on TLS.
It carries IPv4 and IPv6 packets (including v4 in v6 and v6 in v4), but on the wire it is a UDP application. Inside the host it is a virtual interface, conventionally wg0, managed with ordinary ip commands. The whitepaper argues that this deliberate layering violation, combining key exchange and transport encryption behind a normal interface, is what lets firewall rules trust the source address of any packet arriving on wg0.
| Document | What it defines | Date or version |
|---|---|---|
| WireGuard whitepaper | Full protocol, cryptography, timers, Linux design | Draft revision e2da747, 1 June 2020 |
| wireguard.com/protocol | Primitives, packet structures, timers | Page copyright 2015 to 2022 |
| Noise Protocol Framework | The IK pattern and psk modifiers | Revision 34, 11 July 2018 |
| RFC 7539 (May 2015), obsoleted by RFC 8439 (June 2018) | ChaCha20 and Poly1305 AEAD | Informational |
| RFC 7693 (November 2015) and RFC 5869 (May 2010) | BLAKE2 and HKDF | Informational |
| wg(8) and wg-quick(8) | Configuration tools and file format | wireguard-tools |
With no standards body document, nothing obsoletes anything in the RFC sense. Every handshake hashes in the fixed identifier WireGuard v1 zx2c4 Jason@zx2c4.com, so a future version would simply be a different, incompatible protocol.
Ports. WireGuard has no IANA assigned port. The IANA service name and port registry, searched on 23 October 2026, has no entry for WireGuard or for 51820, which is just the value in the man page examples. If ListenPort is unset, wg(8) says the port "will be chosen randomly when the interface comes up". For comparison, IKE and IPsec NAT traversal are registered on UDP 500 and 4500, and OpenVPN on 1194.
Why WireGuard is small: fixed crypto, no negotiation, UDP only
The whitepaper puts the Linux implementation at under 4,000 lines, excluding cryptographic primitives. Each design choice removes a category of code:
- Fixed cryptography. The whitepaper says WireGuard deliberately has no cipher or protocol agility. One suite: Curve25519, ChaCha20Poly1305, XChaCha20Poly1305 for cookies, BLAKE2s, HKDF and SipHash24 for hashtable keys. No cipher lists means no downgrade path.
- No certificates. Peers are 32 byte public keys, 44 Base64 characters. No X.509 or ASN.1 parsing, which the whitepaper notes has a history of kernel vulnerabilities.
- Key distribution is out of scope. Like SSH, each side needs the other's public key in advance. The wireguard.com overview places "key distribution and pushed configurations" outside WireGuard.
- UDP only. The known limitations page rules out TCP tunnelling because TCP inside TCP performs poorly; wrapping is a job for a separate layer.
- Layer 3 only. There is always an IP source address to check against the peer's allowed list.
- Silence. No state and no reply for unauthenticated packets, so scanners get nothing back.
- Timers, not connections. You configure keys and send packets; handshakes, rekeying and expiry run from a small timer state machine.
How WireGuard works on the wire
Message types and header layout
Every message is a UDP payload starting with a one byte type and three reserved zero bytes. Sizes are computed from the field lengths on the wireguard.com protocol page; each AEAD output adds a 16 byte Poly1305 tag.
| Type | Message | Fields in bytes | Total |
|---|---|---|---|
| 1 | Handshake initiation | type 1, reserved 3, sender index 4, unencrypted ephemeral 32, encrypted static 48, encrypted timestamp 28, mac1 16, mac2 16 | 148 |
| 2 | Handshake response | type 1, reserved 3, sender index 4, receiver index 4, unencrypted ephemeral 32, encrypted nothing 16, mac1 16, mac2 16 | 92 |
| 3 | Cookie reply | type 1, reserved 3, receiver index 4, nonce 24, encrypted cookie 32 | 64 |
| 4 | Transport data | type 1, reserved 3, receiver index 4, counter 8, encrypted padded packet plus 16 | 32 plus payload |
- Indices identify sessions. The 32 bit indices work like an IPsec SPI, so the receiver finds the right keys without trial decryption.
- The counter is the nonce for ChaCha20Poly1305 and the replay protection value.
- No length field. The tag decides authenticity and the inner IP header carries its own length.
- Padding. The inner packet is zero padded to a multiple of 16 bytes to blunt traffic analysis, without exceeding the MTU.
- DSCP. Handshake packets are marked 0x88 (AF41); data packets carry DSCP 0 so the inner value never leaks, while ECN bits are copied per RFC 6040.
The Linux driver's default MTU is 1500 minus 80 bytes of worst case overhead (32 bytes of WireGuard header and tag, 8 of UDP, 40 of IPv6), giving 1420, as drivers/net/wireguard/device.c shows. wg-quick(8) instead derives the MTU from the endpoint or default route unless MTU is set.
The flow, as a sequence
Initiator (I) Responder (R)
| |
| first packet queued for R, no session yet |
| |
| ---- Type 1: Handshake initiation (148 bytes) -----> | checks mac1 (and mac2 if under
| ephemeral pub, enc(I static pub), enc(TAI64N) | load), decrypts I's static key,
| | checks timestamp is newer
| |
| <--- Type 2: Handshake response (92 bytes) --------- | R derives keys but may not send
| ephemeral pub, enc(empty), psk mixed in | data until it hears from I
| |
| ---- Type 4: Transport data (counter 0) -----------> | key confirmation: R promotes
| queued packet, or empty keepalive | the new session
| |
| <=== Type 4 data in both directions ===============> |
| |
| about 120 s later, initiator starts a new handshake |
| |
| (if R is under load and mac2 is missing or invalid) |
| <--- Type 3: Cookie reply (64 bytes) --------------- | I stores cookie, retries after
| | Rekey-Timeout with valid mac2
The send and receive path
Per section 3 of the whitepaper: on send, the destination address is looked up in the cryptokey routing table. No match means drop plus ICMP "no route to host". Otherwise the packet is encrypted with that peer's sending key and counter, the header is prepended and it goes out as UDP to the peer's endpoint.
On receive, the index selects the session, the counter is checked, and the packet is authenticated and decrypted; failures are dropped silently. Only then is the peer's endpoint updated from the outer source address. Finally the inner packet must be IP and its source address must map, in the same table, to the same peer, or it is dropped.
The Noise_IKpsk2 handshake, message by message
What the name means
In Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s, I means the initiator's static key is sent Immediately (encrypted) in message 1, K means the responder's static key is Known in advance, and psk2 puts a pre-shared key token at the end of message 2. The Noise specification writes the pattern as:
IKpsk2:
<- s
...
-> e, es, s, ss
<- e, ee, se, psk
The line before the ellipsis is a pre-message: the initiator already holds the responder's static key. e sends a fresh ephemeral public key; es, ss, ee and se are Diffie Hellman operations between ephemeral and static keys, each mixed into a running chaining key; psk mixes in the pre-shared key. Without a configured PSK, WireGuard uses 32 zero bytes, so the computation is identical. WireGuard adds the 4 byte indices, an encrypted TAI64N timestamp and the mac1 and mac2 fields.
Message 1: initiator to responder
Starting from a hash of the construction and identifier strings and the responder's static key, the initiator:
- Generates an ephemeral key pair and sends the public half in the clear.
- Computes
es(its ephemeral with the responder's static key) and derives a key. - Encrypts its own static public key under it, hiding its identity from passive observers.
- Computes
ss(both static keys) and derives another key. - Encrypts a 12 byte TAI64N timestamp under that key.
- Adds mac1, keyed from the responder's public key, and mac2 from a cookie if it holds a fresh one, otherwise zeros.
The responder reverses these steps. A bad mac1, an unknown static key or a timestamp not greater than the last one seen from that peer means a silent drop. The timestamp exists because message 1 already authenticates the initiator and could otherwise be replayed to force a new ephemeral key and break the live session. It only needs to be a per peer increasing 96 bit value, and the whitepaper allows truncating 24 bits of nanoseconds if precision leaks too much.
Message 2: responder to initiator
The responder sends its own ephemeral key, computes ee and se, mixes in the PSK, and sends an AEAD encryption of an empty payload: a 16 byte tag proving it holds the right keys. Its mac1 is keyed to the initiator's public key. The ee step gives forward secrecy: once both ephemeral private keys are wiped, recorded traffic stays sealed even if both static keys later leak.
Key confirmation and transport keys
Both sides then run HKDF on the final chaining key to get one 32 byte key per direction, with counters at zero, and zero all handshake state. The initiator may send data at once; the responder must wait for the first data packet under the new session, which is the key confirmation. If the initiator has nothing to send it sends an empty keepalive. Until then the responder keeps using the previous session or queues packets.
What Noise says about IK's properties
| Payload or key | Noise grade | Meaning |
|---|---|---|
| Message 1, authentication | Source 1 | Rests on static to static DH, so open to key compromise impersonation if the recipient's key leaks |
| Message 1, confidentiality | Destination 2 | Encrypted to a known recipient, replayable, forward secret only against sender compromise |
| Message 2 | Source 2, destination 4 | Resists key compromise impersonation; weak forward secrecy |
| Transport data | Source 2, destination 5 | Strong forward secrecy |
| Initiator identity | 4 | Encrypted to the responder's static key without forward secrecy |
| Responder identity | 3 | Not sent, but a passive attacker can test guesses of the responder's private key |
WireGuard carries no application data in the handshake, and the whitepaper and formal analyses treat the full protocol as resistant to key compromise impersonation even though Noise grades the IK first message as vulnerable. Section 5.1 of the whitepaper explains what remains: someone with a compromised static key could forge an initiation with the maximum timestamp, which never completes a handshake but blocks future connections with that key. The author argues this actually helps force the compromised key out of service.
Cryptokey routing: how AllowedIPs act as routing table and ACL
The whitepaper defines a secure VPN as "an association between peers and the IP addresses each is allowed to use as source IPs." Each peer has one public key and a list of AllowedIPs, consulted in both directions.
| Direction | Question asked of AllowedIPs | Result |
|---|---|---|
| Outbound | Which peer's prefix contains this destination? | Encrypt for that peer; no match means drop |
| Inbound | After decryption, which peer would the table pick for this inner source, and is it the sender? | Accept only if it is the same peer |
The inbound check asks which peer the table would choose for that source, not merely whether the source is in the sender's list. The whitepaper says this enforces a one to one mapping, so replies go back to the same peer, much like reverse path filtering. The Linux implementation stores the table in a radix trie with lock free lookups because every packet consults it.
Three consequences follow. AllowedIPs is an access control list, so a peer with 10.10.10.230/32 can only inject packets claiming that address, and your firewall can treat wg0 source addresses as authenticated identities. A server peer entry with 0.0.0.0/0 lets that client claim any source inside the tunnel. And wg-quick(8) turns every peer's AllowedIPs into system routes, using policy routing rules for a default route, so widening AllowedIPs widens what you send as well as what you accept. Its Table option turns route creation off.
Roaming and endpoints
A peer's endpoint (IP and UDP port) is optional. Whenever a packet from that peer authenticates, WireGuard updates the endpoint to the outer source address. The whitepaper compares this to Mosh: a laptop moves from Wi-Fi to mobile data and the tunnel follows with no reconnection. The listen port and send port are always the same, which helps with NAT, and because the endpoint tracks the latest authenticated packet, NAT mappings need not live long. A peer behind NAT that must stay reachable while idle can set PersistentKeepalive; the quickstart suggests 25 seconds and notes the option is off by default and unnecessary for most users.
The known limitations page names the downside "roaming mischief". Roaming needs no extra round trip, so an active man in the middle can rewrite outer source addresses and redirect the endpoint, and might keep relaying after losing that position. The packets stay unreadable and tamper evident, so this is redirection and denial of service, not a confidentiality break. The documented mitigation is firewalling the WireGuard socket to an expected address.
Cookies, timers and key rotation
Two MACs and the cookie reply
Checking a handshake costs Curve25519 operations, so a flood of fake initiations could exhaust CPU. Section 5.3 of the whitepaper answers with two MACs.
mac1 is always required and is keyed from the recipient's public key. A sender must know whom it is addressing before provoking any work, which keeps the server silent toward scanners. Because the key is derived from a public value, an observer can test whether a packet targets a known public key, which the whitepaper says slightly weakens identity hiding.
mac2 matters only under load. If a message has a valid mac1 but a missing or invalid mac2 while the server is busy, the server may send a type 3 cookie reply instead of doing DH work. The cookie is a MAC of the sender's IP address and port under a secret that changes every two minutes, which proves the sender can receive at that address and lets rate limiting work per source. The Linux source sets INITIATIONS_PER_SECOND to 50 for its handshake rate limiter.
The whitepaper says this fixes three flaws in older cookie schemes: replies go only to senders with a valid mac1, so silence holds; the cookie is encrypted with XChaCha20Poly1305 under a key derived from the responder's public key, so it never travels in clear; and the reply is bound to the triggering message's mac1 as authenticated data, so an off path attacker cannot forge cookie replies. The cookie reply is smaller than either handshake message, so it cannot amplify, and a recipient stores the cookie and waits for its normal retry timer rather than resending immediately.
The timer constants
Section 6.1 of the whitepaper and the Linux messages.h agree on these values:
| Constant | Value | Controls |
|---|---|---|
| Rekey-After-Messages | 2^60 messages | New handshake after this many messages on one key |
| Reject-After-Messages | 2^64 minus 2^13 minus 1 (whitepaper) | Key never used beyond this |
| Rekey-After-Time | 120 seconds | Initiator rekeys once the session is this old |
| Reject-After-Time | 180 seconds | Session refused after this age |
| Rekey-Attempt-Time | 90 seconds | Stop retrying a handshake after this long |
| Rekey-Timeout | 5 seconds | Retry interval; never initiate more often |
| Keepalive-Timeout | 10 seconds | Empty keepalive if data arrived and nothing went back |
How they combine, per the protocol page and section 6:
- An unanswered initiation is retried after Rekey-Timeout plus 0 to 333 ms of jitter, with fresh ephemeral keys, for up to 90 seconds.
- Only the current initiator rekeys on time, which avoids both sides starting at once. A receive only initiator also rekeys when the session reaches 165 seconds (180 minus 10 minus 5).
- Data received with nothing sent back for 10 seconds triggers a keepalive. Data sent with nothing heard for 15 seconds triggers a new handshake. That is how a dead peer is detected without any disconnect message.
- Current, previous and unconfirmed next sessions are kept; the oldest is zeroed when a new one arrives. After three times Reject-After-Time, 540 seconds, without a new session, all keys and partial handshake state are wiped.
During active traffic keys rotate about every two minutes; an idle tunnel sends nothing.
Replay protection for data packets
The 64 bit counter only increases, so nonces are never reused. UDP reorders packets, so receivers keep a sliding window of seen counters, checked only after authentication. The protocol page describes the window as "roughly 2000 prior values"; the whitepaper cites RFC 2401 appendix C and RFC 6479; the current Linux source uses an 8,192 bit bitmap, a window of 8,128 counters on 64 bit systems. The size is an implementation choice; accepting each counter at most once is the protocol guarantee.
Versions and history
| Date | Event | Source |
|---|---|---|
| 2015 | Project copyright begins; the wg(8) man page header is dated 13 August 2015 | wireguard.com, wg(8) |
| 2017 | A version of the whitepaper appears at NDSS 2017 | Whitepaper |
| 11 July 2018 | Noise Protocol Framework revision 34 | noiseprotocol.org |
| 30 March 2020 | WireGuard 1.0.0 announced as shipping in Linux 5.6, built in from then on | WireGuard mailing list |
| 1 June 2020 | Whitepaper draft revision e2da747, the current static version | Whitepaper |
| Today | CONFIG_WIREGUARD in mainline Linux; implementations for Windows, macOS, BSD, iOS and Android | Kernel Kconfig, wireguard.com |
The wire protocol is still "WireGuard v1". There is no second version, nothing deprecated and nothing to disable. Kernels before 5.6 used backports or a compatibility module, which the install page still describes. A 2020 kernel and a 2026 phone app speak the same protocol.
The wireguard.com verification page lists a Tamarin symbolic proof (key agreement, authenticity, key compromise impersonation resistance, unknown key share resistance, key secrecy, forward secrecy, session uniqueness, identity hiding), an eCK model proof of a closely related variant, a CryptoVerif proof in an ACCE style model including transport messages, Noise Explorer ProVerif models of IK, and verified Curve25519 code from HACL* and Fiat-Crypto.
Security properties and known weakness classes
| Property | Provided? | How or why not |
|---|---|---|
| Confidentiality | Yes | ChaCha20Poly1305 with per session keys |
| Integrity and origin authentication | Yes | AEAD tag on every message; inner source checked against AllowedIPs |
| Mutual authentication | Yes, by static key | No usernames, passwords or second factor |
| Forward secrecy of data | Yes | Ephemeral ee DH; keys rotated and wiped |
| Replay protection | Yes | Counter window for data; timestamp for initiations |
| Identity hiding | Partial | Initiator key encrypted without forward secrecy; mac1 allows guess testing |
| Post-quantum secrecy | Only with a PSK | Curve25519 alone is not post-quantum |
| Hiding VPN use | No | Fixed message types and sizes; obfuscation out of scope |
| Availability | Partial | Cookies and rate limits help; an on path attacker can always drop packets |
| Address assignment, user identity, logging | No | Out of scope by design |
Identity hiding without forward secrecy. The known limitations page explains that if a responder's private key is ever compromised, old handshake captures reveal who sent them, though not the data that followed. Rotating keys is the documented mitigation where unlinkability matters.
No cipher agility. Nothing to negotiate means no downgrade and no weak suite left on by accident. The trade-off is that a break in any primitive requires every endpoint to update together.
Post-quantum exposure. The concern is recording traffic now to decrypt later. A 256 bit PSK mixed into the handshake keeps recorded traffic safe while that key stays secret. The limitations page suggests running a post-quantum key exchange on top and feeding its output into the PSK slot.
Clock dependence. System time acts as the monotonic counter. The limitations page warns that a forward jump can lock a host out of its own keys, a backward jump blocks handshakes, and an adversary controlling time could store an initiation for later.
Handshake floods. Before mac2 kicks in, ECDH work can use considerable CPU, though the project says mac2 is usually sufficient. Linux handles handshakes on low priority workers so floods do not starve data.
Fingerprinting. WireGuard does not try to look like anything else, so networks can identify and block it.
No user layer. No DHCP equivalent, no pushed routes or DNS, no usernames, no connection log, no revocation list. Address and DNS in a config file are wg-quick features, not protocol messages. Removing a peer's key is revocation.
Routing loops and leaks. The limitations page notes open issues detecting routing loops. wg-quick(8) shows an opt in kill switch using an iptables rule and fwmark, and notes most DHCP traffic still passes because it bypasses Netfilter.
Key handling. A leaked private key is full impersonation of that peer. wg(8) takes keys only from files because command line arguments are not private. The whitepaper warns that sharing one private key across peers lets a handshake to one be replayed to the other, making the initiator roam involuntarily.
Secure configuration with defender-side checks
These commands come from wg(8), wg-quick(8), the wireguard.com quickstart and ss(8). Only run them against systems you own or are authorised to test. Some print secrets, as noted.
Generate and store keys
umask 077
wg genkey | tee private.key | wg pubkey > public.key
wg genpsk > peer.psk # optional pre-shared key per peer pair
wg-quick(8) also shows loading a private key from an encrypted store at startup:
PreUp = wg set %i private-key <(pass WireGuard/private-keys/%i)
Inspect the live tunnel
wg show prints all interfaces readably and hides private and preshared keys unless WG_HIDE_KEYS=never is set. For audits, ask for single fields, which are tab separated and safe to log:
wg show wg0
wg show wg0 listen-port
wg show wg0 endpoints
wg show wg0 allowed-ips
wg show wg0 latest-handshakes
wg show wg0 transfer
wg show wg0 persistent-keepalive
Look for peers that should be active but have stale handshakes (check keys, reachability and clock), endpoints you do not expect for a fixed site peer, and traffic to peers you thought were retired. Never paste wg show wg0 dump (private key on line one, preshared keys after), wg show wg0 preshared-keys or wg showconf wg0 (includes PrivateKey) into logs or tickets.
Confirm AllowedIPs scope
wg show wg0 allowed-ips | grep -E '0\.0\.0\.0/0|::/0'
On a server this should print nothing; on a full tunnel client, exactly one line for the gateway. wg(8) supports incremental edits, where a leading minus removes a prefix, and syncconf applies a file without dropping live sessions; wg-quick strip removes wg-quick only keys first:
wg set wg0 peer <base64-public-key> allowed-ips -0.0.0.0/0
wg syncconf wg0 <(wg-quick strip wg0)
Check the listener
ss -ulpn
Match the result against wg show wg0 listen-port. Set ListenPort explicitly on servers so firewall rules stay stable, and where a peer has a fixed address, restrict inbound UDP to it, the mitigation the limitations page gives for endpoint redirection.
Review wg-quick keys and debug safely
In /etc/wireguard/INTERFACE.conf: SaveConfig = true writes live state back on shutdown, overwriting manual edits and persisting runtime peers without review, so prefer false on managed hosts. PreUp, PostUp, PreDown and PostDown run through bash as root, so keep the file root owned and review it like a script. Table = off hands routing to another system. DNS calls resolvconf.
When handshakes fail, the quickstart gives the kernel dynamic debug switch; user space implementations use LOG_LEVEL=verbose. Turn it off afterwards.
modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
Hardening checklist
| Setting | Recommendation | Why |
|---|---|---|
| Private key files | Owner only, created under umask 077 | Key equals identity |
| Key reuse | One key pair per device | Shared keys break replay protection |
| PresharedKey | Unique per peer pair where long term confidentiality matters | Post-quantum resistance for recorded traffic |
| AllowedIPs on servers | Narrow /32 and /128 per client | AllowedIPs is the ACL |
| ListenPort | Fixed and firewalled to expected sources | Stable rules; limits endpoint redirection |
| PersistentKeepalive | Only for NAT peers that must receive while idle | Off by default |
| Time sync | Reliable, trusted source | Handshake timestamps depend on it |
| SaveConfig | false on managed servers | No silent persistence |
When to use WireGuard versus IPsec and OpenVPN
| Need | WireGuard | IPsec with IKEv2 (RFC 7296, October 2014; ESP RFC 4303, December 2005) | OpenVPN |
|---|---|---|---|
| Transport | UDP only, no IANA port | IKE on UDP 500, NAT traversal on UDP 4500, ESP for data | UDP or TCP (--proto), IANA port 1194 |
| Crypto choice | One fixed suite | Negotiated suites; strength depends on configuration | TLS control channel with configurable ciphers |
| Authentication | Static public keys only | Certificates, pre-shared keys, EAP | TLS certificates, optional username and password |
| Address assignment and user identity | Not in protocol | Configuration payloads such as INTERNAL_IP4_ADDRESS | --push, --ifconfig-pool, --auth-user-pass-verify |
| Works over TCP | No | Not natively | Yes |
| Roaming | Built in | Through the MOBIKE extension referenced by RFC 7296 | --float lets an authenticated peer change address |
| Interoperability | Growing | Widest, standards based | OpenVPN on both ends |
| Audit surface | Small, formally analysed | Large, layered | Large, TLS stack in user space |
| Operational cost | Low per tunnel; key distribution and identity are yours to build | Higher configuration effort, mature tooling | Moderate; familiar PKI and user auth |
Choose WireGuard for site to site links, server meshes and device tunnels where you control both ends and can distribute keys. Choose IPsec to interoperate with standards based gateways or for IKEv2 features such as EAP. Choose OpenVPN for TCP fallback through restrictive networks or push based client setup.
Where Safeguard fits
Safeguard does not run, inspect or filter VPN traffic, and nothing above depends on it. The honest connection is software inventory. WireGuard reaches your estate as the kernel driver, as user space implementations such as wireguard-go (which the quickstart mentions for non-Linux systems), and as libraries embedded in agents and container images. Safeguard's software composition analysis (generally available) builds a full transitive dependency inventory of repositories and images, and SBOM generation and analysis in SPDX and CycloneDX (generally available) answers "which images ship which WireGuard implementation, at which version" when an advisory lands. Vulnerability management with EPSS and KEV enrichment and reachability analysis, both generally available, help order the fixes. The host kernel outside your images is not covered; kernel fixes come from your distribution. See using SBOMs to find affected products fast, and TCP vs UDP: what an eavesdropper can see and change for the UDP layer underneath.
Common mistakes
- Treating AllowedIPs as routes only. It is the inbound ACL too;
0.0.0.0/0on a server peer lets that client claim any source address. - Giving two peers the same range. The table maps each address to one peer, so the other fails in ways that look like a network fault.
- Copying one private key to several devices. It breaks timestamp replay protection and makes peers flap between endpoints.
- Pasting
wg show dumporwg showconfinto tickets. Both contain private key material. - Assuming the tunnel logs who connected. Handshake times and byte counters are not an audit trail.
- Expecting a TCP fallback. There is none.
- Leaving ListenPort unset on servers. It changes on every restart and firewall rules drift.
- Enabling PersistentKeepalive everywhere. The quickstart notes it makes WireGuard slightly more chatty; an idle tunnel is otherwise silent.
- Ignoring time. A badly wrong clock can lock a device out until it passes the last accepted timestamp.
- Believing WireGuard hides VPN use. It protects content, not the fact of the tunnel.
FAQ
What port does WireGuard use?
Whatever you configure, always UDP. There is no IANA assigned port; 51820 is the value in the official examples. If ListenPort is unset, a random port is chosen when the interface comes up.
Is WireGuard encrypted?
Yes. Tunnelled packets are encrypted and authenticated with ChaCha20Poly1305 using keys from a Curve25519 handshake, rotated about every two minutes with forward secrecy. Outer IP and UDP headers, message type, receiver index and counter stay visible.
Does WireGuard use TCP or UDP?
UDP only. The project rules out TCP tunnelling because TCP inside TCP performs poorly; separate tools can wrap it.
What is the Noise_IKpsk2 handshake?
The Noise IK pattern with a pre-shared key mixed in at the end of the second message. The initiator already knows the responder's static key and sends its own encrypted in message 1; two messages give mutual authentication and fresh session keys.
What are AllowedIPs in WireGuard?
The address prefixes bound to a peer's public key. They choose which peer an outgoing packet is encrypted for, and reject any decrypted packet whose inner source does not belong to the sending peer.
How often does WireGuard rotate keys?
The initiator rehandshakes when a session is 120 seconds old and traffic is flowing. No session is used past 180 seconds, and after 540 seconds without a new one all keys are erased.
Is WireGuard quantum safe?
Not by default. A unique PresharedKey per peer pair adds a symmetric layer that protects recorded traffic against a future quantum attacker while that key stays secret.
Does WireGuard log connections or authenticate users?
No. Peers are identified only by public key and there is no connection log. User identity, address assignment and audit logging must be built around it.
WireGuard vs IPsec: which is more secure?
Both can be secure. WireGuard's fixed, formally analysed design removes misconfigured suites and downgrade paths. IPsec offers wider interoperability and more authentication options, but its security depends on how each side is configured.
Fix it without waiting for upstream
WireGuard's protocol has no options to get wrong, so most real exposure sits in implementations: the user space WireGuard libraries, VPN agents and container images your teams build and ship, and the crypto and networking libraries they depend on.
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
These demos show dependency, image and SBOM workflows, not VPN monitoring. You can download Safeguard and follow the CLI docs to try them on your own projects.
This demo sets up the Safeguard runner on your own machine, approves it with its device code and then scans from the terminal. Pointed at a container image for a VPN gateway or an agent that bundles a user space WireGuard implementation, the same scan lists the components inside it with their versions and any known vulnerabilities.
Click through it at your own pace, or open it full screen.
This demo is a real CLI session: install, open the interactive terminal, ask it to fix the lodash CVE in a repository, and watch it read the finding and advisory, ask before each command, move lodash to 4.17.21, run npm install and npm test with approval, and print a summary without pushing anything. The same approve each step flow applies when the dependency to bump is a WireGuard library or a crypto package pinned in your manifest.
Click through it at your own pace, or open it full screen.
This demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. Once connected, you can ask in plain language which of your projects or images include a given WireGuard implementation version, and get the answer from your own scan data.
Click through it at your own pace, or open it full screen.
Quick check
A server peer for a single laptop has AllowedIPs set to 0.0.0.0/0. What can go wrong?
AllowedIPs is the inbound access list as well as the routing table. With 0.0.0.0/0, the laptop can send packets claiming any source address inside the tunnel, and the server will route every destination not matched more specifically to that one laptop. A client peer on a server should normally have only its own /32 and /128.
An attacker records WireGuard handshakes today and steals the server's private key next year. What can they learn?
They can decrypt the initiator's static public key from old initiation messages and so learn which peers connected, because identity hiding in the IK pattern has no forward secrecy. They cannot decrypt the tunnelled data, because session keys came from ephemeral Diffie Hellman values that were wiped after use.
Sources
- WireGuard: Next Generation Kernel Network Tunnel, whitepaper, draft revision e2da747, 1 June 2020
- WireGuard: Protocol and Cryptography
- WireGuard: Known Limitations
- WireGuard: Formal Verification
- WireGuard: Quick Start
- WireGuard: Installation
- WireGuard home page and conceptual overview
- The Noise Protocol Framework, revision 34, 11 July 2018
- wg(8) man page, wireguard-tools
- wg-quick(8) man page, wireguard-tools
- Linux kernel source: drivers/net/wireguard/messages.h
- Linux kernel source: drivers/net/wireguard/device.c
- Linux kernel source: drivers/net/Kconfig
- WireGuard mailing list: WireGuard 1.0.0 for Linux 5.6 Released, 30 March 2020
- IANA Service Name and Transport Protocol Port Number Registry
- RFC 7296: Internet Key Exchange Protocol Version 2 (IKEv2), October 2014
- RFC 4303: IP Encapsulating Security Payload (ESP), December 2005
- RFC 7539: ChaCha20 and Poly1305 for IETF Protocols, May 2015
- RFC 8439: ChaCha20 and Poly1305 for IETF Protocols, June 2018
- RFC 7693: The BLAKE2 Cryptographic Hash and Message Authentication Code, November 2015
- RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF), May 2010
- ss(8) Linux manual page
- OpenVPN 2.6 reference manual
Last reviewed: 23 October 2026. WireGuard implementations and their defaults evolve; check the wg(8) and wg-quick(8) man pages shipped with your version.