Unbound, as well as a script for automating the process of adding new clients.
The Arch Linux Wiki has an excellent guide for configuring WireGuard. I opted to use wg-quick
for persistent configuration management.
Note that this configuration is IPv4-only, but can be extended to support IPv6.
This example assumes that clients live within the address space 10.0.0.2
through 10.0.0.254
.
The WireGuard server configuration (e.g. /etc/wireguard/wg0.conf
) looks like this:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <Server Private Key>
[Peer]
PublicKey = <Client A Public Key>
PresharedKey = <Client A Pre-Shared key>
AllowedIPs = 10.0.0.20/32
[Peer]
...
Because my setup uses nftables, I opted to statically configure it rather than supply PreUp
/PostUp
/PreDown
/PostDown
hooks in the server configuration.
To create a new client, the following steps must be performed:
Generate a private key (wg genkey
) and pre-shared key (wg genpsk
) for that client.
Create a client configuration file (which should be securely sent to that client, e.g. with qrencode).
Add the client’s pre-shared key and public key (wg genpub
) to the server configuration.
For a full-tunnel configuration (all traffic is routed through the VPN), the client configuration should look something like this:
[Interface]
Address = 10.0.0.20/24
PrivateKey = <Client Private Key>
DNS = 10.0.0.1
[Peer]
# server
Endpoint = <VPN Server Address>:51820
PublicKey = <Client Public Key>
PresharedKey = <Client Pre-Shared Key>
AllowedIPs = 0.0.0.0/0
For a split-tunnel configuration (only traffic destined for other clients in the VPN network is routed through the VPN), AllowedIPs
can instead be set to 10.0.0.0/24
.
Here is a simple script that creates both split- and full-tunnel configurations for a new client and adds that client as a peer to the server configuration. It should be run as root
, since that is the only user who should be allowed to access the /etc/wireguard
directory.
Configuring nftables is a matter of:
Opening the port that WireGuard listens on
Allowing WireGuard clients to make DNS requests
Forwarding client traffic to the internet
Masquerading that traffic so that it looks like it comes from the server
Allowing clients to access a restricted set of other clients
I followed this guide to configure nftables.
The following definitions are relevant for the changes to /etc/nftables.conf
:
define WAN_IFC = ens0
define VPN_IFC = wg0
define VPN_NET = 10.0.0.0/24
define VPN_SERVICES = { 10.0.0.20, 10.0.0.21 }
The following lines open WireGuard’s port (51820 UDP) to anyone and DNS to only the VPN interface from an IP address within the VPN network. It’s probably possible to limit the latter by only one of those two qualifiers.
table inet filter {
...
chain input {
type filter hook input priority 0; policy drop;
...
udp dport 51820 accept comment "Allow VPN"
iifname $VPN_IFC udp dport 53 ip saddr $VPN_NET accept comment "Allow DNS for VPN"
}
}
The addition below to the forward
chain forwards traffic from clients to the internet. The addition to the postrouting
chain makes the traffic look as though it’s coming from the server.
table inet filter {
...
chain forward {
type filter hook forward priority 0; policy drop;
...
# forward WireGuard traffic, allowing it to access internet via WAN
iifname $VPN_IFC oifname $WAN_IFC ct state new accept
}
}
table ip router {
# both prerouting and postrouting must be specified
chain prerouting {
type nat hook prerouting priority 0;
}
chain postrouting {
type nat hook postrouting priority 100;
# masquerade wireguard traffic
# make wireguard traffic look like it comes from the server itself
oifname $WAN_IFC ip saddr $VPN_NET masquerade
}
}
If some clients provide services that other clients might want to use (e.g. mail server, web server, etc.), nftables needs one more line of configuration to forward traffic appropriately. Accessible clients are defined by the VPN_SERVICES
set
table inet filter {
...
chain forward {
type filter hook forward priority 0; policy drop;
...
# allow all clients to access those in the $VPN_SERVICES whitelist
iifname $VPN_IFC oifname $VPN_IFC ip daddr $VPN_SERVICES ct state new accept
}
}
The server must enable packet forwarding. One can do this by creating a file in /etc/sysctl.d/
with the contents:
net.ipv4.ip_forward = 1
Analogous settings for IPv6 implementations also exist.
Unbound works nearly out of the box. At a minimum, the VPN IP address should be added to the interfaces to listen on and access should be allowed to clients:
server:
...
interface: 10.0.0.1
access-control: 10.0.0.0/24 allow
As usual, the Arch Wiki has a great guide on setting it up for more complex usages. In particular, the private-domain
configuration feature can be used to set up an intranet of sorts.