0

Scenario

ClientA and ClientB are both connected via VPN to ServerA

Goal

All traffic of ClientA that goes into Internet through ServerA should use the public IP 192.0.2.1, and ClientB should use public IP 192.0.2.2

Setup

My configuration is a server and four clients, I want to route all traffic through ServerA.

VPN IP
ServerA 10.0.0.1 (192.0.2.1 and 192.0.2.2 (public IPs))
ClientA 10.0.0.2
ClientB 10.0.0.3

Rules

I've added these rules via iptables:

iptables -t nat -A POSTROUTING -s 10.0.0.2/22 -o eth0 -j SNAT --to 192.0.2.1
iptables -t nat -A POSTROUTING -s 10.0.0.3/22 -o eth0 -j SNAT --to 192.0.2.2

The configuration works fine, but all the traffic is translted into IP 192.0.2.1 for both clients, even though I specified that each client goes out through a certain public IP.

To be honest, I don't know if it's possible what I want, but if it can be done, how would it be.

1 Answers1

2

In the context of firewall address matching both -s 10.0.0.2/22 and -s 10.0.0.3/22 work like -s 10.0.0.0/22, i.e. the mask shows how many bits to analyze, and both .2 and .3 are not to be analyzed here. These unused parts are actually stripped before addition to a firewall:

# iptables -A OUTPUT -s 10.0.0.3/22 -j REJECT
# iptables-save | grep REJECT
-A OUTPUT -s 10.0.0.0/22 -j REJECT --reject-with icmp-port-unreachable

Notice how .2 is silently converted to .0. Check yours after you run these commands with iptables-save, you'll see that you created two rules with the exact same match, so only first one has a chance to fire!


If you want these addresses and not a whole subnet to match, you don't specify mask (a.k.a. use the mask /32):

iptables -t nat -A POSTROUTING -s 10.0.0.2 -o eth0 -j SNAT --to 192.0.2.1
iptables -t nat -A POSTROUTING -s 10.0.0.3 -o eth0 -j SNAT --to 192.0.2.2

Update: Firewall rules are processed sequentially. First rule matched terminates the chain processing. So, if you want to have a "default" NAT setting and "override" it for a limited number of clients, put the "override" rules first then make a "default" rule last with a match broad enough:

iptables -t nat -A POSTROUTING -s 10.0.0.22 -o eth0 -j SNAT --to 192.0.2.2
iptables -t nat -A POSTROUTING -s 10.0.0.44 -o eth0 -j SNAT --to 192.0.2.2
iptables -t nat -A POSTROUTING -s 10.0.0.0/22 -o eth0 -j SNAT --to 192.0.2.1

This will have 192.0.2.1 as a "default" setting which matches all clients. Most clients who aren't .22 or .44 won't be matched by first two rules, and will be translated by the third one. The special clients .22 and .44 would have been matched with the last rule too, but there are dedicated rules just for them, which happen earlier in the chain and translate them into 192.0.2.2, so the processing won't reach the last rule.

You can use -I action to insert rule into arbitrary place in the populated chain (whereas -A always appends to the end of chain). For example, you can add yet another override into the front, above the catch-all last rule:

iptables -t nat -I POSTROUTING 3 -s 10.0.0.33 -o eth0 -j SNAT --to 192.0.2.2

This will put the new rule into third position, shifting the catch-all rule into fourth. Often, since the order amongst overrides isn't important, you always put them into first position; the only requirement here is to put the new rule above the broad matched "default" one.


For completeness, I'd explain how masks can be correctly used in firewall.

If you can control client IP address assignments, you can group clients that should be mapped in a similar way into blocks. Then use masks to match the block. The following will translate IP addresses 10.0.0.32÷.63 (inclusive) into 192.0.2.1 and 10.0.0.64÷.95 into 192.0.2.2:

iptables -t nat -A POSTROUTING -s 10.0.0.32/27 -o eth0 -j SNAT --to 192.0.2.1
iptables -t nat -A POSTROUTING -s 10.0.0.64/27 -o eth0 -j SNAT --to 192.0.2.2

The example above distributes clients into blocks of 32 addresses which corresponds to the mask /27=255.255.255.224. Notice that these masks are not correlated with netmasks that are used when assigning addresses to clients themselves; these serve different role. Also "first and last" addressed of the block can be used on client systems since these blocks aren't used as subnets but merely for firewall matching, because your subnet is 10.0.0.0/22 where addresses like .32, .63 etc don't play any special role.


If you can't group clients into such blocks, you can use ipset and the corresponding match to statically distribute clients. First create a dedicated ipset for each outgoing ip:

ipset create nat1 hash:ip
ipset create nat2 hash:ip

Instead of rules above, add NAT rules that match the corresponding set:

iptables -t nat -A POSTROUGING -m set --match-set nat1 src -j SNAT --to 192.0.2.1
iptables -t nat -A POSTROUGING -m set --match-set nat2 src -j SNAT --to 192.0.2.2

Now, you add clients to corresponding sets and they'll get NATed to the corresponding IP:

ipset add nat1 10.0.0.2
ipset add nat2 10.0.0.3
...

Address blocks also can be used as members of sets, so this is the most generic way. Read manual page to find out how to use them.