WireGuard VPN Client
WireGuard is the right VPN protocol for this network. It is built into the Linux kernel, has minimal attack surface compared to OpenVPN or IPsec, uses modern cryptography by default, and roams cleanly between networks without dropping connections. On Kubuntu it integrates natively with NetworkManager, which means connecting and disconnecting is a single click or keyboard shortcut rather than a terminal command.
This page covers the desktop client configuration. The server-side WireGuard setup, running on the Ubiquiti UniFi routers at each site, is covered in the router section of this series. This page assumes that the server-side configuration is in place and you have a client configuration file ready.
Two use cases
There are two distinct ways you will use WireGuard on the desktop, and they have slightly different configurations.
Roaming client. When the desktop leaves the home network and connects from elsewhere, WireGuard provides a tunnel back to the home network. This gives access to all internal services and all three sites as if you were physically present. This is split-tunnel: only traffic destined for the internal subnets goes through the VPN, and internet traffic uses whatever network you are on.
Site-to-site connectivity. When the desktop is at one of the three sites, WireGuard is already handling inter-site routing at the router level. The desktop does not need its own VPN tunnel in this case: traffic to the other sites routes through the Ubiquiti router automatically. The desktop-level WireGuard configuration is for the roaming case.
Installation
WireGuard is included in the Linux kernel from version 5.6. Kubuntu 24.04 includes it natively. Install the tools and NetworkManager integration:
sudo apt install wireguard wireguard-tools
NetworkManager’s WireGuard support is included by default on Kubuntu 24.04. No additional packages are required.
Generating a key pair
Each WireGuard peer needs a key pair. Generate one for the desktop:
wg genkey | tee /tmp/desktop-private.key | wg pubkey > /tmp/desktop-public.key
Store the private key securely. It should never leave the desktop. The public key is what you give to the WireGuard server (your router) to add this desktop as a permitted peer.
cat /tmp/desktop-public.key
Copy this output and add it to your router’s WireGuard peer configuration. The router section covers the server-side steps.
Move the private key to the WireGuard configuration directory and lock down permissions:
sudo mkdir -p /etc/wireguard
sudo mv /tmp/desktop-private.key /etc/wireguard/desktop-private.key
sudo chmod 600 /etc/wireguard/desktop-private.key
sudo chown root:root /etc/wireguard/desktop-private.key
rm /tmp/desktop-public.key
Client configuration file
Create /etc/wireguard/home.conf. This is the configuration for the tunnel back to Prevernal, which acts as the primary VPN endpoint when roaming:
[Interface]
# Desktop's private key
PrivateKey = <contents of /etc/wireguard/desktop-private.key>
# The VPN address assigned to this desktop
# Allocate from a range set aside for VPN clients in your network design
# For example, a dedicated VPN client subnet within the 10.1.x.x space
Address = 10.1.254.2/32
# Use your internal DNS server when connected
DNS = 10.1.0.1
DNS = yourdomain.net lan
# Optional: restrict the connection to a specific port if your router
# is behind a NAT that maps to a non-standard port
# ListenPort = 51820
[Peer]
# Prevernal's public key (from the router configuration)
PublicKey = <Prevernal's WireGuard public key>
# The router's external IP or hostname and WireGuard port
Endpoint = vpn.yourdomain.net:51820
# Split tunnel: route only internal subnets through the VPN
# Covers all three sites
AllowedIPs = 10.1.0.0/16, 10.2.0.0/16, 10.3.0.0/16
# Keep the tunnel alive through NAT (sends a keepalive every 25 seconds)
PersistentKeepalive = 25
Set correct permissions on the config file:
sudo chmod 600 /etc/wireguard/home.conf
sudo chown root:root /etc/wireguard/home.conf
Importing into NetworkManager
NetworkManager can manage WireGuard connections directly, which integrates it into the system tray and allows on-demand connect/disconnect.
Import the configuration:
sudo nmcli con import type wireguard file /etc/wireguard/home.conf
Verify it was imported:
nmcli con show
A connection named home should appear in the list with type wireguard.
The connection is now accessible from the KDE network applet in the system tray. Find it under VPN connections and toggle it on or off as needed.
Configuring via the KDE Network Manager applet
Alternatively, configure the connection entirely through the GUI. Open the network applet, click the settings icon, and add a new VPN connection of type WireGuard.
The fields map directly to the configuration file:
- Interface name:
wg0or any name - Private key: the contents of
desktop-private.key - IP address:
10.1.254.2/32 - DNS:
10.1.0.1 - Peer public key: Prevernal’s public key
- Allowed IPs:
10.1.0.0/16, 10.2.0.0/16, 10.3.0.0/16 - Endpoint:
vpn.yourdomain.net:51820 - Persistent keepalive:
25
Save and connect.
DNS when connected
The DNS line in the configuration sets the DNS server used when the tunnel is active. NetworkManager adds this as a per-connection DNS override, so while the VPN is connected your internal domain names resolve correctly through the tunnel.
When the VPN disconnects, DNS reverts to whatever Unbound is configured to use, which for external networks is the fallback public resolvers set in the Unbound configuration.
Verify DNS is routing correctly when connected:
resolvectl status
The WireGuard interface should show 10.1.0.1 as its DNS server and yourdomain.net and lan as search domains.
Full tunnel vs split tunnel
The configuration above uses split tunnelling: only the three internal subnets (10.1.0.0/16, 10.2.0.0/16, 10.3.0.0/16) go through the VPN. Internet traffic uses whatever network you are currently on.
For full tunnel, where all traffic including internet goes through the home network, change AllowedIPs to:
AllowedIPs = 0.0.0.0/0, ::/0
Full tunnel is useful when on untrusted networks (public WiFi, hotel networks) and you want all traffic protected. The downside is that internet speed is limited by your home connection’s upload capacity. Split tunnel is the better default for day-to-day use.
Auto-connect on specific networks
If you want the VPN to connect automatically when the desktop is away from the home network but not when it is on the home network, a NetworkManager dispatcher script handles this:
Create /etc/NetworkManager/dispatcher.d/40-wireguard-autoconnect:
#!/usr/bin/env bash
#
# Auto-connect WireGuard VPN when not on the home network.
#
INTERFACE=$1
ACTION=$2
VPN_CONNECTION="home"
# Only act on physical connections, not the VPN interface itself
if [[ "$INTERFACE" == wg* ]]; then
exit 0
fi
case "$ACTION" in
up)
# Get the current subnet
SUBNET=$(nmcli -g IP4.ADDRESS device show "$INTERFACE" 2>/dev/null | \
head -1 | awk -F'.' '{print $1"."$2}')
# If we are on one of the home subnets, do not connect the VPN
if [[ "$SUBNET" == "10.1" ]] || \
[[ "$SUBNET" == "10.2" ]] || \
[[ "$SUBNET" == "10.3" ]]; then
exit 0
fi
# On an external network, bring up the VPN
nmcli con up "$VPN_CONNECTION" &
;;
down)
# Bring down the VPN when the underlying connection drops
nmcli con down "$VPN_CONNECTION" 2>/dev/null
;;
esac
sudo chmod 0744 /etc/NetworkManager/dispatcher.d/40-wireguard-autoconnect
sudo chown root:root /etc/NetworkManager/dispatcher.d/40-wireguard-autoconnect
Testing the connection
Bring the VPN up manually:
nmcli con up home
Test connectivity to all three sites:
ping -c3 10.1.0.1 # Prevernal
ping -c3 10.2.0.1 # Vernal
ping -c3 10.3.0.1 # Estival
Test internal DNS resolution through the tunnel:
dig server.yourdomain.net +short
Check the WireGuard interface status:
sudo wg show
This shows the current handshake time, bytes transferred, and allowed IPs for each peer. A recent handshake (within the last few minutes) confirms the tunnel is active.
Checking for DNS leaks
When using split tunnel, verify that external DNS queries are not going through the VPN:
dig google.com +short
This should resolve quickly using your local Unbound instance. If it is slow or failing, check that AllowedIPs does not include 0.0.0.0/0 and that Unbound is running correctly.
Troubleshooting
Handshake never completes: Check that the router’s WireGuard port is reachable from outside:
nc -u -v vpn.yourdomain.net 51820
If it times out, the port may not be forwarded through the router’s WAN firewall.
DNS not resolving internal names: Verify the DNS setting was applied:
resolvectl status wg0
If the DNS server is not showing, reload the connection:
nmcli con down home && nmcli con up home
Connection drops when switching networks:
WireGuard handles roaming by design. If connections are dropping, check that PersistentKeepalive = 25 is set in the peer configuration. This is important for NAT traversal.
Cannot reach sites 2 or 3: Verify the router at Prevernal has routes to the other site subnets and that the inter-site VPN between routers is up. The desktop tunnel only needs to connect to Prevernal: inter-site routing is handled at the router level from there.
The private key in
/etc/wireguard/home.confand/etc/wireguard/desktop-private.keyis the most sensitive file in the WireGuard setup. Anyone with this key can authenticate to your VPN as this desktop. The permissions set above (600, root-owned) are the minimum. Store a backup in KeePassXC.