Wireguard is a free and libre software that allows to set up virtual private networks (VPN).
Wireguard adds virtual interfaces that manage encrypted traffic to and from other wireguard interfaces. Physically the traffic is sent via UDP datagrams, but applications treat the wireguard interfaces as any other interface.
By design, wireguard has a minimal set of features leaving many details in the hand of the user. In particular: key generation, key exchange, and to set up the routing.
Wireguard interfaces are set up with configuration files that uses a ini syntax. In the ini file is a section called [Interface]
that describes the interface itself and one or more [Peer]
sections that describe from who can be reached and how.
Each interface comprises a asymmetrical key, the private key never leaves the computer hosting the interface, the public key is the main identification of each peer.
The connection between two interfaces is also protected by symmetric encryption. So each pair of wireguard interfaces need an extra key known to to both parties.
wg genkey
generates the private key, wg pubkey
generates its relative public key. (umask
ensures that the keys are readable only by the user):
$ ( umask 0077; wg genkey | tee PrivateKey.key | wg pubkey > PublicKey.pub )
wg genkey
by itself can be used for the symmetric key.
$ ( umask 0077; wg genkey > PresharedKey.key )
Virtual Private Networks, as the name suggest, use IPs in the private space. There are multiple spaces and Wireguard supports both IP6 and IP4, but for most purposes to use the IP addresses in the 10.0.0.0/8
block is sufficient.
Here is an example of an [Interface]
section of a Wireguard interface configuration.
[Interface] PrivateKey = yBK+IcuZ2XBaghdJZfH551Veo8T/JXl48XJdMYjrQ0c= #PublicKey = NYsczmRiSV+aekAoTs6uKA+CcXHmxJVLS6gRNpIQ3yM= Address = 10.11.12.100/32 DNS = 1.1.1.1 ListenPort = 51820 MTU=1368
The private key identifies the interface, the public key is only a comment to have it handy. The DNS is arbitrary, but 1.1.1.1
is a easy-to-remember DNS service from Cloudfare.
The IP line is important; the value in CIDR notation is used both to identify the interface (the IP address) and for routing (the IP range). For example, /32
means the interface won't do any routing, /24
means the interface will route traffic to the addresses in the block from 10.11.12.0
to 10.11.12.255
.
The peer sections are similar:
[Peer] PublicKey = zqGcoTNCPbxx0rKxI3iH1ImV+KE1wGIroc6qts51Rzk= PresharedKey = OKrM0kinDG5pUb/GwB1/yDC/X2e2lmPi8mpo/7HcjlE= Endpoint = 1.2.3.4:51820 AllowedIPs = 10.11.12.0/24 PersistentKeepalive = 25
For Peers the AllowedIPs
section is just for routing. The Endpoint
value is used to reach the machine running the wireguard interface; it can omitted if it is expected that the machine will be reached from outside first (i.e., functions as a server).
For firewalling a machine running Wireguard needs to be able to receive UDP datagrams in the 51820
port (or whence the physical interface is listening) and allow traffic to the Wireguard interface.
Besides, if routing is necessary (i.e., the [Interface] Address
section is not a single IP) forward traffic should be allowed.
Here is an example, the relevant lines are indented:
# iptables-save *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate INVALID -j DROP -A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT -A INPUT -i wg0 -j ACCEPT -A INPUT -p udp -m udp --dport 51820 -j ACCEPT -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable -A INPUT -p tcp -j REJECT --reject-with tcp-reset -A INPUT -j REJECT --reject-with icmp-proto-unreachable -A FORWARD -i wg0 -o wg0 -j ACCEPT COMMIT
Forwarding needs to be enabled at kernel level.
# sysctl -w net.ipv4.ip_forward=1
If IPv6 is used:
# sysctl -w net.ipv6.conf.all.forwarding=1
The program to create or destroy the interfaces is wg-quick
with the verb up
to create and down
to destroy the interface.
If the configuration file is in /etc/wireguard
you can just use the interface name, otherwise you need to give the full path.
Finally wg
executed by itself returns the current state of the wireguard interfaces.