A basic VPN is easy to set up, but a modern network needs more than “connect and hope for the best.” A Zero-Trust WireGuard VPN is a practical way to give remote users access to only what they need, while keeping everything else blocked by default. In this tutorial you will build a WireGuard server on Linux, create per-user profiles, restrict each user to specific internal subnets, and add DNS-based blocking to reduce malware and ads on connected devices.
Prerequisites
You will need: (1) a Linux server with a public IP (Ubuntu/Debian examples are used), (2) root or sudo access, (3) a domain name (optional but useful), and (4) one or more internal networks to protect (for example, 10.10.0.0/16). You should also know which UDP port you want for WireGuard (the default is 51820/UDP).
Step 1: Install WireGuard
Update packages and install WireGuard:
Ubuntu/Debian:
sudo apt update && sudo apt install -y wireguard iptables-persistent
Enable IP forwarding so the server can route traffic between VPN and internal networks:
sudo sysctl -w net.ipv4.ip_forward=1
Make it persistent:
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
Step 2: Generate Server Keys
WireGuard uses simple public/private keys. Create them with tight permissions:
umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
Read the public key (you will use it in client configs):
sudo cat /etc/wireguard/server.pub
Step 3: Create the WireGuard Interface (wg0)
Create /etc/wireguard/wg0.conf. In this example, the VPN network is 10.44.0.0/24 and the server is 10.44.0.1. Replace eth0 with your public interface name (check with ip a).
[Interface]
Address = 10.44.0.1/24
ListenPort = 51820
PrivateKey = (paste contents of /etc/wireguard/server.key)
SaveConfig = false
Now add firewall/NAT rules so VPN clients can reach internal networks (and optionally the internet). Add these lines under the interface section:
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
Start and enable the service:
sudo systemctl enable --now wg-quick@wg0
Verify:
sudo wg
Step 4: Add Per-User Keys and “Allow Only What’s Needed”
For Zero-Trust behavior, you should avoid giving every user full access to your entire internal network. WireGuard supports this with AllowedIPs per peer. Generate a key pair for a user (example: user “alice”):
umask 077
wg genkey | tee /etc/wireguard/alice.key | wg pubkey > /etc/wireguard/alice.pub
Decide what Alice is allowed to reach. Example: only a file server subnet 10.10.20.0/24 and the VPN IP for Alice (10.44.0.10/32). Add a peer block to /etc/wireguard/wg0.conf:
[Peer]
PublicKey = (paste contents of /etc/wireguard/alice.pub)
AllowedIPs = 10.44.0.10/32, 10.10.20.0/24
PersistentKeepalive = 25
Apply the changes by restarting WireGuard:
sudo systemctl restart wg-quick@wg0
This approach gives Alice an address on the VPN and only routes the required internal subnet through the tunnel. Everything else stays outside the VPN, reducing accidental exposure.
Step 5: Create a Secure Client Configuration
On the client side, create a config that uses Alice’s private key and the server’s public key. If your server public IP is 203.0.113.10 and the port is 51820, a minimal client config looks like this:
[Interface]
PrivateKey = (paste contents of /etc/wireguard/alice.key)
Address = 10.44.0.10/32
DNS = 10.44.0.1
[Peer]
PublicKey = (server public key)
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.10.20.0/24
PersistentKeepalive = 25
Notice that the client’s AllowedIPs includes only the internal subnet she needs. That keeps her internet traffic off the VPN and limits risk if the client device is compromised.
Step 6: Add DNS Blocking (Optional but Recommended)
A simple way to reduce malicious domains is to run a lightweight DNS resolver with blocklists, such as Unbound plus a blocklist, or dnsmasq. A practical option on a small server is dnsmasq:
sudo apt install -y dnsmasq
Bind DNS to the WireGuard interface IP by editing /etc/dnsmasq.conf and adding:
listen-address=10.44.0.1
bind-interfaces
Then point clients to DNS = 10.44.0.1 (as shown earlier). For blocking, you can add entries to /etc/dnsmasq.d/blocked.conf like:
address=/example-bad-domain.com/0.0.0.0
Restart dnsmasq:
sudo systemctl restart dnsmasq
Step 7: Troubleshooting Checklist
If a client connects but cannot reach anything, check these items: (1) confirm the server is listening on UDP 51820 and the cloud firewall allows it; (2) verify AllowedIPs on both server and client match the intended access; (3) ensure IP forwarding is enabled; (4) confirm your PostUp NAT rule uses the correct public interface; and (5) run sudo wg and look for the latest handshake time and transfer counters.
With these steps, you get a modern WireGuard VPN that follows a Zero-Trust mindset: per-user identities, minimal network exposure, and optional DNS filtering for safer browsing on remote devices.
Comments
Post a Comment