Securing SSH access with Port Knocking using iptables

This is working for Ubuntu and Debian and might be slightly different for other distributions. However, the rules that are implemented will work on every distribution's iptables.

First install the package iptables-persistent:
apt-get install iptables-persistent

Do not save the current configuration when asked as we will create a new one.

Now put the following contents into /etc/iptables/rules.v4:

:KNOCKING - [0:0]
#  Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d -j REJECT

#  Accepts all established inbound connections

#  Would allow incoming connections from a specified network range without knocking
# -A INPUT -p tcp --dport 22 -s X.X.X.X/32 -j ACCEPT

# Allow webserver ports to the outside world
-A INPUT -p tcp --dport 80 -s -j ACCEPT
-A INPUT -p tcp --dport 443 -s -j ACCEPT

# Mailserver: smtp and smtps
-A INPUT -p tcp --dport 25 -s -j ACCEPT
-A INPUT -p tcp --dport 465 -s -j ACCEPT

# Mailserver: imap and imaps
-A INPUT -p tcp --dport 993 -s -j ACCEPT
-A INPUT -p tcp --dport 143 -s -j ACCEPT

# Mailserver: starttls
-A INPUT -p tcp --dport 587 -s -j ACCEPT

# Allow ICMP
-A INPUT -p icmp -s -j ACCEPT

######## TRAFFIC chain for Port Knocking.
-A KNOCKING -m state --state NEW -p tcp --dport 22 -m recent --rcheck --seconds 30 --name knock3 -j ACCEPT
-A KNOCKING -m state --state NEW -m tcp -p tcp --dport 33323 -m recent --rcheck --seconds 20 --name knock2 -m recent --name knock3 --set -j DROP
-A KNOCKING -m state --state NEW -m tcp -p tcp --dport 44434 -m recent --rcheck --seconds 10 --name knock1 -m recent --name knock2 --set -j DROP
-A KNOCKING -m state --state NEW -p tcp --dport 55535 -m recent --name knock1 --set -j DROP



If you do not have a mailserver or webserver, remove the lines accordingly. By restarting iptables-persistent, these rules will be applied. Your current SSH session will be kept established, but do not close it before you've tested that everything works fine!
service iptables-persistent restart

For new sessions, the SSH port will be closed, so let's create a script to knock the ports. Save the following into a script e.g.

ports="55535 44434 33323"
for x in $ports
    nmap -Pn --host_timeout 201 --max-retries 0 -p $x $host
    sleep 1
ssh root@$1

Now execute ./ whereas you replace the IP with the IP of your server. You should be able to login!

So you have to knock the ports 55535, 44434 and 33323 now in this sequence to open the SSH port.

You might want to change these ports to different ones and additionally change the SSH port to a different one to even more improve security!