TTR Networks

Intermediate · WireGuard on Debian: users, routing and keys · section 4 of 10

AllowedIPs: cryptokey routing

✓ Lab-tested 21 October 2026 on Debian 13.7, wireguard-tools 1.0.20210914, nftables 1.1.3

AllowedIPs is the heart of WireGuard, and it does two jobs at once. WireGuard calls the combination cryptokey routing: every address belongs to exactly one public key.

  • Outbound, it's a routing table. When the server sends a packet into wg0, WireGuard looks up the destination in the peers' AllowedIPs to choose which peer, and therefore which key, encrypts it. A packet for 10.20.0.10 goes to the branch router, because only the branch peer lists 10.20.0.0/24.
  • Inbound, it's an access list. When a packet arrives from a peer and is decrypted, its source address must be in that peer's AllowedIPs, or WireGuard drops it.

The lab tests the second job directly. bob adds a second address to his tunnel and pings from it. The server receives the encrypted packet, decrypts it, sees the source 10.9.0.99, which isn't bob's, and drops it, before it ever appears on wg0. Pinging from his real address, 10.9.0.11, works. A user can't pretend to be another user, and the server's firewall rules can trust the source address of anything arriving on wg0.

A network behind a peer

The branch router is a peer like any other, but its AllowedIPs on the server also includes the branch LAN, 10.20.0.0/24. That one line routes the branch LAN to it, and allows packets from the branch PCs to come out of its tunnel. OpenVPN needed two settings for this (route and iroute); WireGuard needs one. The branch router itself forwards between its tunnel and its LAN.

wg show on the server now lists every peer: each user's endpoint (the address they're connecting from), their allowed IPs, the latest handshake and their traffic.

bob tries a source address that isn't his (real output)
root@bob:~# ip addr add 10.9.0.99/24 dev wg0 && ping -c 2 -W 1 -I 10.9.0.99 10.9.0.1; ping -c 2 -W 1 -I 10.9.0.11 10.9.0.1
PING 10.9.0.1 (10.9.0.1) from 10.9.0.99 : 56(84) bytes of data.

--- 10.9.0.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1036ms

PING 10.9.0.1 (10.9.0.1) from 10.9.0.11 : 56(84) bytes of data.
64 bytes from 10.9.0.1: icmp_seq=1 ttl=64 time=0.460 ms
64 bytes from 10.9.0.1: icmp_seq=2 ttl=64 time=0.473 ms

--- 10.9.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1046ms
rtt min/avg/max/mdev = 0.460/0.466/0.473/0.006 ms
  • Lines 1–5: From 10.9.0.99: the server decrypts the packet, finds a source that isn't in bob's AllowedIPs, and drops it.
  • Lines 7–13: From his own address, 10.9.0.11: fine.
…and what the server's wg0 saw (real output)
root@vpn:~# timeout 6 tcpdump -lnni wg0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on wg0, link-type RAW (Raw IP), snapshot length 262144 bytes
06:47:23.701837 IP 10.9.0.11 > 10.9.0.1: ICMP echo request, id 4725, seq 1, length 64
06:47:23.701959 IP 10.9.0.1 > 10.9.0.11: ICMP echo reply, id 4725, seq 1, length 64

2 packets captured
2 packets received by filter
0 packets dropped by kernel
  • Lines 4–5: Only the 10.9.0.11 ping. The 10.9.0.99 packets never reached wg0, let alone the firewall.
/etc/wireguard/wg0.conf on the branch router
# /etc/wireguard/wg0.conf on the branch router  (wg-quick up wg0)
[Interface]
Address = 10.9.0.20/24
PostUp = wg set %i private-key /etc/wireguard/branch.key

[Peer]
# The VPN server: reach the VPN and the office LAN through it.
PublicKey = /S03YCIRbPGjlhE53GYhxFEh8LVo/Joel3BkEgWeeXk=
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.9.0.0/24, 10.10.0.0/24
PersistentKeepalive = 25
  • Line 10: The branch only needs the VPN and the office LAN through the tunnel; its internet traffic goes direct.
The server with every peer connected (real output)
root@vpn:~# wg show
interface: wg0
  public key: /S03YCIRbPGjlhE53GYhxFEh8LVo/Joel3BkEgWeeXk=
  private key: (hidden)
  listening port: 51820

peer: sh5Ul4rjT56yt7ZYYVSrpJvxEIqKFx/HxkeICJIgGws=
  endpoint: 198.51.100.23:35959
  allowed ips: 10.9.0.20/32, 10.20.0.0/24
  latest handshake: Now
  transfer: 308 B received, 220 B sent

peer: 8BpkL72eT8cHe8HTgKE8yWD464E0FVbQGlQ1FPi0tnc=
  endpoint: 198.51.100.22:34037
  allowed ips: 10.9.0.11/32
  latest handshake: Now
  transfer: 308 B received, 220 B sent

peer: hwCdM7ImFlIKfF0sQHngu9THMAenVRLSD1JYIrqR2Qw=
  endpoint: 198.51.100.21:44820
  allowed ips: 10.9.0.10/32
  latest handshake: 17 seconds ago
  transfer: 980 B received, 604 B sent
  • Lines 7–11: The branch router, connecting from 198.51.100.23.
  • Lines 13–23: bob and alice: each with an endpoint, a handshake and a byte count. This is your list of who's connected.

Check your understanding

On the server, bob's peer has AllowedIPs = 10.9.0.11/32. What happens to a packet the server routes to 10.9.0.11, and to a packet arriving from bob with source 10.9.0.12?

  • The first is encrypted to bob's key; the second is dropped.
  • Both are dropped until the firewall allows them.
  • The first goes to whichever peer answers first; the second is accepted because it's in 10.9.0.0/24.
  • Both are accepted, because bob has a valid handshake.

Check your understanding

The branch router's LAN is 10.20.0.0/24. Which server-side setting makes the server send traffic for it to the branch, and accept traffic from it?

  • Adding 10.20.0.0/24 to the branch peer's AllowedIPs.
  • An iroute on the server.
  • A static route only.
  • Nothing: the branch announces it in the handshake.