One client needs to reach another client's SSH service, and the rest of the pool should stay as isolated as it is today. The tempting switch is client-to-client, which turns on peer access for everyone at once and hands the decision to OpenVPN instead of the kernel. A narrower answer keeps that switch off and lets the server's own routing and firewall carry exactly one flow.

The mechanism matters more than the rule text, because the two approaches put the packet in different places. Knowing which one you chose explains both the rule that seems to do nothing and the peer that connects without it.

Where client-to-client moves the packet

With client-to-client enabled, the server relays a peer packet internally instead of sending it back out the tunnel device, so the packet never reaches the kernel's forwarding path. That is convenient and it also means your firewall never sees the flow it is supposed to police. Leave the option off and the server routes the packet back out the same interface, which puts it in front of netfilter. Same interface in, same interface out, and the FORWARD chain gets the vote.

The trade is explicit: you gain a policy point and you take on the routing and forwarding work that the shortcut was doing for you. That work is small for a single flow and grows with every exception you add. Count the exceptions now, because the rule set is where peer policy actually lives.

The path a peer packet actually takes

Assume a Linux Community OpenVPN 2.6 service, userspace TUN processing rather than DCO, subnet topology, tun0, and a pool of 10.44.0.0/24 with client A at 10.44.0.21 and client B at 10.44.0.37. With subnet topology the address and netmask A received already cover the pool, so A sends the packet to the tunnel device without needing an extra pushed route. Check the effective route on A rather than assuming one.

On the server the packet arrives addressed to B, which is not a local address, so it is forwarded. IPv4 forwarding has to be enabled for that to happen at all, and the route lookup has to send the packet back out tun0. The firewall therefore sees a forwarded flow with the same interface on both sides, which is the detail most rulesets miss. Nothing needs source translation here, and keeping A's address intact is what lets B apply its own source-based rule.

Two rules on the server, in this order

Three rules carry the whole grant: forwarding, the pair exception, and a default drop for the tunnel pair. The accept rule names the pair and the service, so the grant survives a pool that grows. Place it before any blanket deny for tunnel-to-tunnel traffic, because the first match wins:

sysctl -w net.ipv4.ip_forward=1
iptables -A FORWARD -i tun0 -o tun0 -s 10.44.0.21 -d 10.44.0.37 -p tcp --dport 22 -j ACCEPT
iptables -A FORWARD -i tun0 -o tun0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i tun0 -o tun0 -j DROP

Those commands are illustrative and belong in whatever mechanism your host uses to persist rules; a runtime insert disappears at the next boot. The stateful rule is what lets B's replies and the TCP handshake return without a second, mirrored accept.

Check the counters afterwards rather than the rule listing, because a rule that is never matched looks identical to one that works. If the counters stay at zero, the packet is not taking the path you think it is taking.

What B still has to permit

B runs the SSH service, so B decides whether anything listens on the tunnel address and whether its own host firewall accepts the source. A network allow on the server is not permission for any account on B, and host-key verification and user authentication still apply as they would from anywhere else.

Test the positive case first: a fresh SSH connection from A to B's tunnel address, accepted with the expected host key. Then test a different peer or a different port and confirm it is refused, because a grant that quietly allows everything is indistinguishable from one that works until it matters. Both halves of that test are necessary, and the second is the one people skip.

Record the assigned addresses, the effective routes on both clients and the exact rules on the server and on B. A successful ping to the gateway or an open port on the server proves none of this, since both can be true while peer forwarding is still blocked. Write those numbers down while the tunnel is up, because the address a client receives after a reconnect may be a different one.

Barruulee wal qabatan