Putting two client groups on different subnets looks like isolation, and it is not. Addresses describe where a packet appears to come from, while permission is a decision made by whatever forwards it. That question is answered by configuration rather than by the mask, so decide which component forwards peer traffic before you write any rule.

A subnet is a label, not a rule

Assigning one pool per group gives you readable addresses and nothing else. A client that accepts an extra route has gained reachability, and reachability is not authorization. The permission to talk sits in the forwarding decision, which may live in the daemon or in the kernel depending on how the server is configured.

Address ownership is what makes a prefix usable as a group selector. Each authenticated identity has to map to a predictable address or range, and that mapping fails as soon as several devices share one credential and one assignment. Resolve identity and concurrency before relying on a source prefix, because a policy that guesses which machine is calling authorizes the wrong one at the worst moment.

Pick the path that carries peer traffic

When the daemon forwards peer packets inside its own process, those packets never reach the host forwarding hook, so a kernel rule cannot see them. Disable that internal forwarding and the kernel becomes the forwarding point, where ordinary forward rules apply.

# peer forwarding inside the daemon: enforce with per-group policy instead of FORWARD rules
client-to-client
# kernel forwarding path: remove the line above, then filter in the forward chain

Offloaded data channels and product-managed servers publish their own enforcement models, so confirm which one you are running rather than assuming the community behaviour. The failure worth avoiding is a rule that looks correct in a review and is never consulted at runtime.

Write the matrix before writing rules

A short matrix is easier to review than a ruleset, and it is the contract the implementation must satisfy: same-group peers allowed, cross-group peers denied, both groups allowed to one named shared service, everything else denied. Write those four lines down before you open a firewall editor. A reviewer who does not read ruleset syntax can still check the matrix. The implementation then only has to match the lines.

SourceDestinationDecision
Group A addressAnother Group A addressAllow
Group A addressA Group B addressDeny
Either groupThe shared internal serviceAllow
Either groupAnything not listedDeny

Keep the three destination classes apart while you write it. A service on the VPN host is local input, another client is peer transit, and a machine behind the gateway is a different forwarded path. One generous rule can grant all three by accident, which is how a group policy turns into a flat network with extra steps. Write the class next to each row so the intent survives the next edit.

Put the rules where they survive

Add the decisions in the same place the rest of the firewall is maintained, ahead of any permissive rule that would otherwise swallow them. Rule order is part of the policy, and a correct rule placed after a broad accept enforces nothing at all. Note the rule numbers before and after the change. A policy that exists in an editor but not in the running ruleset is a plan, not a control.

Decide what happens to flows that already exist when the policy changes, because a session opened before the change may keep running afterwards. If a departed member must lose access immediately, the ruleset needs a way to terminate that state rather than waiting for the connection to close on its own.

Persist the rules through the mechanism that owns the rest of the firewall, and record the matrix beside them. An unrecorded policy gets re-derived from memory during the next incident, usually by somebody in a hurry.

Prove it from both sides

Test same-group reachability, cross-group failure and the shared service from one client in each group. A single test from a single client proves half the matrix, and the half it leaves out is usually the one that is wrong. Repeat the denying direction twice, because a cached session can hide an enforcement mistake. Record which pair you tested so the next person does not repeat it.

Repeat the check on IPv6 if the tunnel carries it, and keep ordinary application authentication in place. A source prefix is not an account, so passing the network policy does not mean a service should trust the caller.

Ũhoro ũhĩtĩte