From ArchWiki
Revision as of 15:19, 6 December 2018 by Insecuriy (talk | contribs) (Endpoint with changing IP: major bug fix (timer didn't schedule))
Jump to navigation Jump to search

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 plans to be cross-platform and widely deployable.
Warning: WireGuard has not undergone proper degrees of security auditing and the protocol is still subject to change.[1]


Install the wireguard-tools package.

Note: WireGuard is not yet mainlined and the required kernel module will be built using DKMS. As such, be sure to have the corresponding kernel-headers package installed.

Raw usage

Below commands will demonstrate how to setup a basic tunnel between two peers with the following settings:

Peer A Peer B
External IP address
Internal IP address
wireguard listening port UDP/48574 UDP/39814

The external addresses should already exist. For example, peer A should be able to ping peer B via ping, and vice versa. The internal addresses will be new addresses created by the ip commands below and will be shared internally within the new WireGuard network. The /24 in the IP addresses is the CIDR.

Key generation

Note: It's recommended to save the private key file with strict permissions such as 600.

To create a private key:

$ wg genkey > privatekey

To create a public key:

$ wg pubkey < privatekey > publickey

Alternatively, do this all at once:

$ wg genkey | tee privatekey | wg pubkey > publickey

One can also create a pre-shared key for added security.

# wg genpsk > preshared

Peer A setup

This peer will listen on UDP port 48574 and will accept connection from peer B by linking its public key with both its inner and outer IPs addresses.

# ip link add dev wg0 type wireguard
# ip addr add dev wg0
# wg set wg0 listen-port 48574 private-key ./privatekey
# wg set wg0 peer [Peer B public key] persistent-keepalive 25 allowed-ips endpoint
# ip link set wg0 up

[Peer B public key] should have the same format as EsnHH9m6RthHSs+sd9uM6eCHe/mMVFaRh93GYadDDnM=. allowed-ips is a list of addresses that peer A will be able to send traffic to. allowed-ips would allow sending traffic to any address.

Peer B setup

As with Peer A, whereas the wireguard daemon is listening on the UDP port 39814 and accept connection from peer A only.

# ip link add dev wg0 type wireguard
# ip addr add dev wg0
# wg set wg0 listen-port 39814 private-key ./privatekey
# wg set wg0 peer [Peer A public key] persistent-keepalive 25 allowed-ips endpoint
# ip link set wg0 up

Basic checkups

Invoking the wg command without parameter 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:

 peer-a$ wg
 interface: wg0
   public key: UguPyBThx/+xMXeTbRYkKlP0Wh/QZT3vTLPOVaaXTD8=
   private key: (hidden)
   listening port: 48574
 peer: 9jalV3EEBnVXahro0pRMQ+cHlmjE33Slo9tddzCVtCw=
   allowed ips:

At this point one could reach the end of the tunnel:

 peer-a$ ping

Persistent configuration

The configuration can be saved by utilizing showconf:

# wg showconf wg0 > /etc/wireguard/wg0.conf
# wg setconf wg0 /etc/wireguard/wg0.conf

Example peer configuration

Address =

AllowedIPs =,, 1234:4567:89ab::/48
Endpoint = [SERVER ENDPOINT]:51820
PersistentKeepalive = 25

Example configuration for systemd-networkd

Name = wg0
Kind = wireguard
Description = Wireguard


PresharedKey = [PRE SHARED KEY]
AllowedIPs =
Endpoint = [SERVER ENDPOINT]:51820
PersistentKeepalive = 25
Name = wg0

Address =

Gateway =
Destination =

Endpoint with changing IP

After resolving a server's domain, WireGuard will not check for changes in DNS again.

If your WireGuard server is frequently changing it's IP-address due DHCP, Dyndns, IPv6, ..., any WireGuard client is going to lose it's connection, until you update it's endpoint via something like wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT".

Also be aware, if your endpoint is ever going to change it's address (for example when moving to a new provider/datacenter), just updating DNS won't be enough, so periodically running reresolve-dns might make sense on any DNS-based setup.

Luckily, WireGuard provides an example script reresolve-dns.sh, that parses WG configuration files and automatically resets the endpoint address.

You can obtain the script from WireGuard.com git or from wireguard-tools package located at /usr/share/wireguard/examples/reresolve-dns/reresolve-dns.sh on ArchLinux or /usr/share/doc/wireguard-tools/examples/reresolve-dns/reresolve-dns.sh on Debian.

You just have to run the script as reresolve-dns.sh /etc/wireguard/wg.conf periodically to recover from an endpoint that has changed it's IP.

One way of doing so is by updating all WireGuard endpoints once every thirty seconds* via a systemd timer.

* suggested by the README of reresolve-dns.sh

bash script for simple setup from your CLI
sudo tee /etc/systemd/system/wireguard_reresolve-dns.timer <<EOF
Description=Periodically reresolve DNS of all WireGuard endpoints



sudo tee /etc/systemd/system/wireguard_reresolve-dns.service <<EOF
Description=Reresolve DNS of all WireGuard endpoints

ExecStart=/bin/sh -c 'for i in /etc/wireguard/*.conf; do "\$reresolve" "\$i"; done'

sudo systemctl daemon-reload
sudo systemctl start wireguard_reresolve-dns.timer
sudo systemctl enable wireguard_reresolve-dns.timer

Specific use-case: setup a VPN server

The purpose of this section is to setup 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 any any number of platforms (the WireGuard Project offers apps on both iOS and Android platforms in addition to Linux-native and MacOS). See the official project install link for more.


On the machine acting as the server, first enable IPv4 forwarding:

# sysctl net.ipv4.ip_forward=1

To make the change permanent, add net.ipv4.ip_forward = 1 to /etc/sysctl.d/99-sysctl.conf.

A properly configured firewall is HIGHLY recommended for any Internet-facing device. Be sure to:

  • Allow UDP traffic on the specified port(s) on which WireGuard will be running (for example allowing traffic on 51820/udp).
  • Setting up the forwarding policy for the firewall if it is not included in the WireGuard config for the interface itself /etc/wireguard/wg0.conf. The example below should work as-is.

Finally, WireGuard port(s) need to be forwarded to the server from the network router so they can be accessed from the WAN.

Key generation

Generate public/private key pairs for the server and for each client as explained in #Key generation. Optionally, generate a preshared key for all peers to share.

Server config

Create the server config file:

Note: The PresharedKey line is optional. If retained, the server and each client are required to have it in their respective config files. See the wg manpage for more.
Address =
SaveConfig = true
ListenPort = 51820

# note - substitute eth0 in the following lines to match the Internet-facing interface
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# client foo
PublicKey = [FOO's PUBLIC KEY]
PresharedKey = [PRE-SHARED KEY]
AllowedIPs =

# client bar
PublicKey = [BAR's PUBLIC KEY]
PresharedKey = [PRE-SHARED KEY]
AllowedIPs =
Note: Additional peers can be listed in the same format as needed.

The interface can be managed manually or by systemctl.

For example: bring the interface up by using wg-quick up wg0 and bring it down by using wg-quick down wg0.

Alternatively, systemctl can be used to manage the interface. Start and optionally enable it via wg-quick@.service where the server config name is inserted after the "@" symbol. For example:

# systemctl start wg-quick@wg0

Client config

Create the corresponding client config file(s):

Address =
PrivateKey = [FOO's PRIVATE KEY]

PresharedKey = [PRE-SHARED KEY]
AllowedIPs =
Endpoint = my.ddns.address.com:51820
Address =
PrivateKey = [BAR's PRIVATE KEY]

PresharedKey = [PRE-SHARED KEY]
AllowedIPs =
Endpoint = my.ddns.address.com:51820
Warning: When setting up config files, both the public/private keys and the Address = values MUST to match for both the client and server pairs.

If the client is a mobile device such as a phone, qrencode can be used to share the config with the client:

$ qrencode -t ansiutf8 < foo.conf
Note: Users of NetworkManager, may need to enable the NetworkManager-wait-online.service and users of systemd-networkd may need to enable the systemd-networkd-wait-online.service to wait until devices are network ready before attempting wireguard connection.


PresharedKey bug with iOS client

The iOS client (v0.0.20181104) has a bug where config files containing a PresharedKey field fail to get imported correctly via QR codes. All the other fields are successfully transferred except for the PresharedKey. One can manually edit the imported file and type it resulting in a functional config.

Routes are periodically reset

Make sure that NetworkManager is not managing your Wireguard interface:


Connection loss with NetworkManager

On desktop, connection loss can be experienced when all the traffic is tunnelled through a Wireguard interface: typically, the connection is seemingly lost after a while or upon new connection to an access point.

By default wg-quick uses a resolvconf provider such as openresolv to register new DNS entries (i.e. DNS keyword in the configuration file). However NetworkManager does not use resolvconf by default: every time a new DHCP lease is acquired, NetworkManager overwrites the global DNS addresses with the DHCP-provided ones which might not be available through the tunnel.

Using resolvconf

If resolvconf is already used by the system and connection losses persist, make sure NetworkManager is configured to use it: NetworkManager#Use openresolv.

Using dnsmasq

See Dnsmasq#openresolv for configuration.

Using systemd-resolved

At the time of writing (Sept. 2018), the resolvconf-compatible mode offered by systemd-resolvconf does not work with wg-quick. However systemd-resolved can still be used by wg-quick through the PostUp hook. First make sure that NetworkManager is configured with systemd-resolved: NetworkManager#systemd-resolved and then alter the tunnel configuration:

Address =  # The client IP from wg0server.conf with the same subnet mask
PostUp = resolvectl domain %i "~."; resolvectl dns %i; resolvectl dnssec %i yes

AllowedIPs =, ::0/0
Endpoint = [SERVER ENDPOINT]:51820
PersistentKeepalive = 25

Setting "~." as a domain name is necessary for systemd-resolved to give priority to the newly available DNS server.

No PostDown key is necessary as systemd-resolved automatically revert all parameters when wg0 is torn down.

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 pass. Just replace the PrivateKey line under [Interface] in your 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 your username. See the wg-quick(8) man page for more details.

See also