How to redirect port 80 to 8080 while keeping 8080 closed to the Internet?
Solution 1:
This schematic should help you understand how packet handling is done:
The filter/INPUT rules see only packets after they were NATed in nat/PREROUTING, so by default can't tell the difference between receiving a packet on port 8080 because a client sent it there directly, from receiving a packet on port 8080 because a client sent it on port 80 which was then redirected to port 8080. In both cases, they see a packet arriving on port 8080. So you need an additional information to be able to tell those two cases apart.
First thing first: this rule should be removed since it's useless:
-A INPUT -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
because as explained filter/INPUT won't see a packet on port 80: it's now arriving on port 8080. Don't trust tcpdump
blindly here: as seen in the schematic, tcpdump
(AF_PACKET) sees packets before all this, so will see port 80.
You can use iptables's
conntrack
match which queries netfilter's connection tracking system and has thus access to the missing information: for this case whether the current incoming packet is part of a connection that underwent a DNAT transformation or not, using--ctstate DNAT
(REDIRECT is a special case of DNAT, just like MASQUERADE is a special case of SNAT). There are other options for this match like--ctorigdstport
etc. which can probably achieve similar results.I'll just put the important rules about this case, rather than all, to illustrate the explanation. Just use them, when needed, at the correct place.
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
probably right after the generic
... ESTABLISHED,RELATED -j ACCEPT
line:iptables -A INPUT -p tcp --dport 8080 -m conntrack --ctstate DNAT -j ACCEPT iptables -A INPUT -p tcp --dport 8080 -j DROP
(or don't add this last DROP rule if there's later a catch-all drop rule).
The first INPUT rule will match traffic arriving at port 8080 which was DNATed (here from initially arriving at port 80), while the second will drop what's remaining arriving at port 8080: direct connection attempts.
Note: if you're wondering why the
state
match and theconntrack
match look similar, thatstate
has been superseded byconntrack
. Actually, internally the kernel modulext_conntrack.ko
handles thestate
match in addition to theconntrack
match, for backward compatibility.An other method to transmit this information is by using a packet mark, which is a more generic method and can be applied to many other cases. It's an arbitrary number that will mark the packet (only in the kernel, not on the wire) and can be used in a few places, including the iptables
mark
match to alter decisions. As theMARK
target is not a terminating rule it can be used before the actualREDIRECT
rule. As they are displayed back in hexadecimal, I set them also in hexadecimal. The value is yours to choose what it means. Here 0x80 (which is decimal 128) will convey the information "hit port 80 and was redirected to port 8080" all by itself, so no need to recheck the port later, checking the mark validates all. As usual it's the first packet of the connection that counts: each other packet of this connection is handled by the generic conntrack stateful rule... ESTABLISHED,RELATED -j ACCEPT
.iptables -t nat -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 0x80 iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
After the generic
... ESTABLISHED,RELATED -j ACCEPT
line:iptables -A INPUT -m mark --mark 0x80 -j ACCEPT iptables -A INPUT -p tcp --dport 8080 -j DROP
I must also warn you against the danger of using a REJECT rule without first dropping (rather than rejecting) INVALID state packets. This might get you random connection reset issues in certain rare congested cases when TCP packets arrive in the wrong order. Relevant documentation of this issue is currently being pondered for addition:
So, instead of:
-A INPUT ... -j REJECT
do consider using:
-A INPUT ... -m conntrack --ctstate INVALID -j DROP -A INPUT ... -j REJECT
Solution 2:
REDIRECT just changes the port number, so if the connection was:
client -> public_addres:80
it becomes
client -> public_address:8080
So, you will not be able to block and accept at the same time by doing what you are doing.
First remove this rule:
-A INPUT -p tcp -m tcp --dport 8080 -m state --state NEW,ESTABLISHED -j ACCEPT
And use DNAT instead of REDIRECT, something like:
-A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to 127.0.0.1:8080
That might work.
You could also listen just on 127.0.0.1:8080 (as opposed to 0.0.0.0:8080).