WireGuard
A rough introduction to the main concepts used in this article can be found on WireGuard's project homepage. WireGuard has been included in the Linux kernel since late 2019.
From the WireGuard project homepage:
- WireGuard is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable.
Installation
Install the wireguard-tools package for userspace utilities.
Alternatively, various network managers provide support for WireGuard, provided that peer keys are available. See #Persistent configuration for details.
Graphical clients
- Qomui — OpenVPN GUI with advanced features and support for multiple providers.
Command-line tools
- wg_tool — Tool to manage wireguard configs for server and users.
Usage
The commands below demonstrate how to set up a basic tunnel between two or more peers with the following settings:
External (public) addresses | Internal IP addresses | Port | ||||
---|---|---|---|---|---|---|
Domain name | IPv4 address | IPv6 address | IPv4 address | IPv6 address | ||
Peer A | 198.51.100.101 | 2001:db8:a85b:70a:ffd4:ec1b:4650:a001 | 10.0.0.1/24 | fdc9:281f:04d7:9ee9::1/64 | UDP/51871 | |
Peer B | peer-b.example | 203.0.113.102 | 2001:db8:40f0:147a:80ad:3e88:f8e9:b002 | 10.0.0.2/24 | fdc9:281f:04d7:9ee9::2/64 | UDP/51902 |
Peer C | dynamic | dynamic | 10.0.0.3/24 | fdc9:281f:04d7:9ee9::3/64 | UDP/51993 |
The external addresses should already exist. For example, if ICMP echo requests are not blocked, peer A should be able to ping peer B via its public IP address(es) and vice versa.
The internal addresses will be new addresses, created either manually using the ip(8) utility or by network management software, which will be used internally within the new WireGuard network. The following examples will use 10.0.0.0/24 and fdc9:281f:04d7:9ee9::/64 as the internal network. The /24
and /64
in the IP addresses is the CIDR.
Key generation
Create a private and public key for each peer. If connecting dozens of peers optionally consider a vanity keypair to personalize the Base64 encoded public key string. See #Vanity keys.
To create a private key run:
$ (umask 0077; wg genkey > peer_A.key)
To create a public key:
$ wg pubkey < peer_A.key > peer_A.pub
Alternatively, do this all at once:
$ wg genkey | (umask 0077 && tee peer_A.key) | wg pubkey > peer_A.pub
One can also generate a pre-shared key to add an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance. A pre-shared key should be generated for each peer pair and should not be reused. For example, three interconnected peers, A, B, and, C will need three separate pre-shared keys, one for each peer pair.
Generate a pre-shared key for each peer pair using the following command (make sure to use for this as well):
$ wg genpsk > peer_A-peer_B.psk $ wg genpsk > peer_A-peer_C.psk $ wg genpsk > peer_B-peer_C.psk
Vanity keys
Currently, WireGuard does not support comments or attaching human-memorable names to keys. This makes identifying the key's owner difficult particularly when multiple keys are in use. One solution is to generate a public key that contains some familiar characters (perhaps the first few letters of the owner's name or of the hostname etc.), does this.
For example:
Peer setup
Manual setup is accomplished by using ip(8) and .
Peer A setup:
In this example peer A will listen on UDP port 51871 and will accept connection from peer B and C.
# ip link add dev wg0 type wireguard # ip addr add 10.0.0.1/24 dev wg0 # ip addr add fdc9:281f:04d7:9ee9::1/64 dev wg0 # wg set wg0 listen-port 51871 private-key /path/to/peer_A.key # wg set wg0 peer PEER_B_PUBLIC_KEY preshared-key /path/to/peer_A-peer_B.psk endpoint peer-b.example:51902 allowed-ips 10.0.0.2/32,fdc9:281f:04d7:9ee9::2/128 # wg set wg0 peer PEER_C_PUBLIC_KEY preshared-key /path/to/peer_A-peer_C.psk allowed-ips 10.0.0.3/32,fdc9:281f:04d7:9ee9::3/128 # ip link set wg0 up
should be the contents of .
The keyword is a list of addresses that will get routed to the peer. Make sure to specify at least one address range that contains the WireGuard connection's internal IP address(es).
Peer B setup:
# ip link add dev wg0 type wireguard # ip addr add 10.0.0.2/24 dev wg0 # ip addr add fdc9:281f:04d7:9ee9::2/64 dev wg0 # wg set wg0 listen-port 51902 private-key /path/to/peer_B.key # wg set wg0 peer PEER_A_PUBLIC_KEY preshared-key /path/to/peer_A-peer_B.psk endpoint 198.51.100.101:51871 allowed-ips 10.0.0.1/32,fdc9:281f:04d7:9ee9::1/128 # wg set wg0 peer PEER_C_PUBLIC_KEY preshared-key /path/to/peer_B-peer_C.psk allowed-ips 10.0.0.3/32,fdc9:281f:04d7:9ee9::3/128 # ip link set wg0 up
Peer C setup:
# ip link add dev wg0 type wireguard # ip addr add 10.0.0.3/24 dev wg0 # ip addr add fdc9:281f:04d7:9ee9::3/64 dev wg0 # wg set wg0 listen-port 51993 private-key /path/to/peer_C.key # wg set wg0 peer PEER_A_PUBLIC_KEY preshared-key /path/to/peer_A-peer_C.psk endpoint 198.51.100.101:51871 allowed-ips 10.0.0.1/32,fdc9:281f:04d7:9ee9::1/128 # wg set wg0 peer PEER_B_PUBLIC_KEY preshared-key /path/to/peer_B-peer_C.psk endpoint peer-b.example:51902 allowed-ips 10.0.0.2/32,fdc9:281f:04d7:9ee9::2/128 # ip link set wg0 up
Additional routes
To establish connections more complicated than point-to-point, additional setup is necessary.
Point-to-site
To access the network of a peer, specify the network subnet(s) in in the configuration of the peers who should be able to connect to it. E.g. .
Make sure to also set up the routing table with . E.g.:
# ip route add 192.168.35.0/24 dev wg0 # ip route add fd7b:d0bd:7a6e::/64 dev wg0
Site-to-point
If the intent is to connect a device to a network with WireGuard peer(s), set up routes on each device so they know that the peer(s) are reachable via the device.
Enable IP forwarding on the peer through which other devices on the network will connect to WireGuard peer(s):
# sysctl -w net.ipv4.ip_forward=1 # sysctl -w net.ipv6.conf.all.forwarding=1
See sysctl#Configuration for instructions on how to set the sysctl parameters on boot.
Site-to-site
To connect two (or more) networks, apply both #Point-to-site and #Site-to-point on all sites.
DNS
To use a peer as a DNS server, add its WireGuard tunnel IP address(es) to /etc/resolv.conf. For example, to use peer B as the DNS server:
Basic checkups
Invoking the command without parameters will give a quick overview of the current configuration.
As an example, when peer A has been configured we are able to see its identity and its associated peers:
# wg
interface: wg0 public key: UguPyBThx/+xMXeTbRYkKlP0Wh/QZT3vTLPOVaaXTD8= private key: (hidden) listening port: 51871 peer: 9jalV3EEBnVXahro0pRMQ+cHlmjE33Slo9tddzCVtCw= endpoint: 203.0.113.102:51902 allowed ips: 10.0.0.2/32, fdc9:281f:04d7:9ee9::2 peer: 2RzKFbGMx5g7fG0BrWCI7JIpGvcwGkqUaCoENYueJw4= endpoint: 192.0.2.103:51993 allowed ips: 10.0.0.3/32, fdc9:281f:04d7:9ee9::3
At this point one could reach the end of the tunnel. If the peers do not block ICMP echo requests, try pinging a peer to test the connection between them.
Using ICMPv4:
$ ping 10.0.0.2
Using ICMPv6:
$ ping fdc9:281f:04d7:9ee9::2
After transferring some data between peers, the utility will show additional information:
Persistent configuration
Persistent configuration can be achieved using , which is shipped with wireguard-tools, or using a network manager. Network managers that support WireGuard are systemd-networkd, netctl, NetworkManager and ConnMan.
wg-quick
configures WireGuard tunnels using configuration files from .
The current WireGuard configuration can be saved by utilizing the utility's showconf
command. For example:
# wg showconf wg0 > /etc/wireguard/wg0.conf
To start a tunnel with a configuration file, use
# wg-quick up interfacename
or use the systemd service—. To start the tunnel at boot, enable the unit.
Peer A setup:
- To route all traffic through the tunnel to a specific peer, add the default route ( for IPv4 and
::/0
for IPv6) to . E.g. . wg-quick will automatically take care of setting up correct routing and fwmark so that networking still functions. - To use a peer as a DNS server, set in the section. Search domains are also set with the
DNS =
option. Separate all values in the list with commas.
Peer B setup:
Peer C setup:
systemd-networkd
systemd-networkd has native support for setting up WireGuard interfaces. An example is provided in the man page.
Peer A setup:
/etc/systemd/network/99-wg0.netdev
[NetDev] Name=wg0 Kind=wireguard Description=WireGuard tunnel wg0 [WireGuard] ListenPort=51871 PrivateKey=''PEER_A_PRIVATE_KEY'' [WireGuardPeer] PublicKey=''PEER_B_PUBLIC_KEY'' PresharedKey=''PEER_A-PEER_B-PRESHARED_KEY'' AllowedIPs=10.0.0.2/32 AllowedIPs=fdc9:281f:04d7:9ee9::2/128 Endpoint=peer-b.example:51902 [WireGuardPeer] PublicKey=''PEER_C_PUBLIC_KEY'' PresharedKey=''PEER_A-PEER_C-PRESHARED_KEY'' AllowedIPs=10.0.0.3/32 AllowedIPs=fdc9:281f:04d7:9ee9::3/128
- To use a peer as a DNS server, specify its WireGuard tunnel's IP address(es) in the .network file using the option. For search domains use the option. See for details.
- To use a peer as the only DNS server, then in the .network file's section set and add
~.
to option. - To route additional subnets add them as sections in the .network file. For example:
Peer B setup:
Peer C setup:
/etc/systemd/network/99-wg0.network
[Match] Name=wg0 [Network] Address=10.0.0.3/24 Address=fdc9:281f:04d7:9ee9::3/64
systemd-networkd: routing all traffic over WireGuard
In this example Peer B connects to peer A with public IP address. Peer B routes all its traffic over WireGuard tunnel and uses Peer A for handling DNS requests.
Peer A setup
Peer B setup:
/etc/systemd/network/99-wg0.netdev
[NetDev] Name=wg0 Kind=wireguard Description=WireGuard tunnel wg0 [WireGuard] ListenPort=51902 PrivateKey=''PEER_B_PRIVATE_KEY'' FirewallMark=0x8888 [WireGuardPeer] PublicKey=''PEER_A_PUBLIC_KEY'' PresharedKey=''PEER_A-PEER_B-PRESHARED_KEY'' AllowedIPs=0.0.0.0/0 Endpoint=198.51.100.101:51871
Exempting specific addresses
In order to exempt specific addresses (such as private LAN addresses) from routing over the WireGuard tunnel, add them to a higher-priority RoutingPolicyRule than the one that was just created. This will configure them to use the default routing table, and prevent them from using the WireGuard table.
Netctl
Netctl has native support for setting up WireGuard interfaces. A typical set of WireGuard netctl profile configuration files would look like this:
Peer A setup:
Peer B setup:
/etc/wireguard/wg0.conf
[Interface] ListenPort = 51902 PrivateKey = ''PEER_B_PRIVATE_KEY'' [Peer] PublicKey = ''PEER_A_PUBLIC_KEY'' AllowedIPs = 10.0.0.1/32 Endpoint = peer-a.example:51871 [Peer] PublicKey = ''PEER_C_PUBLIC_KEY'' AllowedIPs = 10.0.0.3/32
Peer C setup:
Then start and/or enable wg0 interface on every participating peer as needed, i.e.
# netctl start wg0
To implement persistent site-to-peer, peer-to-site or site-to-site type of connection with WireGuard and Netctl, just add appropriate line into the netctl profile configuration file and add this network to in the WireGuard profile, e.g. in the /etc/netctl/wg0
and in and then do not forget to enable IP forwarding.
NetworkManager
NetworkManager has native support for setting up WireGuard interfaces. For all details about WireGuard usage in NetworkManager, read Thomas Haller's blog post—WireGuard in NetworkManager.
The following examples configure WireGuard via the keyfile format .nmconnection files. See and for an explanation on the syntax and available options.
Peer A setup:
/etc/NetworkManager/system-connections/wg0.nmconnection
[connection] id=wg0 type=wireguard interface-name=wg0 [wireguard] listen-port=51871 private-key=''PEER_A_PRIVATE_KEY'' private-key-flags=0 [wireguard-peer.''PEER_B_PUBLIC_KEY''] endpoint=peer-b.example:51902 preshared-key=''PEER_A-PEER_B-PRESHARED_KEY'' preshared-key-flags=0 allowed-ips=10.0.0.2/32;fdc9:281f:04d7:9ee9::2/128; [wireguard-peer.''PEER_C_PUBLIC_KEY''] preshared-key=''PEER_A-PEER_C-PRESHARED_KEY'' preshared-key-flags=0 allowed-ips=10.0.0.3/32;fdc9:281f:04d7:9ee9::3/128; [ipv4] address1=10.0.0.1/24 method=manual [ipv6] address1=fdc9:281f:04d7:9ee9::1/64 method=manual
- To route all traffic through the tunnel to a specific peer, add the default route ( for IPv4 and
::/0
for IPv6) to . E.g. . Special handling of the default route in WireGuard connections is supported since NetworkManager 1.20.0. - To use a peer as a DNS server, specify its WireGuard tunnel's IP address(es) with the and
ipv6.dns
settings. Search domains can be specified with the and options. See for more details. For example, using the keyfile format:
To use a peer as the only DNS server, set a negative DNS priority (e.g. ) and add ~.
to the settings.
Peer B setup:
Peer C setup:
/etc/NetworkManager/system-connections/wg0.nmconnection
[connection] id=wg0 type=wireguard interface-name=wg0 [wireguard] listen-port=51993 private-key=''PEER_C_PRIVATE_KEY'' private-key-flags=0 [wireguard-peer.''PEER_A_PUBLIC_KEY''] endpoint=198.51.100.101:51871 preshared-key=''PEER_A-PEER_C-PRESHARED_KEY'' preshared-key-flags=0 allowed-ips=10.0.0.1/32;fdc9:281f:04d7:9ee9::1/128; [wireguard-peer.''PEER_B_PUBLIC_KEY''] endpoint=peer-b.example:51902 preshared-key=''PEER_B-PEER_C-PRESHARED_KEY'' preshared-key-flags=0 allowed-ips=10.0.0.2/32;fdc9:281f:04d7:9ee9::2/128; [ipv4] address1=10.0.0.3/24 method=manual [ipv6] address1=fdc9:281f:04d7:9ee9::3/64 method=manual
Specific use-case: VPN server
The purpose of this section is to set up a WireGuard "server" and generic "clients" to enable access to the server/network resources through an encrypted and secured tunnel like OpenVPN and others. The "server" runs on Linux and the "clients" can run on any number of platforms (the WireGuard Project offers apps on both iOS and Android platforms in addition to Linux, Windows and MacOS). See the official project install link for more.
Server
On the peer that will act as the "server", first enable IPv4 forwarding using sysctl:
# sysctl -w net.ipv4.ip_forward=1
To make the change permanent, add to .
A properly configured firewall is HIGHLY recommended for any Internet-facing device.
If the server has a public IP configured, be sure to:
- Allow UDP traffic on the specified port(s) on which WireGuard will be running (for example allowing traffic on
51820/UDP
). - Setup the forwarding policy for the firewall if it is not included in the WireGuard configuration for the interface itself . The example below should have the iptables rules and work as-is.
If the server is behind NAT, be sure to forward the specified port(s) on which WireGuard will be running (for example, 51820/UDP
) from the router to the WireGuard server.
Key generation
Generate key pairs for the server and for each client as explained in #Key generation.
Server configuration
Create the "server" configuration file:
Additional peers ("clients") can be listed in the same format as needed. Each peer requires the to be set. However, specifying is optional.
Notice that the has a netmask of /24
and the clients on /32
. The clients only use their IP and the server only sends back their respective address.
The interface can be managed manually using or using a systemd service managed via .
The interface may be brought up using respectively by starting and potentially enabling the interface via , e.g. . To close the interface use respectively stop .
Client configuration
Create the corresponding "client" configuration file(s):
foo.conf
[Interface] Address = 10.200.200.2/32 PrivateKey = ''PEER_FOO_PRIVATE_KEY'' DNS = 10.200.200.1 [Peer] PublicKey = ''SERVER_PUBLICKEY'' PresharedKey = ''PRE-SHARED_KEY'' Endpoint = my.ddns.example.com:51820 AllowedIPs = 0.0.0.0/0, ::/0
Using the catch-all will forward all IPv4 () and IPv6 (::/0
) traffic over the VPN.
Testing the tunnel
Once a tunnel has been established, one can use netcat to send traffic through it to test out throughput, CPU usage, etc.
On one side of the tunnel, run in listen mode and on the other side, pipe some data from /dev/zero
into in sending mode.
In the example below, port 2222 is used for the traffic (be sure to allow traffic on port 2222 if using a firewall).
On one side of the tunnel listen for traffic:
$ nc -vvlnp 2222
On the other side of the tunnel, send some traffic:
$ dd if=/dev/zero bs=1024K count=1024 | nc -v 10.0.0.203 2222
Status can be monitored using directly.
Tips and tricks
Store private keys in encrypted form
It may be desirable to store private keys in encrypted form, such as through use of . Just replace the PrivateKey line under [Interface] in the configuration file with:
PostUp = wg set %i private-key <(su user -c "export PASSWORD_STORE_DIR=/path/to/your/store/; pass WireGuard/private-keys/%i")
where user is the Linux username of interest. See the man page for more details.
Endpoint with changing IP
After resolving a server's domain, WireGuard will not check for changes in DNS again.
If the WireGuard server is frequently changing its IP-address due DHCP, Dyndns, IPv6, etc., any WireGuard client is going to lose its connection, until its endpoint is updated via something like .
Also be aware, if the endpoint is ever going to change its address (for example when moving to a new provider/datacenter), just updating DNS will not be enough, so periodically running reresolve-dns might make sense on any DNS-based setup.
Luckily, wireguard-tools provides an example script , that parses WG configuration files and automatically resets the endpoint address.
One needs to run the /usr/share/wireguard-tools/examples/reresolve-dns/reresolve-dns.sh /etc/wireguard/wg.conf
periodically to recover from an endpoint that has changed its IP.
One way of doing so is by updating all WireGuard endpoints once every thirty seconds via a systemd timer:
Generate QR code
If the client is a mobile device such as a phone, can be used to generate client's configuration QR code and display it in terminal:
$ qrencode -t ansiutf8 -r client.conf
Enable debug logs
When using the Linux kernel module on a kernel that supports dynamic debugging, debugging information can be written into the kernel ring buffer (viewable with dmesg and journalctl) by running:
# modprobe wireguard # echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
Reload peer (server) configuration
In case the WireGuard peer (mostly server) adding or removing another peers from its configuration and wants to reload it without stopping any active sessions, one can execute the following command to do it:
# wg syncconf ${WGNET} <(wg-quick strip ${WGNET})
Where $WGNET
is WireGuard interface name or configuration base name, for example (for server) or (without the .conf extension, for client).
Troubleshooting
Routes are periodically reset
Users of NetworkManager should make sure that it is not managing the WireGuard interface(s). For example, create the following configuration file:
Broken DNS resolution
When tunneling all traffic through a WireGuard interface, the connection can become seemingly lost after a while or upon new connection. This could be caused by a network manager or DHCP client overwriting .
By default wg-quick uses resolvconf to register new DNS entries (from the keyword in the configuration file). This will cause issues with network managers and DHCP clients that do not use resolvconf, as they will overwrite thus removing the DNS servers added by wg-quick.
The solution is to use networking software that supports resolvconf.
Users of NetworkManager should know that it does not use resolvconf by default. It is recommended to use systemd-resolved. If this is undesirable, install and configure NetworkManager to use it: NetworkManager#Use openresolv.
Low MTU
Due to too low MTU (lower than 1280), wg-quick may have failed to create the WireGuard interface. This can be solved by setting the MTU value in WireGuard configuration in Interface section on client.
Key is not the correct length or format
To avoid the following error, put the key value in the configuration file and not the path to the key file.
Unable to establish a persistent connection behind NAT / firewall
By default, WireGuard peers remain silent while they do not need to communicate, so peers located behind a NAT and/or firewall may be unreachable from other peers until they reach out to other peers themselves (or the connection may time out). Adding to the settings of a peer located behind a NAT and/or firewall can ensure that the connection remains open.
# Set the persistent-keepalive via command line (temporarily)
[#] wg set wg0 peer $PUBKEY persistent-keepalive 25
Loop routing
Adding the endpoint IP to the allowed IPs list, the kernel will attempt to send handshakes to said device binding, rather than using the original route. This results in failed handshake attempts.
As a workaround, the correct route to the endpoint needs to be manually added using
ip route add <endpoint ip> via <gateway> dev <network interface>
e.g. for peer B from above in a standard LAN setup:
ip route add 203.0.113.102 via 192.168.0.1 dev eth0
To make this route persistent, the command can be added as to the section of . However, on certain setups (e.g. using in combination with NetworkManager) this might fail on resume. Furthermore, this only works for a static network setup and fails if gateways or devices change (e.g. using ethernet or wifi on a laptop).
Using NetworkManager, a more flexible solution is to start WireGuard using a dispatcher script. As root, create If not already running, start and enable . Also, make sure that NetworkManager is not managing routes for (see above).