A hub running WireGuard can keep peer groups apart without a separate interface or routing table per group. Two mechanisms do the work, and confusing them is the usual reason a policy looks correct and does nothing. One layer decides who may claim a source address. The other decides where that packet is allowed to go.

Two jobs, kept apart

The tunnel layer answers a narrow question: did this peer's key authorize this inner source address. The forwarding layer answers a wider one: may this packet leave toward that destination. Neither answers the other's question, and neither is a complete access policy on its own.

That split is why a peer can be authenticated, addressed correctly, and still be unable to reach a host it should not reach. It also explains the reverse: a firewall rule that permits everything proves nothing about who is allowed to claim which address.

Design the two layers together, then test both. A configuration that is correct at one layer and untested at the other fails open or closed depending on the default.

Give each peer only its own address

Multi-host peers tempt you into granting the whole group range on the server side. That permission is a source-address claim, so a compromised or misconfigured client can present any address in the range and the tunnel will accept it. Assign a /32 per peer for its own inner address. Set group ranges only on the clients, where they describe which destinations that client may reach rather than which sources it may use.

The firewall then matches on a source it can trust. A rule written for the group's range is still a useful grouping, and it becomes meaningful only when no single peer can claim an address belonging to another member.

Write the forward policy that separates groups

The interesting traffic here enters and leaves the same tunnel interface, so it crosses the forward path rather than the host input path. Rules belong in the existing forward chain of whichever firewall manager owns the host, and they are fragments rather than a complete ruleset.

iifname "wg0" oifname "wg0" ip saddr 10.66.10.0/24 ip daddr 10.66.10.0/24 accept
iifname "wg0" oifname "wg0" ip saddr 10.66.20.0/24 ip daddr 10.66.20.0/24 accept
iifname "wg0" oifname "wg0" drop

The first two rules permit traffic within each group. The last one blocks everything else crossing that interface, which is what produces isolation between the groups. Add explicit rules for the other address family if you need them, because a drop written for IPv4 does not authorise or describe IPv6 behaviour.

Order decides whether the policy applies

Rule order matters more than the fragment itself. A generic rule that accepts established connections placed before these entries lets an old cross-group flow continue, so the isolation applies to new connections only. Decide which of those two outcomes you want before you reload.

An accept verdict is also not always final across chains at the same hook. If another base chain runs later, the effective policy is the combination, so inspect the counters rather than the file you edited. Counters are the cheapest evidence available. A rule with no packet count is not matching, and a rule that matches while a later chain accepts tells you the drop is in the wrong place.

Test the matrix, not the file count

Build a small matrix of expected results and check every cell. Group A to group A should work, group B to group B should work, and A to B should not, in both directions and for both address families if you support them.

nft list ruleset
wg show wg0 allowed-ips
ping -c 2 10.66.10.5
conntrack -L -s 10.66.20.7 2>/dev/null | head

The first two show what is configured. The ping from a member of the wrong group shows what actually happens. The conntrack listing shows whether an older flow is still being accepted, which is how an isolation change quietly fails to take effect.

Host services on the hub and access to a LAN behind it are separate paths with separate policy. Record which of them the matrix covers, because a test that only checks peer-to-peer traffic says nothing about either.

Sɛbɛn minnu bɛ tali kɛ