Two clients on the same OpenVPN server can reach each other even though you added a DROP rule for their pool. The rule sits in the FORWARD chain, the traffic is plainly flowing, and the counter for that rule stays at zero. The natural conclusion is that the firewall is broken. More often the packet never entered the chain at all.

There are two paths a peer packet can take through an OpenVPN server, and they are decided by configuration rather than by the firewall. Knowing which path is in use tells you whether a host rule is even in the right place.

Two paths a peer packet can take

With the internal forwarding option disabled, a packet from client A arrives on the tunnel interface with a destination inside client B's assigned address. The server's routing table sends it back out the same tunnel interface, because the server owns the route to B's address. That is an in-and-out flow through the host, and the IPv4 forwarding path evaluates it.

With the internal forwarding option enabled, the server process matches the packet to a connected client and writes it back to that client itself. The packet is handled inside the daemon and never re-enters the tunnel device, so the host forwarding path is skipped entirely. The OpenVPN 2.6 manual describes this option as internal client-to-client forwarding, which is exactly why a host firewall rule cannot see it.

A third variant exists in deployments that offload the data channel to a kernel module. The forwarding decision and the encryption move out of userspace, and whether netfilter observes the flow depends on how the offload path is built. Never assume that an old ruleset behaves the same on an offloaded server as it did on a userspace one. Confirm the mode from the running configuration rather than from the package version.

When the counter never moves

A zero counter means the packet did not match that rule at that point, and there are several reasons. The first is the bypass above: the traffic never reached the chain. The second is a mismatch in interface, direction, source, or destination, which is common when the pool was renumbered and the rule still names the old prefix.

The third is position. A rule earlier in the chain may accept the flow before your DROP is evaluated, and a rule later may be the one that actually decides the outcome. Read the chain in order with counters visible, then check which rule's count increases when you repeat the test.

Run one controlled exchange between two clients whose real addresses you have confirmed from the server's own state, not from a lease you remember. If the relevant counter still does not move, suspect the path before rewriting the match. If it moves but the connection still succeeds, the drop is being overridden later and the ordering is the problem. That answer decides whether the repair is a rule change or a topology change, so get it before editing anything.

Choosing a boundary that can filter

If the policy has to live in the host firewall, put the traffic where the firewall can see it. Disabling internal forwarding is the supported way to make peer traffic traverse the tunnel interface and the forwarding path, at the cost of sending every peer packet through the host stack.

Then match the flow precisely. Traffic arrives on the tunnel interface and leaves on the tunnel interface, in both directions, so rules that only consider the physical interface will not match. Address-based rules need the current pool, and they need to account for the return direction as well as the forward one.

Many deployments are better served by filtering at the client or group level, where the identity is known and the policy follows the user rather than an address that changes on reconnect. That is a different mechanism from a host chain, and the two should not be mixed without a clear reason.

Do not carry an old topology's ruleset across a migration unchanged. Address pools, interface names, and forwarding modes all affect whether a rule matches, and a copied rule that used to work is the most misleading kind of evidence. Start from the flow you want to permit and write the rule that matches it.

Testing isolation instead of trusting a rule

Test the prohibited case and the permitted case in the same run. Start a new connection from one client to the other and confirm it fails, then confirm a connection that should be allowed still works. If only the first is tested, a firewall that blocks everything looks like success.

Repeat after reloading the server configuration and after a client reconnects, because internal forwarding and pool assignments are runtime state. Check counters in both directions, since a rule that matches one direction and not the reply produces partial isolation that is hard to notice in a quick test.

Isolation is the observed result at the boundary that actually carries the flow. A DROP rule somewhere in the host is not evidence; a failed connection plus a moved counter on the rule that caused it is.

مۇناسىۋەتلىك ماقالىلەر