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 127.0.0.1:3306
That is, you want packets to port 34306 on an external interface to be redirected to 127.0.0.1, port 3306. And this would work, except for the fact that once the destination is rewritten to 127.0.0.1, the packet becomes a Martian Packet (coming in from the outside with a destination of 127.0.0.1). 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:
- Your question mentions port 33060, but your code uses port 34306. I've used the latter.
- Make sure MySQL is actually listening to 127.0.0.1:3306. It can use a Unix domain socket for local communications, not an Internet domain (TCP/IP) socket, and that's not networked.
- You only need to
SYN packet. Stateful NAT will automatically translate all other packets. This makes things marginally faster.
- 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)