How does iptables filtering in the FORWARD or INPUT chain interact with NAT?
This page seems to suggest that, if there is a rule in the PREROUTING
chain of the nat
table that translates destination 1.2.3.4:80
to 10.1.1.1:8080
, then the rules in the INPUT
and FORWARD
chain should match on 10.1.1.1:8080
, not 1.2.3.4:80
.
What if one wants to implement an order of operation more like what's outlined here? That is, how do you perform filtering on characteristics of packets before NAT?
One possibility seems to be using the PREROUTING
chain of the raw
table. The issue is that conntrack isn't available in raw/PREROUTING
(see note 1), and e.g. a non-initial UDP fragment will not be related to the initial fragment and will produce unexpected matches.
Please let me know if I've described the dilemma clearly, and whether there's any workaround.
Note 1:
Correct me if I'm wrong, but when I tried to use conntrack in raw/PREROUTING
, all initial SYN packets were marked "INVALID
" rather than "NEW
". It also seems like, in the Netfilter flowchart, "conntrack" is after raw/PREROUTING
.
Solution 1:
You don't need any raw/PREROUTING
rules. You can use the conntrack
match to filter the packets by original (before translation) destination/source address/port number. The conntrack
is a successor of the older state
match. It can check various additional metadata, related with conntrack entry
(and NAT).
Couple of examples:
# allow any port-forwarded packets
iptables -t filter -A FORWARD -m conntrack --ctstate DNAT -j ACCEPT
# check the original destination address of DNATed packets
iptables -t filter -A FORWARD -p tcp --dport 8080 -m conntrack --ctstate DNAT --ctorigdstport 80 --ctorigdst X.X.X.X --ctdir ORIGINAL -j ACCEPT
For more details check the output of iptables -m conntrack --help
and man iptables-extensions
.
Solution 2:
The Packet flow in Netfilter and General Networking describes the relations between the different hooks seeing a packet. Here's a smaller part of it:
So if you want to interact statefully with packets before nat, the logical choice is mangle/PREROUTING
: the conntrack hook already tracked the packet: it won't get an INVALID state, but nat still didn't happen.
Just remember that iptables' nat sees only the first packet and then everything else is handled directly by conntrack, still happening at the same place: between mangle/PREROUTING
and the routing decision.
The other method is described in Anton Danilov's answer: by querying conntrack to check the former address from its lookup table.