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.
- 1 Installation
- 2 Usage
- 3 Specific use-case: VPN server
- 4 Testing the tunnel
- 5 Troubleshooting
- 6 Tips and tricks
- 7 See also
- Install .
- Install the appropriate kernel module:
- for the default kernel.
- for the LTS kernel.
- kernels. for the DKMS variant for other
The below commands demonstrate how to setup a basic tunnel between two peers with the following settings:
|Peer A||Peer B|
|External IP address||198.51.100.101||203.0.113.102|
|Internal IP address||10.0.0.1/24||10.0.0.2/24|
|WireGuard listening port||UDP/51871||UDP/51902|
The external addresses should already exist. For example, peer A should be able to ping peer B via
ping 203.0.113.102, and vice versa. The internal addresses will be new addresses created by the commands below and will be shared internally within the new WireGuard network using . The
/24 in the IP addresses is the CIDR.
To create a private key:
$ wg genkey > privatekey
$ chmod 600 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 generate a preshared key to add an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance.
# wg genpsk > preshared
Peer A setup
This peer will listen on UDP port 51871 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 10.0.0.1/24 dev wg0 # wg set wg0 listen-port 51871 private-key ./privatekey # wg set wg0 peer PEER_B_PUBLIC_KEY persistent-keepalive 25 allowed-ips 10.0.0.2/32 endpoint 203.0.113.102:51902 # ip link set wg0 up
PEER_B_PUBLIC_KEY should have the same format as
EsnHH9m6RthHSs+sd9uM6eCHe/mMVFaRh93GYadDDnM=. The keyword
allowed-ips is a list of addresses that peer A will be able to send traffic to;
allowed-ips 0.0.0.0/0 would allow sending traffic to any IPv4 address,
::/0 allows sending traffic to any IPv6 address.
Peer B setup
As with peer A, whereas the wireguard daemon is listening on the UDP port 51902 and accept connection from peer A only.
# ip link add dev wg0 type wireguard # ip addr add 10.0.0.2/24 dev wg0 # wg set wg0 listen-port 51902 private-key ./privatekey # wg set wg0 peer PEER_A_PUBLIC_KEY persistent-keepalive 25 allowed-ips 10.0.0.1/32 endpoint 198.51.100.101:51871 # ip link set wg0 up
Invoking thecommand 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:
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
At this point one could reach the end of the tunnel:
[user@peer-a]$ ping 10.0.0.2
The configuration can be saved by utilizing
# wg showconf wg0 > /etc/wireguard/wg0.conf # wg setconf wg0 /etc/wireguard/wg0.conf
Example peer configuration
[Interface] PrivateKey = CLIENT_PRIVATE_KEY [Peer] PublicKey = SERVER_PUBLICKEY AllowedIPs = 10.0.0.0/24, 10.123.45.0/24, 1234:4567:89ab::/48 Endpoint = SERVER_ENDPOINT:51871 PersistentKeepalive = 25
Example configuration for systemd-networkd
Specific use-case: 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 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.
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
net.ipv4.ip_forward = 1 to
A properly configured firewall is HIGHLY recommended for any Internet-facing device.
If the server have the 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 config for the interface itself
/etc/wireguard/wg0.conf. 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.
Generate key pairs for the server and for each client as explained in #Key generation.
Create the "server" config file:
[Interface] Address = 10.200.200.1/24 ListenPort = 51820 PrivateKey = SERVER_PRIVATE_KEY # note - substitute eth0 in the following lines to match the Internet-facing interface # if the server is behind a router and receive traffic via NAT, this iptables rules are not needed 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 [Peer] # foo PublicKey = PEER_FOO_PUBLIC_KEY PresharedKey = PRE-SHARED_KEY AllowedIPs = 10.200.200.2/32 [Peer] # bar PublicKey = PEER_BAR_PUBLIC_KEY AllowedIPs = 10.200.200.3/32
Additional peers ("clients") can be listed in the same format as needed. Each peer requires the
PublicKey to be set. However, specifying
PresharedKey is optional.
Notice that the
Address have mask "/24" and the clients on
AllowedIPs "/32". The client only use their IP and the server only send back their respective address.
The interface can be managed manually using systemd service managed via .or using a
The interface may be brought up using
wg-quick up wg0 respectively by starting and potentially enabling the interface via
email@example.com. To close the interface use
wg-quick down wg0 respectively stop
Create the corresponding "client" config file(s):
[Interface] Address = 10.200.200.2/24 PrivateKey = PEER_FOO_PRIVATE_KEY DNS = 10.200.200.1 [Peer] PublicKey = SERVER_PUBLICKEY PresharedKey = PRE-SHARED_KEY AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = my.ddns.example.com:51820
[Interface] Address = 10.200.200.3/24 PrivateKey = PEER_BAR_PRIVATE_KEY DNS = 10.200.200.1 [Peer] PublicKey = SERVER_PUBLICKEY PresharedKey = PRE-SHARED KEY AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = my.ddns.example.com:51820
Using the catch-all
AllowedIPs = 0.0.0.0/0, ::/0 will forward all IPv4 (
0.0.0.0/0) and IPv6 (
::/0) traffic over the VPN.
NetworkManager-wait-online.serviceand users of systemd-networkd may need to enable the
systemd-networkd-wait-online.serviceto wait until devices are network ready before attempting wireguard connection.
Testing the tunnel
Once a tunnel has been established, one can use
nc in listen mode and on the other side, pipe some data from
nc 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
interface: wg0 public key: UguPyBThx/+xMXeTbRYkKlP0Wh/QZT3vTLPOVaaXTD8= private key: (hidden) listening port: 51820 peer: 9jalV3EEBnVXahro0pRMQ+cHlmjE33Slo9tddzCVtCw= preshared key: (hidden) endpoint: 192.168.1.216:53207 allowed ips: 10.0.0.0/0 latest handshake: 1 minutes, 17 seconds ago transfer: 56.43 GiB received, 1.06 TiB sent
Routes are periodically reset
If you are not configuring WireGuard from NetworkManager, make sure that NetworkManager is not managing the WireGuard interface(s):
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
DNS keyword in the configuration file). This will cause issues with network managers and DHCP clients that do not use resolvconf, as they will overwrite
/etc/resolv.conf thus removing the DNS servers added by wg-quick.
The solution is to use networking software that supports resolvconf.
In case of NetworkManager, it does not use resolvconf by default. This will not be an issue when using systemd-resolved, but if you do not use systemd-resolved, install and configure NetworkManager to use it: NetworkManager#Use openresolv.
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.
[Interface] Address = 10.200.200.2/24 MTU = 1500 PrivateKey = PEER_FOO_PRIVATE_KEY DNS = 10.200.200.1
Tips and tricks
systemd-networkd has native support for WireGuard protocols and therefore does not require the package.
In order to prevent leak of private keys, it is recommended to set the permissions of the .netdev file:
# chown root:systemd-network /etc/systemd/network/99-*.netdev # chmod 0640 /etc/systemd/network/99-*.netdev
[NetDev] Name = wg0 Kind = wireguard Description = WireGuard [WireGuard] ListenPort = 51820 PrivateKey = SERVER_PRIVATE_KEY [WireGuardPeer] PublicKey = PEER_FOO_PUBLIC_KEY PresharedKey = PRE-SHARED_KEY AllowedIPs = 10.200.200.2/32 [WireGuardPeer] PublicKey = PEER_BAR_PUBLIC_KEY PresharedKey = PRE-SHARED_KEY AllowedIPs = 10.200.200.3/32
[Match] Name = wg0 [Network] Address = 10.200.200.1/32 [Route] Gateway = 10.200.200.1 Destination = 10.200.200.0/24
[NetDev] Name = wg0 Kind = wireguard Description = WireGuard [WireGuard] PrivateKey = FOO_PRIVATE_KEY [WireGuardPeer] PublicKey = SERVER_PUBLICKEY PresharedKey = PRE-SHARED_KEY AllowedIPs = 10.200.0.0/24 Endpoint = my.ddns.example.com:51820 PersistentKeepalive = 25
[Match] Name = wg0 [Network] Address = 10.200.200.2/32 [Route] Gateway = 10.200.200.1 Destination = 10.200.200.0/24 GatewayOnlink=true
[NetDev] Name = wg0 Kind = wireguard Description = WireGuard [WireGuard] PrivateKey = PEER_BAR_PRIVATE_KEY [WireGuardPeer] PublicKey = SERVER_PUBLICKEY PresharedKey = PRE-SHARED_KEY AllowedIPs = 10.200.0.0/24 Endpoint = my.ddns.example.com:51820 PersistentKeepalive = 25
[Match] Name = wg0 [Network] Address = 10.200.200.3/32 [Route] Gateway = 10.200.200.1 Destination = 10.200.200.0/24 GatewayOnLink=true
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 theman 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, ..., any WireGuard client is going to lose its connection, until its endpoint is updated via something like
wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT".
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.
/usr/share/wireguard/examples/reresolve-dns/reresolve-dns.sh, that parses WG configuration files and automatically resets the endpoint address.
One needs to run the
/usr/share/wireguard/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:
[Unit] Description=Periodically reresolve DNS of all WireGuard endpoints [Timer] OnCalendar=*:*:0/30 [Install] WantedBy=timers.target
[Unit] Description=Reresolve DNS of all WireGuard endpoints Wants=network-online.target After=network-online.target [Service] Type=oneshot ExecStart=/bin/sh -c 'for i in /etc/wireguard/*.conf; do /usr/share/wireguard/examples/reresolve-dns/reresolve-dns.sh "$i"; done'
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 < client.conf