Two VPN clients can leave through two different public IPv4 addresses, and the rule that does it is shorter than most people expect. What makes it work is not the iptables syntax but the state of the packet when it reaches the egress chain: the source address there still has to tell the clients apart. If an earlier masquerade has already collapsed both of them onto one address, no per-client rule can recover the distinction. Settle that question first, then write the translation.

Which Identity Survives to the Gateway

The rule matches the packet's source address, not a VPN username and not the name a client application displays. Everything that translates the packet earlier on the path decides what your rule can still see. Run the nat table in order and find the point where the source is still per-client.

iptables -t nat -S POSTROUTING

Read that listing from the top. A masquerade rule near the top rewrites the source for every matching flow, and per-client entries placed after it match an address that no longer exists. When two LAN devices share one tunnel and arrive as a single inner address, the design has to change before the firewall does: keep their source ranges distinct or give each one its own inner address.

Tie the inner-address assignment to the authenticated client. Handing that address to another device hands over the egress policy with it, and source translation is not an identity proof on its own.

Translation Cannot Invent an Address

Each public address has to be assigned or routed to this egress point, and the replies have to find their way back to it. A rule that sends traffic from an address nobody routes back is dropped at the far end or answered to a host that never asked for it. A cloud-provider instance usually has its public address translated to a private interface address before the packet leaves, so the SNAT source has to be the address the provider will accept, not the value the console shows. Read the provider's address model and follow it rather than copying the public value into every packet.

In practice that means checking the routed prefix in the provider console before touching the firewall, and using the private interface address as the translation source when the mapping is one-to-one. The addresses used in the examples below come from the documentation ranges, so the first thing to replace is the address text itself.

Insert Before the Generic Masquerade

Confirm the egress interface and the current chain before adding anything. The first decides which packets the rule can match, and the second shows what is already standing in front of it.

ip -4 route show default
iptables -t nat -L POSTROUTING -n -v --line-numbers

The first command names the interface a packet to the internet actually leaves through. The second prints the chain with line numbers. The generic masquerade entry is the line to stay above.

iptables -t nat -I POSTROUTING 1 -s 10.88.4.20/32 -o ens3 -j SNAT --to-source 198.51.100.77
iptables -t nat -I POSTROUTING 1 -s 10.88.4.21/32 -o ens3 -j SNAT --to-source 198.51.100.78

The two rules above match one inner address each, restrict the match to the egress interface, and set the source explicitly. Insert rather than append: at position 1 they are evaluated before the masquerade line. The order between the two does not matter because their sources differ. Replace ens3 with whatever the route command reported, and keep other firewall managers off the same hooks, since two tools writing one table is the usual reason a correct rule has no effect.

Prove the Source With a New Flow

Stateful NAT binds a translation when a flow begins and reuses it for later packets, so a session opened before the change keeps its old source after the change. Only a newly initiated connection proves the rule.

conntrack -L -s 10.88.4.20 -p tcp --dport 443

Read the reply direction of that entry: its destination is the address the remote endpoint sees as the source. Compare it with the public address you intended, and treat an entry carrying the old address as evidence of a flow that started too early rather than a broken rule. Ask the client to open a fresh connection, then repeat the query. Flushing the whole tracking table to force the test interrupts every other session on the gateway.

Translation still does not choose the route, admit the flow through the filter, or publish an inbound service. A wrong route and a missing allow rule keep failing after a perfect SNAT rule. Check IPv6 and any secondary egress path as well, then note the inner-to-public pairs in the firewall's documentation so the next change starts from the mapping instead of the rule text.

Amanqephe ahlobene