1

Hopefully somebody can help me or at least get me pointed in the correct direction. I've got a NAT instance on AWS which is providing outbound traffic for a host to the internet. This works just fine. What I need to do is take an arbitrary port (lets use port 26 as an example) from the HOST and on the NAT machine, translate that to appear to come from the NAT machine on port 25. This rule is what I'm trying to make work:

iptables -A POSTROUTING -t nat -o eth0 -p tcp --dport 26 -j SNAT --to 172.17.2.125:25

The rule currently will happily change the IP address but leaves the outgoing port unaltered.

A redirect will happily change the port but won't change the source IP (so it won't leave the box on the correct public IP).

I've thought about using something like pfSense or some other firewall product which I think might be able to do this but it seems like overkill for something which seems "simple". I've tried googling like crazy but I must be googling the wrong terms... I've tried the "transparent proxy" but that didn't seem to work either.

Updating this a bit

HOST = 172.17.1.125, outbound SMTP connection via port 26 NAT = 172.17.2.126 DESTINATION = 216.1.2.3 port 25 (standard SMTP port)

What is needed is to have

HOST (internal) -> NAT -> DESTINATION

To make this happen correctly, NAT server must translate port 26 traffic to 172.17.2.126 port 25.

  • Could you explain why you need to appear the connection coming from port 25? SNAT works by re-assigning port numbers so different source addresses will have different ports (otherwise it can't track the connection, and restore the correct IP address for incoming packets). So you'll be able to map at most one particular source with one particular port to the NAT IP and port 25. (Also, you need --sport, because it NATs the source port). – dirkt Jul 12 '17 at 9:59
  • The connection needs to appear to be coming from the ip address 172.17.2.125 (so that AWS routes it our the correct elastic IP) but the destination port must be 25 (this is an outbound SMTP connection so the receiving servers are all listening on port 25) Also, --sport won't match because the incoming connection which is being NATted is coming in on port 26 and that appears to the NAT instance as the destination. I've tried using --sport in a logging rule and it doesn't get hit – John Mitchell Jul 12 '17 at 17:21
  • I still don't understand the problem. Am I missing something? If the destination port must be 25, then the source port doesn't matter (the listening servers don't care which port the incoming connection comes from), so you can just do normal SNAT/MASQERADE. – dirkt Jul 12 '17 at 20:06
  • 1
    I've updated the question a bit so as to hopefully make it clear. The source port matters because that is what drives the IP on which it will submit out via. Port 26 should go out 172.17.2.126 port 25, port 27 goes out 172.17.2.127:25, port 27 = 172.17.2.128:25, etc. AWS requires big instance sizes for lots of IPs (even internal) but our MTA software can send SMTP traffic via a large collection of alternate ports. This way we can have many email servers feeding a NAT instance which has all the public IPs – John Mitchell Jul 12 '17 at 20:53
  • I just tested (with nc and namespaces, and tcpdump to verify), and it happily translates both source IP and source port for me, both for --sport and --dport to determine the source ip. Of course, when using the destination port to choose source ip, you can't change the destination port to something canonical like 25, that would require DNAT... No idea what goes wrong on your side. I run kernel 4.8.5, if this matters. – dirkt Jul 13 '17 at 11:17
1

Test setup: Two network namespaces ns0 (your external mail server), ns1 (your HOST). Main network namespace (your NAT) does forwarding and nat. Two veth-pairs connecting namespaces:

ns0 --->     <------ main ns ----->     <--- ns1
veth0b   --- veth0a      veth1a     --- veth1b
10.0.0.1     10.0.0.254  10.0.1.254     10.0.1.1

tcpdump on veth0a and veth1a. nc -k -l -p 9999 listens on port 9999 in ns0 as "mail server". Client via various nc commands in ns1. Normal connection, no nat, random source port per default:

$ echo foo | nc 10.0.0.1 9999 -q0
veth*a: IP 10.0.1.1.60560 > 10.0.0.1.9999 
veth*a: IP 10.0.0.1.9999 > 10.0.1.1.60560

Match on source port (I added -s to all matches to avoid surprises, but it also works without -s):

# iptables -t nat -A POSTROUTING -o veth0a -s 10.0.1.1/32 -p tcp --sport 8001 -j SNAT --to 10.0.1.81:9900
$ echo foo | nc 10.0.0.1 -p 8001 9999 -q0
veth1a: IP 10.0.1.1.8001 > 10.0.0.1.9999 
veth0a: IP 10.0.1.81.9900 > 10.0.0.1.9999
veth0a: IP 10.0.0.1.9999 > 10.0.1.81.9900
veth1a: IP 10.0.0.1.9999 > 10.0.1.1.8001

Match on destination port (doesn't reach "server", because rules can't rewrite destination port):

# iptables -t nat -A POSTROUTING -o veth0a -s 10.0.1.1/32 -p tcp --dport 7002 -j SNAT --to 10.0.1.82:9900
$ echo bar | nc 10.0.0.1 7002 -q0
veth1a: IP 10.0.1.1.38614 > 10.0.0.1.7002 
veth0a: IP 10.0.1.82.9900 > 10.0.0.1.7002
veth0a: IP 10.0.0.1.7002 > 10.0.1.82.9900
veth1a: IP 10.0.0.1.7002 > 10.0.1.1.38614

As you can see, both source address and source port get translated fine. In SNAT (source nat), you can only translate source address and port, that's what the S means, and that's what the documentation you linked says. Conversely, in DNAT, you can translate destionation address and port. SNAT happens in POSTROUTING (must be on veth0a in this setup), DNAT in PREROUTING (must be on veth1a in this setup). So you can't combine both in one rule, and you can't do a DNAT after SNAT, because PREROUTING happens before POSTROUTING.

I am getting the impression that what you really want to do is to access various mail servers, but control the source address they see you under by replacing the canonical 25 port with other destination ports. For example, mail.xxx.com:26 should contact mail.xxx.com:25 coming from one address, and mail.xxx.com:27 should contact mail.xxx.com:25 coming from another address. (I really do hope this is not connected to sending spam mail...) So the source port doesn't matter, the given destination port determines source address, and the destination port should be changed back to 25. Is this correct? If yes, that can't be done easily on Linux due to the way nat works.

And if you can convince you don't need it for spam mail, I can think of two better options (depending on what your MTA software can do).

  • Thanks for the explanation helps quite a bit. I'm pretty new to iptables so I misunderstood the documentation :) You basically have it exactly correct on what we are attempting to do (minus the spam part, each customer gets an ip so we can manage reputation). We are moving our system to AWS and we are limited unlike in our own data centres (where we have our own FWs and we can have as many internal IPs as we want). To get more internal IPs, AWS requires you to increase your instance size and I've been trying to reduce the complexity. I've love to hear some other options! – John Mitchell Jul 15 '17 at 21:11
1

I managed to get somebody who could help me out with this problem. Here are the rules we used to map ports through to outgoing IP addresses in AWS:

First, you have to have this running through a NAT instance to make it work. There are lots of resources on how to configure that.

iptables -t mangle -A PREROUTING -p tcp --dport 27 -j MARK  --set-mark 27
iptables -t nat -A PREROUTING -p tcp --dport 27 -j DNAT --to :25
iptables -t nat -A POSTROUTING -m mark --mark 27 -j SNAT --to-source 10.0.0.11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.