WireGuard: a secure tunnel between the office and the server
Contents
For an administrator who wants RDP, SSH and databases on the server to be reachable only by the company’s staff, not the whole internet. The result is an encrypted WireGuard tunnel between the office (or individual computers and phones) and the server, so the service ports can be closed to the outside.
WireGuard has no “server” and “client”, only equal peers, each with a key pair and an AllowedIPs list. The peer with a fixed public address (your server) listens on a UDP port, and the others (clients below) connect to it themselves.
What you will need#
- A server running Ubuntu LTS (24.04 or 26.04) or Debian 13 and an account with
sudorights. Support sends the server’s public address with the access details. - A private subnet for the tunnel. It must not overlap with the office network, staff home networks (most often 192.168.0.0/24 and 192.168.1.0/24) or the private network between servers, if you use one.
- A free UDP port on the server; the usual one for WireGuard is 51820.
All addresses here are examples; replace them with your own: the server’s public address is 203.0.113.10, the tunnel subnet is 10.66.0.0/24 (server 10.66.0.1, laptop 10.66.0.2, phone 10.66.0.3, office router 10.66.0.10) and the office network is 192.168.10.0/24.
Step 1 A server running Ubuntu or Debian#
Installation and keys#
The WireGuard module is already in the kernel; the package adds the wg and wg-quick tools.
sudo apt update
sudo apt install wireguardCreate the keys in a root shell with umask 077, so only root can read the new files:
sudo -i
umask 077
cd /etc/wireguard
wg genkey | tee server.key | wg pubkey > server.pub
cat server.pubThe public key in server.pub goes to the clients; the private key stays on the server only.
The /etc/wireguard/wg0.conf file#
Still in the root shell, create the file with any editor, for example nano /etc/wireguard/wg0.conf:
[Interface]
Address = 10.66.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEYReplace SERVER_PRIVATE_KEY with the contents of server.key (cat server.key prints it). Add [Peer] sections once you have the clients’ public keys. Leave the root shell with exit.
Firewall and starting the tunnel#
If ufw is installed on the server, open the UDP port. The command only adds a rule and does not switch the firewall on. If ufw is still disabled, do not enable it until you have allowed SSH — see “Basic Linux server hardening”. If you use nftables, add the rule udp dport 51820 accept to the input chain.
sudo ufw allow 51820/udpStart the tunnel and enable it at boot; wg show should list the wg0 interface and port 51820:
sudo systemctl enable --now wg-quick@wg0
sudo wg showStep 2 Computers and phones#
Official apps: for Windows, the installer from wireguard.com/install; for macOS and iOS, the App Store; for Android, Google Play.
A computer#
- In the app choose “Add Tunnel” → “Add empty tunnel…” (on macOS, “+” → “Add Empty Tunnel…”). The app generates a key pair: the private key is already in the configuration text and the public key is shown above it.
- Complete the configuration and save it:
[Interface] PrivateKey = LAPTOP_PRIVATE_KEY Address = 10.66.0.2/32 [Peer] PublicKey = SERVER_PUBLIC_KEY Endpoint = 203.0.113.10:51820 AllowedIPs = 10.66.0.0/24 PersistentKeepalive = 25 - On the server, open the file with
sudo nano /etc/wireguard/wg0.confand append a section with this computer’s public key:[Peer] PublicKey = LAPTOP_PUBLIC_KEY AllowedIPs = 10.66.0.2/32 - Apply the change without dropping other connections, then press “Activate” in the app:
sudo systemctl reload wg-quick@wg0
On a client, AllowedIPs defines which traffic goes into the tunnel: here only the tunnel subnet, so the rest of the internet works as before. PersistentKeepalive = 25 is needed by peers behind NAT: every 25 seconds the peer sends an empty packet so the router does not “forget” the connection.
A ready-made configuration file can be imported: “Add Tunnel” → “Import tunnel(s) from file…”. The file contains the private key, so delete it after the import.
A phone: configuration by QR code#
Create the configuration on the server and print the QR code in the terminal:
sudo apt install qrencode
sudo -i
umask 077
mkdir -p /root/wg-clients
cd /root/wg-clients
wg genkey | tee phone.key | wg pubkey > phone.pub
cat phone.key
nano phone.conf
qrencode -t ansiutf8 < phone.confThe phone.conf file is the same as the computer’s, but with the address 10.66.0.3/32 and the private key from phone.key. In the app press “+” and scan the QR code, then delete the files with the private key and print the public one:
rm phone.key phone.conf
cat phone.pub
exitAdd the public key to a new [Peer] section on the server with AllowedIPs = 10.66.0.3/32 and run reload again. The QR code contains the private key: do not keep screenshots of it or send it through messengers.
Step 3 The whole office network#
To avoid installing the app on every computer, the tunnel is set up on the office router, if it supports WireGuard (for example, with OpenWrt firmware). Every device in the office then reaches the server at 10.66.0.1 with no further configuration. Add a peer on the server:
[Peer]
PublicKey = ROUTER_PUBLIC_KEY
AllowedIPs = 10.66.0.10/32, 192.168.10.0/24The office network in AllowedIPs means the server accepts packets from office computers and replies to them through this peer. This time run sudo systemctl restart wg-quick@wg0 (connections drop for a moment): wg-quick adds the route to a new subnet only when it starts, and reload will not create it.
On the router (field names depend on the model) set the interface address to 10.66.0.10/24, the peer’s public key to the contents of server.pub, Endpoint to 203.0.113.10:51820, AllowedIPs to 10.66.0.0/24 and PersistentKeepalive to 25. The router generates its own key pair; copy its public key to the server. The router’s firewall must allow forwarding from the local network into the tunnel, and NAT towards the tunnel is not needed: the server knows the route to the office network.
If the router does not support WireGuard, a Linux computer in the office can be the tunnel peer with the same settings. On the router add a static route to 10.66.0.0/24 via that computer’s address, and on the computer itself enable packet forwarding:
echo net.ipv4.ip_forward=1 | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --systemOn a Linux server, forwarding is enabled in the same way, but only if the server has to pass packets between peers — for example, so an employee at home can reach the office network through it. Then add 192.168.10.0/24 to the client’s AllowedIPs and allow transit in ufw on the server: sudo ufw route allow in on wg0 out on wg0.
A server running Windows Server#
WireGuard for Windows supports Windows Server 2016, 2019, 2022 and 2025. No separate “server” mode is needed: the server becomes an ordinary tunnel peer with its own address, and you connect to the remote desktop at that address.
- Install the app with the official installer, choose “Add Tunnel” → “Add empty tunnel…” and name the tunnel
wg0. The app fills in the private key; add the rest:[Interface] PrivateKey = SERVER_PRIVATE_KEY ListenPort = 51820 Address = 10.66.0.1/24 [Peer] PublicKey = LAPTOP_PUBLIC_KEY AllowedIPs = 10.66.0.2/32 - Press “Activate”. The tunnel runs as the Windows service
WireGuardTunnel$wg0and starts with the system, before any user signs in. Check this in a new PowerShell window run as administrator:Get-Service WireGuardTunnel* | Select-Object Name, Status, StartType wg show - Open the inbound UDP port in the Windows firewall:
New-NetFirewallRule -DisplayName "WireGuard UDP 51820" -Direction Inbound -Protocol UDP -LocalPort 51820 -Action Allow - Set up the tunnel on the client as in step 2 and connect to the remote desktop at 10.66.0.1.
How to check the result#
- Run
sudo wg showon the server. Each peer should have an external address, the latest handshake time and traffic counters:peer: LAPTOP_PUBLIC_KEY endpoint: 198.51.100.7:53211 allowed ips: 10.66.0.2/32 latest handshake: 37 seconds ago transfer: 1.21 MiB received, 3.48 MiB sent - Run
ping 10.66.0.1from the client. Windows Server may not answerpinguntil its firewall allows it; then rely on the handshake and RDP. - Watch the handshake time: with
PersistentKeepaliveit is renewed roughly every two minutes. If there is nolatest handshakeline, the peers have never connected; if the handshake is more than three minutes old, the link is down.
Step 4 Service ports through the tunnel only#
Warning. The commands in this section can cut you off from the server. Run them only after the tunnel has been checked, including after a reboot, and keep your current session open. If access is lost anyway, you will need the remote console: it is provided on request through support, and the Lite line does not have it.
Linux with ufw enabled. First allow SSH from the tunnel (if the SSH port is not 22, use your own) and look at the rule numbers:
sudo ufw allow in on wg0 to any port 22 proto tcp
sudo ufw status numberedOpen a new SSH session to 10.66.0.1. If it works, delete the rules that allow SSH from everywhere (usually two, for IPv4 and IPv6): sudo ufw delete and the rule number; the numbers shift after each deletion. To roll back, run sudo ufw allow 22/tcp in the old session, which stays connected. Treat database ports the same way.
Windows Server. Connect by RDP to 10.66.0.1 and, from that session, restrict the built-in remote desktop rules to the tunnel subnet:
Set-NetFirewallRule -DisplayGroup "Remote Desktop" -RemoteAddress 10.66.0.0/24Check a new connection without closing this session. To roll back, allow any address again:
Set-NetFirewallRule -DisplayGroup "Remote Desktop" -RemoteAddress AnyThe group name “Remote Desktop” is given for an English-language system. If the RDP port has been changed or the rules were created by hand, see “Secure RDP”; for Linux, see “Basic Linux server hardening”.
Common mistakes#
| Symptom | Cause | What to do |
|---|---|---|
| No handshake | The UDP port is closed on the server, the client’s network blocks outbound UDP, or Endpoint is wrong | Check the firewall rule and sudo wg show wg0 listen-port; try connecting from another network |
| The port is open but there is no handshake | Keys are mixed up: [Interface] takes the peer’s own private key, [Peer] the public key of the opposite peer | Compare sudo wg show wg0 public-key on the server with PublicKey on the client |
| After a peer was added, another one stopped responding | Overlapping AllowedIPs: the same address or subnet is given to two peers, and WireGuard keeps it only for the last one | Give every peer on the server its own /32 address; list the office network for the router only |
ping works but RDP or file copying hangs | MTU: large packets do not get through the link (mobile internet, PPPoE, another VPN) | Add MTU = 1380 to the client’s [Interface]; lower it further if needed, but not below 1280 |
MTU is tested with a packet that must not be fragmented: on Windows ping -f -l 1392 10.66.0.1, on Linux ping -M do -s 1392 10.66.0.1. If small packets pass and this one does not, lower the MTU. If the tunnel fails to start, sudo systemctl status wg-quick@wg0 shows why.
What next#
- Related articles: private network between servers, measuring response time.
- Servers are in the catalogue, countries on the locations page. Support: +38 044 206 08 08, info@united.net.ua.