The main rules are:

 /sbin/iptables --policy INPUT DROP
 /sbin/iptables --policy FORWARD DROP
 /sbin/iptables --policy OUTPUT ACCEPT
 /sbin/iptables -A INPUT -i lo -j ACCEPT
 /sbin/iptables -A INPUT -i $OUTIF -m state --state RELATED,ESTABLISHED -j ACCEPT

I want mysql to listen on lo at port 3306 port. And I also want it to listen at port 33060 on the WAN interface (eth0) as I want to keep it away from bots.

I've tried several redirections with no success. For example:

/sbin/iptables -A INPUT -p tcp -i $OUTIF --dport 34306 -j ACCEPT
/sbin/iptables -A PREROUTING -t nat -p tcp -s 0/0 --dport 34306 \
               -j DNAT --to

How can I do this?


I'm going to use redir however I still want to know how to do this with iptables.

  • i think ssh-tunnel may be useful in this case for more secure . – Rahul Patil Jan 18 '13 at 4:05

This one can work, but only if you allow access to port 3306 from the outside: (but this doesn't work)

iptables -t nat -A PREROUTING -p tcp --dport 34306 --syn -j DNAT --to :3306

What you ultimately want to do though, is: (but this doesn't work either)

iptables -t nat -A PREROUTING -p tcp --dport 34306 --syn -j DNAT --to

That is, you want packets to port 34306 on an external interface to be redirected to, port 3306. And this would work, except for the fact that once the destination is rewritten to, the packet becomes a Martian Packet (coming in from the outside with a destination of Martian packets are filtered out normally and siletly, and you really, really want this.

There's a more roundabout solution involving firewall marks. The theory: when a packet comes calling for port 34306/tcp on your external interface, we simply mark it as acceptable, then rewrite it to seem like it came from 3306/tcp. Traffic to 3306/tcp with the mark is allowed. All other traffic to 3306/tcp is rejected (either explicitly or implicitly, via the default policy). The code:

IFACE=eth0 # or whatever
MARK=42 # not-so-random random number
iptables -t mangle -A PREROUTING -p tcp -i $IFACE --dport $HIPORT -j MARK --set-mark $MARK
iptables -t nat -A PREROUTING -p tcp --dport $HIPORT --syn -j DNAT --to :$REALPORT
iptables -A INPUT -p tcp -m mark --mark $MARK -j ACCEPT
iptables -A INPUT -p tcp --dport $REALPORT -j DROP # explicitly dropping

The last rule is where you can act on disallowed packets. I like to log all dropped packets, so my chains fall through to the last two rules, which are a -j LOG and -j DROP. So in my case, I wouldn't need it but your own mileage will, of course vary.

I just tested this and it works on my setup. It's a bit more circuitous than expected, but such is life with netfilter.

If you use stateful filtering, add --syn to rule 3 (-m mark in the INPUT chain) and stick it before your state inspection rules. If you explicitly drop/reject packets with rule 4 and you do stateful firewalling, you should add --syn to that too. This is a tiny bit more complicated (by eight whole bytes), but the mark checking rules only apply to the SYN (first) packet of each TCP connection. Once the accept/reject decision has been made, the state inspection rules do the rest, so the firewall doesn't have to keep checking marks for every single packet. No reason to waste CPU cycles, and it keeps your network performance high.

Some things to note:

  1. Your question mentions port 33060, but your code uses port 34306. I've used the latter.
  2. Make sure MySQL is actually listening to It can use a Unix domain socket for local communications, not an Internet domain (TCP/IP) socket, and that's not networked.
  3. You only need to DNAT the SYN packet. Stateful NAT will automatically translate all other packets. This makes things marginally faster.
  4. You don't have to specify -s 0/0 explicitly, just leave the source out entirely.

The spirit of the question: security by obscurity is a harsh mistress. You're not really protecting yourself by changing the port MySQL is listening on. Port scanners are automated things, and they don't mind scanning all ports on a system. :) I'd recommend either using ssh with the -L option to tunnel in in a secure fashion, or, if you want to get fancy, a VPN. But sometimes, life gives you lemons and you have to make lemonade with netfilter (which is probably one of the few things you can't do with it)

  • Thanks Alexios however the port is still filtered. Maybe because the default policy is to drop packets; 1- it's just a typo in the post. 2- it's listening as per netstat's output. 3- Cool. 4- OK. About the last comment. I'm a big fan of ssh bindings however I'm forced to go with the iptables redirection as a lesser evil. It's a requirement :-( – eproyectos Feb 8 '12 at 18:54
  • Your rule works only if I allow INPUT to both ports 3306 and 34306 and I make mysql listen to eth0. If I drop connections to eth0:3306 the 34306 redirection stops working (so I guess that that rule is not redirecting to the loopback interface) – eproyectos Feb 8 '12 at 19:09
  • Oh, of course. It's Martian packets. I've got a better answer, and I'll update this soon. – Alexios Feb 8 '12 at 20:23
  • Thanks Alexios for your excellent answer. It gives me many ideas to do further playing :-) – eproyectos Feb 9 '12 at 16:42

Just take a peek at tools like nmap, consider that the miscreants commanding botnets have unlimited time and machines at hand, and estimate how much time "hide MySQL at a (easily guessable) port" will buy you.

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.