4

For a while now (introduced in version 1.3 I believe), iptables' conntrack module can track two virtual states, SNAT and DNAT:

SNAT A virtual state, matching if the original source address differs from the reply destination. DNAT A virtual state, matching if the original destination differs from the reply source.

On my router/firewall host, I have some rules for SNAT like this:

# SNAT
iptables -t filter -A FORWARD -i $FROM_IFACE -o $TO_IFACE -s $FROM_IP -d $TO_IP -m conntrack --ctstate NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -t filter -A FORWARD -i $TO_IFACE -o $FROM_IFACE -s $TO_IP -d $FROM_IP -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -o $TO_IFACE -s $FROM_IP -d $TO_IP -j SNAT --to-source $SNAT_IP

# DNAT
iptables -t nat -A PREROUTING -i $FROM_IFACE -d $FROM_IP -p $PROTO --dport $PORT -j DNAT --to-destination $TO_IP
iptables -t filter -A FORWARD -i $FROM_IFACE -o $TO_IFACE -d $TO_IP -p $PROTO --dport $PORT -j ACCEPT
iptables -t filter -A FORWARD -i $TO_IFACE -o $FROM_IFACE -s $TO_IP -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

After a fair bit of googling, I couldn't find any example of iptables rules using those "new" SNAT or DNAT states, but I tried anyway to replace ESTABLISHED,RELATED by SNAT or DNAT, like this:

# SNAT
iptables -t filter -A FORWARD -i $FROM_IFACE -o $TO_IFACE -s $FROM_IP -d $TO_IP -m conntrack --ctstate NEW,SNAT -j ACCEPT
iptables -t filter -A FORWARD -i $TO_IFACE -o $FROM_IFACE -s $TO_IP -d $FROM_IP -m conntrack --ctstate SNAT -j ACCEPT
iptables -t nat -A POSTROUTING -o $TO_IFACE -s $FROM_IP -d $TO_IP -j SNAT --to-source $SNAT_IP

# DNAT
iptables -t nat -A PREROUTING -i $FROM_IFACE -d $FROM_IP -p $PROTO --dport $PORT -j DNAT --to-destination $TO_IP
iptables -t filter -A FORWARD -i $FROM_IFACE -o $TO_IFACE -d $TO_IP -p $PROTO --dport $PORT -j ACCEPT
iptables -t filter -A FORWARD -i $TO_IFACE -o $FROM_IFACE -s $TO_IP -m conntrack --ctstate DNAT -j ACCEPT

It seemed to work, and this method has at least one benefit I could notice: my firewall used to drop RST packets going from my internal hosts to the Internet (since they are in INVALID state), but with this new method, they were allowed to pass.

Unfortunately, although convenient, I'm not sure if this method is really suitable, because my theoretical knowledge about networks is not sufficient to understand if it's too permissive (i.e. allow some unwanted packets from outside my LAN to reach inside).

I think my question could be worded like this: can a packet have the SNAT or DNAT state while not also have the ESTABLISHED or RELATED state (except, obviously, the first one which has the NEW state) ?

Note: I tried to log such packets but to my knowledge it's impossible, since iptables accepts only one --ctstate option, and ! can't be used within it (in other words, I can't say, or at least couldn't find a way to say, "log packets which have SNAT state but not ESTABLISHED or RELATED state"). If there is an alternate method to log them I didn't think of, this would also be very welcomed.

EDIT 1: after some trial and error, I realized I was wrong (hence the stroked text): some packets are still in state INVALID and thus finally dropped.

EDIT 2: if using SNAT/DNAT in place of ESTABLISH,RELATED is not safe, please provide some concrete examples of cases where packets could be in those former states without being in the latter ones.

0

2 Answers 2

3

Thanks to @A.B's advice about logging, I could do some more tests and here are the results, as well as answers to my own questions, in the hope this will help other people who, like me, didn't find anything on the web about the states SNAT and DNAT, and/or their ability to replace ESTABLISHED,RELATED for matching.

So, on a moderately busy home network (a couple of hosts accessing the Internet over SNAT, as well as some virtual machines hosting servers (HTTP/HTTPS, SMTP, IMAP, etc etc) publicly accessible over DNAT), in five days, I didn't see a single line of log about a packet which would be in SNAT or DNAT state, and not also ESTABLISHED or RELATED.

So, the answer to the question "can a packet have the SNAT or DNAT state while not also have the ESTABLISHED or RELATED state" is no.

Since my real worry was that matching against SNAT or DNAT instead of ESTABLISHED,RELATED to allow packets to get into my LAN could be too permissive, this would seem reassuring at first, but I found out that it's not a good idea.

In fact, it appears that this is, on the contrary, less permissive: during my tests with those rules, I saw a small but non-negligible number of packets in state RELATED that were dropped, mostly ICMP type 3, codes 1 and 3 (respectively destination host unreachable and destination port unreachable), coming from the Internet and destined to the hosts inside my LAN. In other words (and if I understand networks correctly), my hosts tried to make some connections to the Internet, the remote routers responded that the connection couldn't be made, and my own firewall/router host blocked those responses. This can't be good.

So, the answer to the underlying question "Is it a good idea to replace ESTABLISHED,RELATED by SNAT or DNAT", is, again, no.

2

I wouldn't use your method for security. Imagine you rework your network and don't have to use SNAT anymore. What would happen? That's the obvious part. There might be hidden issues lurking somewhere.

As much as possible, the NAT rules and filter rules should stay independant, unless there's a good reason not to. I'm not sure you have a good reason. A good reason would be to treat differently NATed traffic from non NATed traffic (eg: the server DNATs a service to an other server behind, but doesn't allow to be used as router for direct access to the server/service behind).

Now about your note: just divide the problem in multiple steps.

This incorrect rule:

iptables -A FORWARD -m conntrack --ctstate SNAT ! --ctstate ESTABLISHED,RELATED -j LOG

can be replaced with:

iptables -N subtest
iptables -A FORWARD -m conntrack --ctstate SNAT -j subtest
iptables -A subtest -m conntrack ! --ctstate ESTABLISHED,RELATED -j LOG

or instead with RETURN and reversed logic:

iptables -N speciallog
iptables -A FORWARD -j speciallog
iptables -A speciallog -m conntrack ! --ctstate SNAT -j RETURN
iptables -A speciallog -m conntrack ! --ctstate ESTABLISHED,RELATED -j LOG

or, using marks (not if marks are used elsewhere (and not wanting to bother with masks)) can also achieve the same instead:

iptables -A FORWARD -m conntrack --ctstate SNAT -j MARK 1
iptables -A FORWARD -m mark --mark 1 -m conntrack ! --ctstate ESTABLISHED,RELATED -j LOG

For each method, one can imagine chaining more tests easily (by incrementing the mark for the mark method).

3
  • Thanks. Using an additional rule to exclude the unwanted packets from the log seems so obvious that... I'm feeling dumb :-/
    – MoonSweep
    May 5, 2018 at 21:05
  • Note that I didn't accept the answer yet, because if using SNAT/DNAT in place of ESTABLISH,RELATED is not safe, I expected some concrete examples. Sorry if I wasn't clear enough.
    – MoonSweep
    May 5, 2018 at 21:32
  • no problem, I couldn't find a concrete example
    – A.B
    May 5, 2018 at 21:40

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.