WireGuard is an open-source VPN solution written in C by Jason Donenfeld and others, aiming to fix many of the problems that have plagued other modern server-to-server VPN offerings like IPSec/IKEv2, OpenVPN, or L2TP. It shares some similarities with other modern VPN offerings like Tinc and MeshBird, namely good cipher suites and minimal config. As of 2020-01 itâs been merged into the 5.6 version of the Linux kernel, meaning it will ship with most Linux systems out-of-the-box.
Official Links
wg
, wg-quick
WireGuard Goals
See https://github.com/pirate/wireguard-docs for example code and documentation source.
Whether living behind the Great Wall of China or just trying to form a network between your servers, WireGuard is a great option and serves as a âlego blockâ for building networks (much in the same way that ZFS is a lego block for building filesystems).
Things WireGuard does not do:
But you can write your own solutions for these problems using WireGuard under the hood (like Tailscale or AltheaNet).
These are demo hostnames, domain names, IP addresses, and ranges used in the documentation and example configs. Replace them with your preferred values when doing your own setup.
example-vpn.dev
can be replaced with any publicly accessible domain you controlpublic-server1
, public-server2
, home-server
, laptop
, phone
can be changed to your device hostnames192.0.2.1/24
, 192.0.2.3
, 192.0.2.3/32
, 2001:DB8::/64
can be replaced with your preferred subnets and addresses (e.g. 192.168.5.1/24
)Wherever you see these strings below, theyâre just being used as placeholder values to illustrate an example and have no special meaning.
Make sure to change the IP addresses in your configs! The blocks used in these docs are reserved for example purposes by the IETF and should never be used in real network setups.
You can use any private range you want for your own setups, e.g. 10.0.44.0/24
, just make sure
they donât conflict with any of the LAN subnet ranges your peers are on.
A host that connects to the VPN and registers a VPN subnet address such as 192.0.2.3
for itself. It can also optionally route traffic for more than its own address(es) by specifying subnet ranges in comma-separated CIDR notation.
A publicly reachable peer/node that serves as a fallback to relay traffic for other VPN peers behind NATs. A bounce server is not a special type of server, itâs a normal peer just like all the others, the only difference is that it has a public IP and has kernel-level IP forwarding turned on which allows it to bounce traffic back down the VPN to other clients.
See more: https://tailscale.com/blog/how-nat-traversal-works/ (Tailscale uses Wireguard under the hood)
A group of IPs separate from the public internet, e.g. 192.0.2.1-255 or 192.168.1.1/24. Generally behind a NAT provided by a router, e.g. in office internet LAN or a home Wi-Fi network.
A way of defining a subnet and its size with a âmaskâ, a smaller mask = more address bits usable by the subnet & more IPs in the range. Most common ones:
192.0.2.1/32
(a single IP address, 192.0.2.1
) netmask = 255.255.255.255
192.0.2.1/24
(255 IPs from 192.0.2.0
-192.0.2.255
) netmask = ` 255.255.255.0`192.0.2.1/16
(65,536 IPs from 192.0.0.0
- 192.0.255.255
) netmask = 255.255.0.0
192.0.2.1/8
(16,777,216 IPs from 192.0.0.0
- 192.255.255.255
) netmask = 255.0.0.0
0.0.0.1/0
(4,294,967,296 IPs from 0.0.0.0
- 255.255.255.255
) netmask = 0.0.0.0
2001:DB8::/64
https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
To people just getting started 192.0.2.1/32
may seem like a weird and confusing way to refer to a single IP. This design is nice though because it allows peers to expose multiple IPs if needed without needing multiple notations. Just know that anywhere you see something like 192.0.2.3/32
, it really just means 192.0.2.3
.
A subnet with private IPs provided by a router standing in front of them doing Network Address Translation, individual nodes are not publicly accessible from the internet, instead the router keeps track of outgoing connections and forwards responses to the correct internal IP (e.g. standard office networks, home Wi-Fi networks, free public Wi-Fi networks, etc)
The publicly accessible address:port for a node, e.g. 123.124.125.126:1234
or some.domain.tld:1234
(must be accessible via the public internet, generally canât be a private IP like 192.0.2.1
or 192.168.1.1
unless itâs directly accessible using that address by other peers on the same subnet).
A WireGuard private key for a single node, generated with:
wg genkey > example.key
(never leaves the node itâs generated on)
A WireGuard public key for a single node, generated with:
wg pubkey < example.key > example.key.pub
(shared with other peers)
Domain Name Server, used to resolve hostnames to IPs for VPN clients, instead of allowing DNS requests to leak outside the VPN and reveal traffic. Leaks are testable with http://dnsleak.com.
Public relays are just normal VPN peers that are able to act as an intermediate relay server between any VPN clients behind NATs, they can forward any VPN subnet traffic they receive to the correct peer at the system level (WireGuard doesnât care how this happens, itâs handled by the kernel net.ipv4.ip_forward = 1
and the iptables routing rules).
If all peers are publicly accessible, you donât have to worry about special treatment to make one of them a relay server, itâs only needed if you have any peers connecting from behind a NAT.
Each client only needs to define the publicly accessible servers/peers in its config, any traffic bound to other peers behind NATs will go to the catchall VPN subnet (e.g. 192.0.2.1/24
) in the public relays AllowedIPs
route and will be forwarded accordingly once it hits the relay server.
In summary: only direct connections between clients should be configured, any connections that need to be bounced should not be defined as peers, as they should head to the bounce server first and be routed from there back down the vpn to the correct client.
More complex topologies are definitely achievable, but these are the basic routing methods used in typical WireGuard setups:
Endpoint
addresses and ports so that WireGuard can connect straight to the open port and route UDP packets without intermediate hops.public-server2
), define the publicly accessible node with a hardcoded Endpoint
and the NAT-ed node without. The connection will be opened from NAT client -> public client, then traffic will route directly between them in both directions as long as the connection is kept alive by outgoing PersistentKeepalive
pings from the NAT-ed client.public-server1
, and traffic will forward through the intermediary bounce server as long as the connections are kept alive.More specific (also usually more direct) routes provided by other peers will take precedence when available, otherwise traffic will fall back to the least specific route and use the 192.0.2.1/24
catchall to forward traffic to the bounce server, where it will in turn be routed by the relay serverâs system routing table (net.ipv4.ip_forward = 1
) back down the VPN to the specific peer thatâs accepting routes for that traffic. WireGuard does not automatically find the fastest route or attempt to form direct connections between peers if not already defined, it just goes from the most specific route in [Peers]
to least specific.
You can figure out which routing method WireGuard is using for a given address by measuring the ping times to figure out the unique length of each hop, and by inspecting the output of:
wg show wg0
WireGuard uses encrypted UDP packets for all traffic, it does not provide guarantees around packet delivery or ordering, as that is handled by TCP connections within the encrypted tunnel.
Further reading:
WireGuard claims faster performance than most other competing VPN solutions, though the exact numbers are sometimes debated and may depend on whether hardware-level acceleration is available for certain cryptographic ciphers.
WireGuardâs performance gains are achieved by handling routing at the kernel level, and by using modern cipher suites running on all cores to encrypt traffic. WireGuard also gains a significant advantage by using UDP with no delivery/ordering guarantees (compared to VPNs that run over TCP or implement their own guaranteed delivery mechanisms).
Further reading:
WireGuard uses the following protocols and primitives to secure traffic:
WireGuardâs cryptography is essentially an instantiation of Trevor Perrinâs Noise framework. Itâs modern and, again, simple. Every other VPN option is a mess of negotiation and handshaking and complicated state machines. WireGuard is like the Signal/Axolotl of VPNs, except itâs much simpler and easier to reason about (cryptographically, in this case) than double ratchet messaging protocols. It is basically the qmail of VPN software. And itâs ~4000 lines of code. It is plural orders of magnitude smaller than its competitors.
https://news.ycombinator.com/item?id=14599834
Further reading:
Authentication in both directions is achieved with a simple public/private key pair for each peer. Each peer generates these keys during the setup phase, and shares only the public key with other peers.
No other certificates or pre-shared keys are needed beyond the public/private keys for each node.
Key generation, distribution, and revocation can be handled in larger deployments using a separate service like Ansible or Kubernetes Secrets.
Some services that help with key distribution and deployment:
You can also read in keys from a file or via command if you donât want to hardcode them in wg0.conf
, this makes managing keys via 3rd party service much easier:
[Interface]
...
PostUp = wg set %i private-key /etc/wireguard/wg0.key <(cat /some/path/%i/privkey)
Technically, multiple servers can share the same private key as long as clients arent connected to two servers with the same key simulatenously. An example of a scenario where this is a reasonable setup is if youâre using round-robin DNS to load-balance connections between two servers that are pretending to be a single server. Most of the time however, every peer should have its own public/private keypair so that peers canât read eachothers traffic and can be individually revoked.
Overview of the general process:
apt install wireguard
or pkg/brew install wireguard-tools
on each nodewg genkey
+wg pubkey
wg0.conf
WireGuard config file on the main relay server
[Interface]
Make sure to specify a CIDR range for the entire VPN subnet when defining the address the server accepts routes for Address = 192.0.2.1/24
[Peer]
Create a peer section for every client joining the VPN, using their corresponding remote public keyswg0.conf
on each client node
[Interface]
Make sure to specify only a single IP for client peers that donât relay traffic Address = 192.0.2.3/32
.[Peer]
Create a peer section for each public peer not behind a NAT, make sure to specify a CIDR range for the entire VPN subnet when defining the remote peer acting as the bounce server AllowedIPs = 192.0.2.1/24
. Make sure to specify individual IPs for remote peers that donât relay traffic and only act as simple clients AllowedIPs = 192.0.2.3/32
.wg-quick up /full/path/to/wg0.conf
wg-quick up /full/path/to/wg0.conf
ping 192.0.2.3
checks for a direct route to a peer with AllowedIPs = 192.0.2.3/32
first, then falls back to a relay server thatâs accepting IPs in the whole subnet# install on Ubuntu
sudo add-apt-repository ppa:wireguard/wireguard
apt install wireguard
# install on macOS
brew install wireguard-tools
# install on FreeBSD
pkg install wireguard
# install on iOS/Android using Apple App Store/Google Play Store
# install on other systems using https://www.wireguard.com/install/#installation
# to enable the kernel relaying/forwarding ability on bounce servers
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.conf.all.proxy_arp = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
# to add iptables forwarding rules on bounce servers
sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i wg0 -o wg0 -m conntrack --ctstate NEW -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 192.0.2.0/24 -o eth0 -j MASQUERADE
nano wg0.conf # can be placed anywhere, must be referred to using absolute path (usually placed in /etc/wireguard/wg0.conf on most Linux systems)
# generate private key
wg genkey > example.key
# generate public key
wg pubkey < example.key > example.key.pub
wg-quick up /full/path/to/wg0.conf
wg-quick down /full/path/to/wg0.conf
# Note: you must specify the absolute path to wg0.conf, relative paths won't work
# If wg0.conf is in /etc/wireguard you can use the simpler:
wg-quick up wg0
# start/stop VPN network interface
ip link set wg0 up
ip link set wg0 down
# register/unregister VPN network interface
ip link add dev wg0 type wireguard
ip link delete dev wg0
# register/unregister local VPN address
ip address add dev wg0 192.0.2.3/32
ip address delete dev wg0 192.0.2.3/32
# register/unregister VPN route
ip route add 192.0.2.3/32 dev wg0
ip route delete 192.0.2.3/32 dev wg0
# show system LAN and WAN network interfaces
ip address show
# or if ip is not available:
ifconfig
# show system VPN network interfaces
ip link show wg0
# or
ifconfig wg0
# show WireGuard VPN interfaces
wg show all
wg show wg0
# show public IP address
ip address show eth0
# or
ifconfig eth0
# or
dig -4 +short myip.opendns.com @resolver1.opendns.com
# show VPN IP address
ip address show wg0
# show WireGuard routing table and peer connections
wg show
wg show wg0 allowed-ips
# show system routing table
ip route show table main
ip route show table local
# show system route to specific address
ip route get 192.0.2.3
To enable additional logging run:
modprobe wireguard
echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
To follow logs:
dmesg -wH
Systems with modern kernel and Safe Boot might require disabling Secure Boot DKMS Signature Verification to allow access to kernel logs.
mokutil --disable-verification
reboot
# check that the main relay server is accessible directly via public internet
ping public-server1.example-vpn.dev
# check that the main relay server is available via VPN
ping 192.0.2.1
# check that public peers are available via VPN
ping 192.0.2.2
# check that remote NAT-ed peers are available via VPN
ping 192.0.2.3
# check that NAT-ed peers in your local LAN are available via VPN
ping 192.0.2.4
# install iperf using your preferred package manager
apt/brew/pkg/opkg install iperf
# check bandwidth over public internet to relay server
iperf -s # on public relay server
iperf -c public-server1.example-vpn.dev # on local client
# check bandwidth over VPN to relay server
iperf -s # on public relay server
iperf -c 192.0.2.1 # on local client
# check bandwidth over VPN to remote public peer
iperf -s # on remote public peer
iperf -c 192.0.2.2 # on local client
# check bandwidth over VPN to remote NAT-ed peer
iperf -s # on remote NAT-ed peer
iperf -c 192.0.2.3 # on local client
# check bandwidth over VPN to local NAT-ed peer (on same LAN)
iperf -s # on local NAT-ed peer
iperf -c 192.0.2.4 # on local client
Check for DNS leaks using http://dnsleak.com, or by checking the resolver on a lookup:
dig example.com A
WireGuard config is in INI syntax, defined in a file usually called wg0.conf
. It can be placed anywhere on the system, but is often placed in /etc/wireguard/wg0.conf
.
The config path is specified as an argument when running any wg-quick
command, e.g:
wg-quick up /etc/wireguard/wg0.conf
(always specify the full, absolute path)
The config file name must be in the format ${name of the new WireGuard interface}.conf
. WireGuard interface names are typically prefixed with wg
and numbered starting at 0
, but you can use any name that matches the regex ^[a-zA-Z0-9_=+.-]{1,15}$
.
Config files can opt to use the limited set of wg
config options, or the more extended wg-quick
options, depending on what command is preferred to start WireGuard. These docs recommend sticking to wg-quick
as it provides a more powerful and user-friendly config experience.
Jump to definition:
¶ [Interface]
¶ # Name = node1.example.tld
¶ Address = 192.0.2.3/32
¶ ListenPort = 51820
¶ PrivateKey = localPrivateKeyAbcAbcAbc=
¶ DNS = 1.1.1.1,8.8.8.8
¶ Table = 12345
¶ MTU = 1500
¶ PreUp = /bin/example arg1 arg2 %i
¶ PostUp = /bin/example arg1 arg2 %i
¶ PreDown = /bin/example arg1 arg2 %i
¶ PostDown = /bin/example arg1 arg2 %i
¶ [Peer]
¶ # Name = node2-node.example.tld
¶ AllowedIPs = 192.0.2.1/24
¶ Endpoint = node1.example.tld:51820
¶ PublicKey = remotePublicKeyAbcAbcAbc=
¶ PersistentKeepalive = 25
[Interface]
Defines the VPN settings for the local node.
Examples
[Interface]
# Name = phone.example-vpn.dev
Address = 192.0.2.5/32
PrivateKey = <private key for phone.example-vpn.dev>
[Interface]
# Name = public-server1.example-vpn.tld
Address = 192.0.2.1/24
ListenPort = 51820
PrivateKey = <private key for public-server1.example-vpn.tld>
DNS = 1.1.1.1
# Name
This is just a standard comment in INI syntax used to help keep track of which config section belongs to which node, itâs completely ignored by WireGuard and has no effect on VPN behavior.
NOTE: All comments, including # Name
, are removed from the .conf files by certain operations and applications.
If you need to identify peers, consider using a wireguard vanity key generator, such as
wireguard-vanity-keygen or
wireguard-vanity-address,
which will allow you to include the host name in the public key of the host.
The key generation can take minutes (4 characters), hours (5 characters) or longer,
so consider using an abbreviation for hosts with longer names.
Address
Defines what address range the local node should route traffic for. Depending on whether the node is a simple client joining the VPN subnet, or a bounce server thatâs relaying traffic between multiple clients, this can be set to a single IP of the node itself (specified with CIDR notation), e.g. 192.0.2.3/32), or a range of IPv4/IPv6 subnets that the node can route traffic for.
Examples
Node is a client that only routes traffic for itself
Address = 192.0.2.3/32
Node is a public bounce server that can relay traffic to other peers
When the node is acting as the public bounce server, it should set this to be the entire subnet that it can route traffic, not just a single IP for itself.
Address = 192.0.2.1/24
Address = 192.0.2.1/24,2001:DB8::/64
ListenPort
When the node is acting as a public bounce server, it should hardcode a port to listen for incoming VPN connections from the public internet. Clients not acting as relays should not set this value.
Examples
ListenPort = 51820
ListenPort = 7000
PrivateKey
This is the private key for the local node, never shared with other servers. All nodes must have a private key set, regardless of whether they are public bounce servers relaying traffic, or simple clients joining the VPN.
This key can be generated with wg genkey > example.key
Examples
PrivateKey = somePrivateKeyAbcdAbcdAbcdAbcd=
DNS
The DNS server(s) to announce to VPN clients via DHCP, most clients will use this server for DNS requests over the VPN, but clients can also override this value locally on their nodes
Examples
DNS = 1.1.1.1
DNS = 1.1.1.1,8.8.8.8
Table
Optionally defines which routing table to use for the WireGuard routes, not necessary to configure for most setups.
There are two special values: âoffâ disables the creation of routes altogether, and âautoâ (the default) adds routes to the default table and enables special handling of default routes.
https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8
Examples
Table = 1234
MTU
Optionally defines the maximum transmission unit (MTU, aka packet/frame size) to use when connecting to the peer, not necessary to configure for most setups.
The MTU is automatically determined from the endpoint addresses or the system default route, which is usually a sane choice.
https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8
Examples
MTU = 1500
PreUp
Optionally run a command before the interface is brought up. This option can be specified multiple times, with commands executed in the order they appear in the file.
Examples
PreUp = ip rule add ipproto tcp dport 22 table 1234
PostUp
Optionally run a command after the interface is brought up. This option can appear multiple times, as with PreUp
Examples
Read in a config value from a file or some commandâs output
PostUp = wg set %i private-key /etc/wireguard/wg0.key <(some command here)
Log a line to a file
PostUp = echo "$(date +%s) WireGuard Started" >> /var/log/wireguard.log
Hit a webhook on another server
PostUp = curl https://events.example.dev/wireguard/started/?key=abcdefg
Add a route to the system routing table
PostUp = ip rule add ipproto tcp dport 22 table 1234
Add an iptables rule to enable packet forwarding on the WireGuard interface
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Force WireGuard to re-resolve IP address for peer domain
PostUp = resolvectl domain %i "~."; resolvectl dns %i 192.0.2.1; resolvectl dnssec %i yes
PreDown
Optionally run a command before the interface is brought down. This option can appear multiple times, as with PreUp
Examples
Log a line to a file
PostDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log
Hit a webhook on another server
PostDown = curl https://events.example.dev/wireguard/stopping/?key=abcdefg
PostDown
Optionally run a command after the interface is brought down. This option can appear multiple times, as with PreUp
Examples
Log a line to a file
PostDown = echo "$(date +%s) WireGuard Stopped" >> /var/log/wireguard.log
Hit a webhook on another server
PostDown = curl https://events.example.dev/wireguard/stopped/?key=abcdefg
Remove the iptables rule that forwards packets on the WireGuard interface
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
Defines the VPN settings for a remote peer capable of routing traffic for one or more addresses (itself and/or other peers). Peers can be either a public bounce server that relays traffic to other peers, or a directly accessible client via LAN/internet that is not behind a NAT and only routes traffic for itself.
All clients must be defined as peers on the public bounce server. Simple clients that only route traffic for themselves, only need to define peers for the public relay, and any other nodes directly accessible. Nodes that are behind separate NATs should not be defined as peers outside of the public server config, as no direct route is available between separate NATs. Instead, nodes behind NATs should only define the public relay servers and other public clients as their peers, and should specify AllowedIPs = 192.0.2.1/24
on the public server that accept routes and bounce traffic for the VPN subnet to the remote NAT-ed peers.
In summary, all nodes must be defined on the main bounce server. On client servers, only peers that are directly accessible from a node should be defined as peers of that node, any peers that must be relayed by a bounce server should be left out and will be handled by the relay serverâs catchall route.
In the configuration outlined in the docs below, a single server public-server1
acts as the relay bounce server for a mix of publicly accessible and NAT-ed clients, and peers are configured on each node accordingly:
in public-server1
wg0.conf
(bounce server)
[peer]
list: public-server2
, home-server
, laptop
, phone
in public-server2
wg0.conf
(simple public client)
[peer]
list: public-server1
in home-server
wg0.conf
(simple client behind NAT)
[peer]
list: public-server1
, public-server2
in laptop
wg0.conf
(simple client behind NAT)
[peer]
list: public-server1
, public-server2
in phone
wg0.conf
(simple client behind NAT)
[peer]
list: public-server1
, public-server2
Examples
[Peer]
# Name = public-server2.example-vpn.dev
Endpoint = public-server2.example-vpn.dev:51820
PublicKey = <public key for public-server2.example-vpn.dev>
AllowedIPs = 192.0.2.2/32
[Peer]
# Name = home-server.example-vpn.dev
Endpoint = home-server.example-vpn.dev:51820
PublicKey = <public key for home-server.example-vpn.dev>
AllowedIPs = 192.0.2.3/32
[Peer]
# Name = public-server1.example-vpn.tld
Endpoint = public-server1.example-vpn.tld:51820
PublicKey = <public key for public-server1.example-vpn.tld>
# routes traffic to itself and entire subnet of peers as bounce server
AllowedIPs = 192.0.2.1/24
PersistentKeepalive = 25
# Name
This is just a standard comment in INI syntax used to help keep track of which config section belongs to which node, itâs completely ignored by WireGuard and has no effect on VPN behavior.
Endpoint
Defines the publicly accessible address for a remote peer. This should be left out for peers behind a NAT or peers that donât have a stable publicly accessible IP:PORT pair. Typically, this only needs to be defined on the main bounce server, but it can also be defined on other public nodes with stable IPs like public-server2
in the example config below.
Examples
Endpoint = 123.124.125.126:51820
(IPv6 is also supported)Endpoint = public-server1.example-vpn.tld:51820
AllowedIPs
This defines the IP ranges for which a peer will route traffic. On simple clients, this is usually a single address (the VPN address of the simple client itself). For bounce servers this will be a range of the IPs or subnets that the relay server is capable of routing traffic for. Multiple IPs and subnets may be specified using comma-separated IPv4 or IPv6 CIDR notation (from a single /32 or /128 address, all the way up to 0.0.0.0/0
and ::/0
to indicate a default route to send all internet and VPN traffic through that peer). This option may be specified multiple times.
When deciding how to route a packet, the system chooses the most specific route first, and falls back to broader routes. So for a packet destined to 192.0.2.3
, the system would first look for a peer advertising 192.0.2.3/32
specifically, and would fall back to a peer advertising 192.0.2.1/24
or a larger range like 0.0.0.0/0
as a last resort.
Examples
peer is a simple client that only accepts traffic to/from itself
AllowedIPs = 192.0.2.3/32
peer is a relay server that can bounce VPN traffic to all other peers
AllowedIPs = 192.0.2.1/24
peer is a relay server that bounces all internet & VPN traffic (like a proxy), including IPv6
AllowedIPs = 0.0.0.0/0,::/0
peer is a relay server that routes to itself and only one other peer
AllowedIPs = 192.0.2.3/32,192.0.2.4/32
peer is a relay server that routes to itself and all nodes on its local LAN
AllowedIPs = 192.0.2.3/32,192.168.1.1/24
PublicKey
This is the public key for the remote node, shareable with all peers. All nodes must have a public key set, regardless of whether they are public bounce servers relaying traffic, or simple clients joining the VPN.
This key can be generated with wg pubkey < example.key > example.key.pub
.
(see above for how to generate the private key example.key
)
Examples
PublicKey = somePublicKeyAbcdAbcdAbcdAbcd=
PersistentKeepalive
If the connection is going from a NAT-ed peer to a public peer, the node behind the NAT must regularly send an outgoing ping in order to keep the bidirectional connection alive in the NAT routerâs connection table.
Examples
local public node to remote public node
This value should be left undefined as persistent pings are not needed.
local public node to remote NAT-ed node
This value should be left undefined as itâs the clientâs responsibility to keep the connection alive because the server cannot reopen a dead connection to the client if it times out.
local NAT-ed node to remote public node
PersistentKeepalive = 25
this will send a ping to every 25 seconds keeping the connection open in the local NAT routerâs connection table.
The examples in these docs primarily use IPv4, but WireGuard natively supports IPv6 CIDR notation and addresses everywhere that it supports IPv4, simply add them as you would any other subnet range or address.
Example
[Interface]
AllowedIps = 192.0.2.3/24, 2001:DB8::/64
[Peer]
...
AllowedIPs = 0.0.0.0/0, ::/0
If you want to forward all internet traffic through the VPN, and not just use it as a server-to-server subnet, you can add 0.0.0.0/0, ::/0
to the AllowedIPs
definition of the peer you want to pipe your traffic through.
Make sure to also specify an IPv6 catchall even when only forwarding IPv4 traffic in order to avoid leaking IPv6 packets outside the VPN, see:
https://www.reddit.com/r/WireGuard/comments/b0m5g2/ipv6_leaks_psa_for_anyone_here_using_wireguard_to/
Example
[Interface]
# Name = phone.example-vpn.dev
Address = 192.0.2.3/32
PrivateKey = <private key for phone.example-vpn.dev>
[Peer]
# Name = public-server1.example-vpn.dev
PublicKey = <public key for public-server1.example-vpn.dev>
Endpoint = public-server1.example-vpn.dev:51820
AllowedIPs = 0.0.0.0/0, ::/0
WireGuard can sometimes natively make connections between two clients behind NATs without the need for a public relay server, but in most cases this is not possible. NAT-to-NAT connections are only possible if at least one host has a stable, publicly-accessible IP address:port pair that can be hardcoded ahead of time, whether thatâs using a FQDN updated with Dynamic DNS, or a static public IP with a non-randomized NAT port opened by outgoing packets, anything works as long as all peers can communicate it beforehand and it doesnât change once the connection is initiated.
A known port and address need to be configured ahead of time because WireGuard doesnât have a signalling layer or public STUN servers that can be used to search for other hosts dynamically. WebRTC is an example of a protocol that can dynamically configure a connection between two NATs, but it does this by using an out-of-band signaling server to detect the IP:port combo of each host. WireGuard doesnât have this, so it only works with a hardcoded Endpoint
+ ListenPort
(and PersistentKeepalive
so it doesnât drop after inactivity).
Learn more from Tailscaleâs bible of NAT traversal: https://tailscale.com/blog/how-nat-traversal-works/
Endpoint
defined. If theyâre both behind NATs without stable IP addresses, then youâll need to use Dynamic DNS or another solution to have a stable, publicly accessibly domain/IP for at least one peerListenPort
defined, and itâs NAT router must not do UDP source port randomization, otherwise return packets will be sent to the hardcoded ListenPort
and dropped by the router, instead of using the random port assigned by the NAT on the outgoing packetPersistentKeepalive
enabled on all other peers, so that they continually send outgoing pings to keep connections persisted in their NATâs routing tableThis process of sending an initial packet that gets rejected, then using the fact that the router has now created a forwarding rule to accept responses is called âUDP hole-punchingâ.
When you send a UDP packet out, the router (usually) creates a temporary rule mapping your source address and port to the destination address and port, and vice versa. UDP packets returning from the destination address and port (and no other) are passed through to the original source address and port (and no other). This is how most UDP applications function behind NATs (e.g. BitTorrent, Skype, etc). This rule will timeout after some minutes of inactivity, so the client behind the NAT must send regular outgoing packets to keep it open (see PersistentKeepalive
).
Getting this to work when both end-points are behind NATs or firewalls requires that both end-points send packets to each-other at about the same time. This means that both sides need to know each-otherâs public IP addresses and port numbers ahead of time, in WireGuardâs case this is achieved by hard-coding pre-defined ports for both sides in wg0.conf
.
As of 2019, many of the old hole-punching methods used that used to work are no longer effective. One example was a novel method pioneered by pwnat that faked an ICMP Time Exceeded response from outside the NAT to get a packet back through to a NATâed peer, thereby leaking its own source port. Hardcoding UDP ports and public IPs for both sides of a NAT-to-NAT connection (as described above) still works on a small percentage of networks. Generally the more âenterpriseyâ a network is, the less likely youâll be able to hole punch public UDP ports (commercial public Wi-Fi and cell data NATs often donât work for example).
NAT-to-NAT connections are not possible if all endpoints are behind NATâs with strict UDP source port randomization (e.g. most cellular data networks). Since neither side is able to hardcode a ListenPort
and guarantee that their NAT will accept traffic on that port after the outgoing ping, you cannot coordinate a port for the initial hole-punch between peers and connections will fail. For this reason, you generally cannot do phone-to-phone connections on LTE/3g networks, but you might be able to do phone-to-office or phone-to-home where the office or home has a stable public IP and doesnât do source port randomization.
NAT-to-NAT connections from behind NATs with strict source-port randomization is possible, you just need a signaling server to tell each side the otherâs IP:port tuple. Here are a few implementations that achieve this with WireGuard:
Many users report having to restart WireGuard whenever a dynamic IP changes, as it only resolves hostnames on startup. To force WireGuard to re-resolve dynamic DNS Endpoint
hostnames more often, you may want to use a PostUp
hook to restart WireGuard every few minutes or hours.
You can see if a hole-punching setup is feasible by using netcat on the client and server to see what ports and connection order work to get a bidirectional connection open: run nc -v -u -p 51820 <address of peer2> 51820
(on peer1) and nc -v -u -l 0.0.0.0 51820
(on peer2), then type in both windows to see if you can get bidirectional traffic going. If it doesnât work regardless of which peer sends the initial packet, then WireGuard wonât be unable to work between the peers without a public relay server.
NAT-to-NAT connections are often more unstable and have other limitations, which is why having a fallback public relay server is still advised.
Example
Peer1:
[Interface]
...
ListenPort 12000
[Peer]
...
Endpoint = peer2.example-vpn.dev:12000
PersistentKeepalive = 25
Peer2:
[Interface]
...
ListenPort 12000
[Peer]
...
Endpoint = peer1.example-vpn.dev:12000
PersistentKeepalive = 25
Note: this section is about dynamic peer IPs within the VPN subnet, not dynamic public Endpoint
addresses.
Dynamic allocation of peer IPs (instead of only having fixed peers) is being developed, the WIP implementation is available here: https://github.com/WireGuard/wg-dynamic
You can also build a dynamic allocation system yourself by reading in IP values from files at runtime by using PostUp
(see below).
Example
[Interface]
...
PostUp = wg set %i allowed-ips /etc/wireguard/wg0.key <(some command)
https://git.zx2c4.com/wireguard-go/about/
A compliant userland WireGuard implementation written in Go.
https://git.zx2c4.com/wireguard-rs/about/
An incomplete, insecure userspace implementation of WireGuard written in Rust (not ready for the public).
https://git.zx2c4.com/wireguard-hs/about/
An incomplete, insecure userspace implementation of WireGuard written in Haskell (not ready for the public).
https://github.com/cloudflare/boringtun
A non-compliant, independent WireGuard implementation written in Rust (a separate fork written by CloudFlare).
See https://blog.cloudflare.com/boringtun-userspace-wireguard-rust/
Platform-specific WireGuard apps
https://git.zx2c4.com/wireguard-ios/about/
https://git.zx2c4.com/wireguard-android/about/
https://git.zx2c4.com/wireguard-windows/about/
All of the userspace implementations are slower than the native C version that runs in kernel-land, but provide other benefits by running in userland (e.g. easier containerization, compatibility, etc.).
These are some GUI and CLI tools that wrap WireGuard to assist with config, deployment, key management, and connection.
Credit for these shortcuts goes to: https://www.ericlight.com/new-things-i-didnt-know-about-wireguard.html
WireGuard will ignore a peer whose public key matches the interfaceâs private key. So you can distribute a single list of peers everywhere, and only define the [Interface]
separately on each server.
See: https://lists.zx2c4.com/pipermail/wireguard/2018-December/003703.html
You can combine this with wg addconf
like this:
Each peer has its own /etc/wireguard/wg0.conf
file, which only contains its [Interface]
section.
Each peer also has a shared /etc/wireguard/peers.conf
file, which contains all the peers.
The wg0.conf
file also has a PostUp
hook: PostUp = wg addconf /etc/wireguard/peers.conf
.
Itâs up to you to decide how you want to share the peers.conf
, be it via a proper orchestration platform, something much more pedestrian like Dropbox, or something kinda wild like Ceph. I dunno, but itâs pretty great that you can just wildly fling a peer section around, without worrying whether itâs the same as the interface.
You can set config values from arbitrary commands or by reading in values from files, this makes key management and deployment much easier as you can read in keys at runtime from a 3rd party service like Kubernetes Secrets or AWS KMS.
See: https://lists.zx2c4.com/pipermail/wireguard/2018-December/003702.html
Example
You can read in a file as the PrivateKey
by doing something like:
PostUp = wg set %i private-key /etc/wireguard/wg0.key <(some command)
WireGuard can be run in Docker with varying degrees of ease. In the simplest case, --privileged
and --cap-add=all
arguments can be added to the docker commands to enable the loading of the kernel module.
Setups can get somewhat complex and are highly dependent on what youâre trying to achieve. You can have WireGuard itself run in a container and expose a network interface to the host, or you can have WireGuard running on the host exposing an interface to specific containers.
See below for an example of a Docker container vpn_test
routing all its traffic through a WireGuard relay server.
version: '3'
services:
wireguard:
image: linuxserver/wireguard
ports:
- 51820:51820/udp
cap_add:
- NET_ADMIN
- SYS_MODULE
volumes:
- /lib/modules:/lib/modules
- ./wg0.conf:/config/wg0.conf:ro
wg0.conf
:
[Interface]
# Name = relay1.wg.example.com
Address = 192.0.2.1/24
ListenPort = 51820
PrivateKey = oJpRt2Oq27vIB5/UVb7BRqCwad2YMReQgH5tlxz8YmI=
DNS = 1.1.1.1,8.8.8.8
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Name = peer1.wg.example.com
PublicKey = I+hXRAJOG/UE2IQvIHsou2zTgkUyPve2pzvHTnd/2Gg=
AllowedIPs = 192.0.2.2/32
In this example all the traffic from inside the speedtest
container will go through the wireguard VPN.
To only route some traffic, replace 0.0.0.0/0
in wg0.conf
below with the subnet ranges you want to route via the VPN.
docker-compose.yml
:
version: '3'
services:
wireguard:
image: linuxserver/wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
volumes:
- /lib/modules:/lib/modules
- ./wg0.conf:/config/wg0.conf:ro
vpn_test:
image: curlimages/curl
entrypoint: curl -s http://whatismyip.akamai.com/
network_mode: 'service:wireguard'
wg0.conf
:
[Interface]
# Name = peer1.wg.example.com
Address = 192.0.2.2/32
PrivateKey = YCW76edD4W7nZrPbWZxPZhcs32CsBLIi1sEhsV/sgk8=
DNS = 1.1.1.1,8.8.8.8
[Peer]
# Name = relay1.wg.example.com
Endpoint = relay1.wg.example.com:51820
PublicKey = zJNKewtL3gcHdG62V3GaBkErFtapJWsAx+2um0c0B1s=
AllowedIPs = 192.0.2.1/24,0.0.0.0/0
PersistentKeepalive = 21
For more details see the Further Reading: Docker section below.
For more detailed instructions, see the QuickStart guide and API reference above. You can also download the complete example setup here: https://github.com/pirate/wireguard-example.