Traffic between users
WireGuard has no equivalent of OpenVPN's client-to-client. Every packet from one user to another arrives on wg0, goes through the server's kernel, and leaves on wg0 again, so the firewall always decides. That's simpler and safer: there's no mode where user-to-user traffic bypasses your rules.
With the lesson's firewall, the branch PC and the office reach each other, but alice can reach neither bob nor the branch LAN: the forward chain's default drop counts those packets.
To allow something specific, add a rule for exactly that. One rule lets alice, and only alice, reach the branch LAN; alice to bob is still dropped. Because cryptokey routing guarantees that packets from 10.9.0.10 really came from alice's key, a firewall rule on her VPN address is a rule about her.
root@branchpc:~# ping -c 2 -W 1 10.10.0.20
PING 10.10.0.20 (10.10.0.20) 56(84) bytes of data.
64 bytes from 10.10.0.20: icmp_seq=1 ttl=61 time=0.262 ms
64 bytes from 10.10.0.20: icmp_seq=2 ttl=61 time=1.20 ms
--- 10.10.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1012ms
rtt min/avg/max/mdev = 0.262/0.731/1.201/0.469 ms
root@office:~# ping -c 3 -W 2 10.20.0.10
PING 10.20.0.10 (10.20.0.10) 56(84) bytes of data.
64 bytes from 10.20.0.10: icmp_seq=1 ttl=62 time=0.273 ms
From 10.10.0.254 icmp_seq=2 Redirect Host(New nexthop: 10.10.0.1)
64 bytes from 10.20.0.10: icmp_seq=2 ttl=62 time=0.374 ms
--- 10.20.0.10 ping statistics ---
2 packets transmitted, 2 received, +1 errors, 0% packet loss, time 1053ms
rtt min/avg/max/mdev = 0.273/0.323/0.374/0.050 ms
root@alice:~# ping -c 2 -W 1 10.20.0.10; ping -c 2 -W 1 10.9.0.11
PING 10.20.0.10 (10.20.0.10) 56(84) bytes of data.
--- 10.20.0.10 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1059ms
PING 10.9.0.11 (10.9.0.11) 56(84) bytes of data.
--- 10.9.0.11 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1044ms
root@vpn:~# nft list chain inet filter forward | grep -E "counter"
counter packets 4 bytes 336 comment "everything else, including user to user, is dropped"- Lines 1–8: A branch PC reaches the office: branch router, tunnel, VPN server, office LAN.
- Lines 10–18: And back. lanrtr forwards the first packet, then tells the office host (an ICMP redirect) to use the VPN server directly.
- Lines 20–29: alice can reach neither the branch LAN nor bob.
- Lines 32–33: Four packets: alice's two pings to each. The default drop decided.
root@vpn:~# nft insert rule inet filter forward iifname "wg0" oifname "wg0" ip saddr 10.9.0.10 ip daddr 10.20.0.0/24 counter accept comment \"alice to the branch LAN\"
root@alice:~# ping -c 2 -W 1 10.20.0.10; ping -c 2 -W 1 10.9.0.11
PING 10.20.0.10 (10.20.0.10) 56(84) bytes of data.
64 bytes from 10.20.0.10: icmp_seq=1 ttl=62 time=0.450 ms
64 bytes from 10.20.0.10: icmp_seq=2 ttl=62 time=1.75 ms
--- 10.20.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1050ms
rtt min/avg/max/mdev = 0.450/1.101/1.753/0.651 ms
PING 10.9.0.11 (10.9.0.11) 56(84) bytes of data.
--- 10.9.0.11 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1020ms
root@vpn:~# nft list chain inet filter forward | grep -E "counter"
iifname "wg0" oifname "wg0" ip saddr 10.9.0.10 ip daddr 10.20.0.0/24 counter packets 2 bytes 168 accept comment "alice to the branch LAN"
counter packets 2 bytes 168 comment "everything else, including user to user, is dropped"- Line 1:
insertputs the rule at the top of the chain, before the final counter. - Lines 3–14: alice reaches the branch PC, and still not bob.
- Lines 18–19: Two packets accepted by the new rule, two (to bob) dropped.
Check your understanding
Why is a firewall rule matching ip saddr 10.9.0.10 on packets from wg0 a trustworthy rule about alice?
- Because WireGuard only accepts 10.9.0.10 from alice's key, so no other user can send from it.
- Because the address is fixed in alice's config.
- Because nftables checks WireGuard keys.
- It isn't; anyone could use that address.