I have quite some VPS running Ubuntu LTS 18 with iptables as firewall. At the moment I am running SSH on port 22, on which there are many, many login attempts from foreign IP addresses.

I want to limit these hits by redirecting an arbitrairy port number (for example 2222) to port 22 via IPTables. For reasons I do not want to adjust config of SSH script to listen on port 2222.

As a "bonus" I want to be able to keep port 22 open for ONLY IP x.x.x.x (for now

I have tried the following:

Exclude all but my own IP:

iptables -A INPUT -s -i venet0 -p tcp -m tcp --dport 22 -j ACCEPT

This works well.

Now to redirect 2222 to 22:

iptables -t nat -A PREROUTING -i venet0 -p tcp --dport 2222 -j REDIRECT --to-port 22

This doesn't seem to work. Only if I open port 22 the redirection is working. But than the port is open to all visitors.

Could someone shed some light on this? Thanks!

2 Answers 2


If you want only 1 IP address to be able to access port 22, but you also want to redirect some other port to the local port 22, then you still need to open up port 22 to everyone, as the INPUT rule is processed after the nat REDIRECT rule.

One way could be:

iptables -t nat -A PREROUTING -s --proto tcp --dport 22 -j ACCEPT
iptables -t nat -A PREROUTING --proto tcp --dport 22 -j REDIRECT --to-port 65535
iptables -t nat -A PREROUTING --proto tcp --dport 2222 -j REDIRECT --to-port 22

iptables -A INPUT --proto tcp --dport 22 -j ACCEPT
iptables -A INPUT --proto tcp --dport 65535 -j DROP

As the nat PREROUTING table is processed first, the source address to port 22 is accepted (meaning no NAT is performed). Other source addresses trying to reach port 22 will hit the next rule, redirecting incoming port 22 packets to port 65535 (unfortunately you can't use DROP in the PREROUTING nat table).

Next the connections to port 2222 are redirected to port 22, all incoming packets to port 22 are accepted, and finally incoming connections to port 65535, which are the "other" connections to port 22, are dropped.

Note I'm assuming you have a line such as this as one of your first INPUT rules:

iptables -A INPUT  -m state --state ESTABLISHED,RELATED -j ACCEPT

Otherwise, you might have an outgoing connection having source port 65535, and the responses to that port would be blocked. Alternatively, add TCP flag matching to the 65535 rule, matching only SYN packets. However it's always a good thing to allow ESTABLISHED,RELATED packets early in the INPUT chain for efficiency.

Feel free to add -i venet0 if those rules should only be applied to packets coming in on that interface.

  • I can follow your suggested sequentially ordered filtering. For the second rule it reports NAT table is not for filtering, DROP inhibited (I have version 1.6.0). If I instead use a redirect to a non-existing port, everything works. Not the prettiest. Any suggestions? At least I thank you for your given solution!
    – tinus88
    Commented Oct 21, 2019 at 13:41
  • Argh I had forgotten about that. I've edited my answer to fix this
    – wurtel
    Commented Oct 22, 2019 at 7:55
  • Thank you, it all works as I wanted to!
    – tinus88
    Commented Oct 22, 2019 at 10:35

You have to have port 22 open to pass traffic to it from port 2222, if ssh and your iptables setup are on the same machine, its possible to bind your ssh socket to your loopback interface(, which would allow you to leave ssh on port 22, and you can still pass traffic from the public facing interface to your local machine's loopback.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .