pirate / wireguard-docs
- четверг, 30 мая 2019 г. в 00:16:13
Shell
The Missing WireGuard Documentation: Setup, Usage, Configuration, and a full example for server-to-server VPN with roaming clients & public peers.
API reference guide for Wireguard including Setup, Configuration, and Usage, with examples.
All credit goes to the WireGuard project, zx2c4, Edge Security, and the open source contributors for the original software,
this is my solo unofficial attempt at providing more comprehensive documentation, API references, and examples.
Source for these docs, example code, and issue tracker: https://github.com/pirate/wireguard-docs Nicer HTML page version: https://docs.sweeting.me/s/wireguard
WireGuard is a BETA/WIP 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.
This is my attempt at writing "The Missing Wireguard Documentation" to make up for the somewhat sparse offical docs on an otherwise great piece of software.
Official Links
WireGuard Goals

See https://github.com/pirate/wireguard-docs for example code and documentation source.
Over the last 8+ years I've tried a wide range of VPN solutions. Somewhat out of necessity, since the city I was living in was behind the Great Wall of China. Everything from old-school PPTP to crazy round-robin GoAgent AppEngine proxy setups were common back in the early 2010's to break through the GFW, these days it's mostly OpenVPN, StealthVPN, IPSec/IKEv2 and others. From the recommendation of a few people in the RC Zulip community, I decided to try WireGuard and was surprised to find it checked almost all the boxes.
A host that connects to the VPN and has registers a VPN subnet address like 10.0.0.3 for itself. It can also optionally route traffic for more than its own address(es) by specifing 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 group of IPs separate from the public internet, e.g. 10.0.0.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 WiFi 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:
https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
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 wifi networks, free public wifi 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 10.0.0.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.
example-vpn.dev, public-server1, public-server2, home-server, laptop, phone
These are demo host and domain names used in the example config. Replace them with your own names when doing your actual setup.
Overview of the general process:
apt install wireguard or pkg/brew install wireguard-tools on each nodewg genkey+wg pubkeywg0.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 = 10.0.0.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 = 10.0.0.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 = 10.0.0.1/24. Make sure to specify individual IPs for remote peers that don't relay traffic and only act as simple clients AllowedIPs = 10.0.0.3/32.wg-quick up /full/path/to/wg0.confwg-quick up /full/path/to/wg0.confping 10.0.0.3 checks for local direct route first, then checks for route via public internet, then finally tries to route by bouncing through the public relay server.# 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/Andoid using Apple App Store/Google Play Store
# install on other systems using https://www.wireguard.com/install/#installation# to enable kernel relaying/forwarding ability on bounce servers
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.proxy_arp" >> /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
# to add iptables forwarding rules on bounce servers
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i wg0 -o wg0 -m conntrack --ctstate NEW -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
nano wg0.conf # can be placed anywhere, must be referred to using absolute path# generate private key
wg genkey > example.key
# generate public key
wg pubkey < example.key > example.key.pubwg-quick up /full/path/to/wg0.conf
wg-quick down /full/path/to/wg0.conf# 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 10.0.0.3/32
ip address delete dev wg0 10.0.0.3/32
# register/unregister VPN route
ip route add 10.0.0.3/32 dev wg0
ip route delete 10.0.0.3/32 dev wg0# show system LAN and WAN network interfaces
ifconfig
ip address show
# show system VPN network interfaces
ifconfig wg0
ip link show wg0
# show wireguard VPN interfaces
wg show all
wg show wg0# show public ip address
ifconfig eth0
ip address show eth0
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 10.0.0.3# check that main relay server is accesible directly via public internet
ping public-server1.example-vpn.dev
# check that the main relay server is available via VPN
ping 10.0.0.1
# check that public peers are available via VPN
ping 10.0.0.2
# check that remote NAT-ed peers are available via VPN
ping 10.0.0.3
# check that NAT-ed peers in your local lan are available via VPN
ping 10.0.0.4# 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 10.0.0.1 # on local client
# check bandwidth over VPN to remote public peer
iperf -s # on remote public peer
iperf -c 10.0.0.2 # on local client
# check bandwidth over VPN to remote NAT-ed peer
iperf -s # on remote NAT-ed peer
iperf -c 10.0.0.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 10.0.0.4 # on local clientCheck for DNS leaks using http://dnsleak.com, or by checking the resolver on a lookup:
dig example.com AJump to definition:
¶ [Inteface]
¶ # Name = node1.example.tld
¶ Address = 10.0.0.3/32
¶ ListenPort = 51820
¶ PrivateKey = localPrivateKeyAbcAbcAbc=
¶ DNS = 1.1.1.1,8.8.8.8
¶ [Peer]
¶ # Name = node2-node.example.tld
¶ AllowedIPs = 10.0.0.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 = 10.0.0.5/32
PrivateKey = <private key for phone.example-vpn.dev>[Interface]
# Name = public-server1.example-vpn.tld
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <private key for public-server1.example-vpn.tld>
DNS = 1.1.1.1# NameThis 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.
AddressDefines 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 (specificed with CIDR notation), e.g. 10.0.0.3/32), or a range of IPv4/IPv6 subnets that the node can route traffic for.
Examples
Address = 10.0.0.3/32
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 = 10.0.0.1/24
Address = 10.0.0.1/24,fd42:42:42::1/64
ListenPortWhen 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 = 51820ListenPort = 7000PrivateKeyThis 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=
DNSThe 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.1DNS = 1.1.1.1,8.8.8.8[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, 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, however on the simple clients that only route traffic for themselves, only the public relay and other directly accessible nodes need to be defined as peeers. Nodes that are behind separate NATs should not be defined as peers outside of the public server config, as no specific 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 = 10.0.0.1/24 on the public server that accept routes and bounce traffic 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 sherver 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 is a simple public client that only routes traffic for itself
[Peer]
# Name = public-server2.example-vpn.dev
Endpoint = public-server2.example-vpn.dev:51820
PublicKey = <public key for public-server2.example-vpn.dev>
AllowedIPs = 10.0.0.2/32Peer is a simple client behind a NAT that only routes traffic for itself
[Peer]
# Name = home-server.example-vpn.dev
Endpoint = home-server.example-vpn.dev:51820
PublicKey = <public key for home-server.example-vpn.dev>
AllowedIPs = 10.0.0.3/32Peer is a public bounce server that can relay traffic to other peers
[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 = 10.0.0.1/24
PersistentKeepalive = 25# NameThis 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.
EndpointDefines 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:51820Endpoint = public-server1.example-vpn.tld:51820AllowedIPsThis defines the IP ranges that a peer will route traffic for. Usually this is a single address (the VPN address of the peer itself), but for bounce servers this will be a range of the IPs or subnets that the relay server is capable of routing traffic for. Using comma-separated IPv4 or IPv6 CIDR notation, a single address can be defined as routable, or multiple ranges of IPs all the way up to 0.0.0.0/0 to route all internet and VPN traffic through that peer.
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 10.0.0.3, the system would first look for a peer advertising 10.0.0.3/32 specifically, and would fall back to a peer advertising 10.0.0.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 = 10.0.0.3/32
peer is a relay server that can bounce VPN traffic to all other peers
AllowedIPs = 10.0.0.1/24
peer is a relay server that bounces all internet & VPN traffic (like a proxy)
AllowedIPs = 0.0.0.0/0,::/0
peer is a relay server that routes to itself and only one other peer
AllowedIPs = 10.0.0.3/32,10.0.0.4/32
peer is a relay server that routes to itself and all nodes on its local LAN
AllowedIPs = 10.0.0.3/32,192.168.1.1/24
PublicKeyThis is the public key for the remote node, sharable 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 = somePrivateKeyAbcdAbcdAbcdAbcd=
PersistentKeepaliveIf 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 complete example config for the setup below can be found here: https://github.com/pirate/wireguard-docs/tree/master/full-example (WARNING: do not use it on your devices without chaning the public/private keys!).
These 5 devices are used in our example setup to explain how WireGuard supports bridging across a variety of network conditions, they're all under an example domain example-vpn.dev, with the following short hostnames:
public-server1 (not behind a NAT, acts as the main VPN bounce server)public-server2 (not behind a NAT, joins as a peer without bouncing traffic)home-server (behind a NAT, joins as a peer without bouncing traffic)laptop (behind NAT, sometimes shared w/ home-server/phone, sometimes roaming)phone (behind NAT, sometimes shared w/ home-server/laptop, sometimes roaming)This VPN config simulates setting up a small VPN subnet 10.0.0.1/24 shared by 5 nodes. Two of the nodes (public-server1 and public-server2) are VPS instances living in a cloud somewhere, with public IPs accessible to the internet. home-server is a stationary node that lives behind a NAT with a dynamic IP, but it doesn't change frequently. Phone and laptop are both roaming nodes, that can either be at home in the same LAN as home-server, or out-and-about using public wifi or cell service to connect to the VPN.
Whenever possible, nodes should connect directly to eachother, depending on whether nodes are directly accessible or NATs are between them, traffic will route accordinly:
public-server1 acts as an intermediate relay server between any VPN clients behind NATs, it will forward any 10.0.0.1/24 traffic it receives 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).
Each client only needs to define the publicly accessible servers/peers in it's config, any traffic bound to other peers behind NATs will go to the catchall 10.0.0.1/24 for the server and will be forwarded accordingly once it hits the main 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:
public-server2), the connection will be opened from NAT -> public client, then traffic will route directly between them in both directions as long as the connection is kept alive.public-server1, and traffic will forward through the intermediary bounce server as long as the connections are kept alive.public-server1 should make connection establishment and handoff relatively easy, but ICMP packet trickery can also be used. Please let me know if WireGuard does this via Github Issue.Chosing the proper routing method is handled automatically by WireGuard as long as at least one server is acting as a public relay with net.ipv4.ip_forward = 1 enabled, and clients have AllowIPs = 10.0.0.1/24 set in the relay server [peer] (to take traffic for the whole subnet).
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 10.0.0.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 back down the VPN to the specific peer thats accepting routes for that traffic.
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 wg0To run this full example, simply copy the full wg0.conf config file for node section from each node onto each server, enable IP forwarding on the public relay, and then start WireGuard on all the machines.
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-docs/tree/master/full-example (WARNING: do not use it on your devices without chaning the public/private keys!).
public-server1.example-vpn.tld:5182010.0.0.110.0.0.1/24<private key for public-server1.example-vpn.tld><public key for public-server1.example-vpn.tld>[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 = 10.0.0.1/24
PersistentKeepalive = 25[Interface]
# Name = public-server1.example-vpn.tld
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <private key for public-server1.example-vpn.tld>
DNS = 1.1.1.1wg0.conf config file for node:[Interface]
# Name = public-server1.example-vpn.tld
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <private key for public-server1.example-vpn.tld>
DNS = 1.1.1.1
[Peer]
# Name = public-server2.example-vpn.dev
Endpoint = public-server2.example-vpn.dev:51820
PublicKey = <public key for public-server2.example-vpn.dev>
AllowedIPs = 10.0.0.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 = 10.0.0.3/32
[Peer]
# Name = laptop.example-vpn.dev
PublicKey = <public key for laptop.example-vpn.dev>
AllowedIPs = 10.0.0.4/32
[Peer]
# phone.example-vpn.dev
PublicKey = <public key for phone.example-vpn.dev>
AllowedIPs = 10.0.0.5/32public-server2.example-vpn.dev:5182010.0.0.210.0.0.2/32<private key for public-server2.example-vpn.dev><public key for public-server2.example-vpn.dev>[Interface]
# Name = public-server2.example-vpn.dev
Address = 10.0.0.2/32
ListenPort = 51820
PrivateKey = <private key for public-server2.example-vpn.dev>
DNS = 1.1.1.1[Peer]
# Name = public-server2.example-vpn.dev
Endpoint = public-server2.example-vpn.dev:51820
PublicKey = <public key for public-server2.example-vpn.dev>
AllowedIPs = 10.0.0.2/32wg0.conf config file for node:[Interface]
# Name = public-server2.example-vpn.dev
Address = 10.0.0.2/32
ListenPort = 51820
PrivateKey = <private key for public-server2.example-vpn.dev>
DNS = 1.1.1.1
[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 = 10.0.0.1/24
PersistentKeepalive = 2510.0.0.310.0.0.3/32<private key for home-server.example-vpn.dev><public key for home-server.example-vpn.dev>[Interface]
# Name = home-server.example-vpn.dev
Address = 10.0.0.3/32
ListenPort = 51820
PrivateKey = <private key for home-server.example-vpn.dev>
DNS = 1.1.1.1[Peer]
# Name = home-server.example-vpn.dev
Endpoint = home-server.example-vpn.dev:51820
PublicKey = <public key for home-server.example-vpn.dev>
AllowedIPs = 10.0.0.3/32wg0.conf config file for node:[Interface]
# Name = home-server.example-vpn.dev
Address = 10.0.0.3/32
ListenPort = 51820
PrivateKey = <private key for home-server.example-vpn.dev>
DNS = 1.1.1.1
[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 = 10.0.0.1/24
PersistentKeepalive = 2510.0.0.410.0.0.4/32<private key for laptop.example-vpn.dev><public key for laptop.example-vpn.dev>[Interface]
# Name = laptop.example-vpn.dev
Address = 10.0.0.4/32
PrivateKey = <private key for laptop.example-vpn.dev>
DNS = 1.1.1.1[Peer]
# Name = laptop.example-vpn.dev
PublicKey = <public key for laptop.example-vpn.dev>
AllowedIPs = 10.0.0.4/32wg0.conf config file for node:[Interface]
# Name = laptop.example-vpn.dev
Address = 10.0.0.4/32
PrivateKey = <private key for laptop.example-vpn.dev>
DNS = 1.1.1.1
[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 = 10.0.0.1/24
PersistentKeepalive = 2510.0.0.510.0.0.5/32<private key for phone.example-vpn.dev><public key for phone.example-vpn.dev>[Interface]
# Name = phone.example-vpn.dev
Address = 10.0.0.5/32
PrivateKey = <private key for phone.example-vpn.dev>
DNS = 1.1.1.1[Peer]
# phone.example-vpn.dev
PublicKey = <public key for phone.example-vpn.dev>
AllowedIPs = 10.0.0.5/32wg0.conf config file for node:[Interface]
# Name = phone.example-vpn.dev
Address = 10.0.0.5/32
PrivateKey = <private key for phone.example-vpn.dev>
DNS = 1.1.1.1
[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 = 10.0.0.1/24
PersistentKeepalive = 25Suggest changes: https://github.com/pirate/wireguard-docs/issues