WIUNIX ACADEMY
Set up a routed WireGuard site-to-site VPN on Ubuntu and Debian
Page 3 of 8. Review the earlier steps and prerequisites before continuing.
YOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
USE YOUR OWN VALUES
Configure this tutorial
Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.
Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.
Set values for my environment24 editable values
NAT endpoints and keepalive
If B is behind NAT, A must be reachable: forward UDP 51820 on A’s edge when necessary. B initiates toward A; add the next optional line under B’s existing Peer only when idle mappings must stay open. On A remove B’s Endpoint if B’s public address/port is unstable; it is learned after authentication. A’s UDP input source rule must match B’s real egress IP, or a deliberately approved wider source set for roaming peers. B needs permitted output and stateful replies. Two unforwardable CGNAT sites need a reachable hub/provider address; keepalive cannot create one. Public-edge NAT is distinct from inner-LAN masquerading, which this guide avoids.
Optional B Peer addition
FILE CONTENT, only when NAT idle reachability needs it; omit in the public/public baseline.
PersistentKeepalive = 25
LET’S BUILD
Enable IPv4 forwarding on A/B
The new dedicated file changes only this setting; it does not grant firewall permission. Do not overwrite an existing owner’s sysctl file.
sudo bash -eu <<'FORWARD'
test ! -e /etc/sysctl.d/70-academy-wireguard.conf
printf 'net.ipv4.ip_forward = 1\n' > /etc/sysctl.d/70-academy-wireguard.conf
sysctl -p /etc/sysctl.d/70-academy-wireguard.conf
FORWARD
IPv4 forwarding is 1 on both gateways.
LET’S BUILD
Prepare the site-specific filter
Use the following A or B FILE CONTENT on its matching gateway. Interface names and peer addresses follow the configuration fields. Management source/SSH22 must match the independent IPv4 administration path before activation. The dedicated inet table defaults input/forward to drop without flushing rules. Do not enable through IPv6-only SSH; ICMPv6 here supports underlay control, not an IPv6 VPN.
sudo install -o root -g root -m 0600 /dev/null /etc/wireguard/academy-filter.nft
sudo nano /etc/wireguard/academy-filter.nft
Rules preserve actual management and restrict site traffic.
A filter contents; B substitutions
FILE CONTENT on A only. Hosts must separately allow approved ICMP/TCP 8080 from the opposite test host in their own firewall. No arbitrary production ports.
define wan = "ens18"
define lan = "ens19"
define local_net = 10.10.10.0/24
define remote_net = 10.20.20.0/24
define local_host = 10.10.10.100
define remote_host = 10.20.20.100
define peer_wan = 203.0.113.20
define management = 192.0.2.0/24
table inet academy_wg {
chain input {
type filter hook input priority 0; policy drop;
iifname "lo" accept
ct state invalid drop
ct state established,related accept
ip saddr $management tcp dport 22 counter accept
iifname $wan ip saddr $peer_wan udp dport 51820 counter accept
ip protocol icmp counter accept
meta l4proto ipv6-icmp accept
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state invalid drop
ct state established,related counter accept
iifname $lan oifname "wg0" ip saddr $local_net ip daddr $remote_net ip protocol icmp counter accept
iifname "wg0" oifname $lan ip saddr $remote_net ip daddr $local_net ip protocol icmp counter accept
iifname $lan oifname "wg0" ip saddr $local_host ip daddr $remote_host tcp dport 8080 counter accept
iifname "wg0" oifname $lan ip saddr $remote_host ip daddr $local_host tcp dport 8080 counter accept
}
}
Site B firewall file contents
FILE CONTENT on B. Values below follow the B gateway fields directly; do not copy A’s peer or interface values.
define wan = "ens18"
define lan = "ens19"
define local_net = 10.20.20.0/24
define remote_net = 10.10.10.0/24
define local_host = 10.20.20.100
define remote_host = 10.10.10.100
define peer_wan = 198.51.100.10
define management = 192.0.2.0/24
table inet academy_wg {
chain input {
type filter hook input priority 0; policy drop;
iifname "lo" accept
ct state invalid drop
ct state established,related accept
ip saddr $management tcp dport 22 counter accept
iifname $wan ip saddr $peer_wan udp dport 51820 counter accept
ip protocol icmp counter accept
meta l4proto ipv6-icmp accept
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state invalid drop
ct state established,related counter accept
iifname $lan oifname "wg0" ip saddr $local_net ip daddr $remote_net ip protocol icmp counter accept
iifname "wg0" oifname $lan ip saddr $remote_net ip daddr $local_net ip protocol icmp counter accept
iifname $lan oifname "wg0" ip saddr $local_host ip daddr $remote_host tcp dport 8080 counter accept
iifname "wg0" oifname $lan ip saddr $remote_host ip daddr $local_host tcp dport 8080 counter accept
}
}