Table of Contents

Ubuntu - iptables - Configure Port Knocking

Port knocking works by covering the ports associated with a process behind a firewall until a specific, predetermined sequence of network activity occurs. These events could simply be connecting to various ports in a specific sequence.

The sequence we will use for this tutorial is ports:

Obviously, it is highly recommended to use a completely different range of ports.


Configure iptables for Port Knocking

sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -F
 
# Additional chains to support knocking.
sudo iptables -N KNOCKING
sudo iptables -N GATE1
sudo iptables -N GATE2
sudo iptables -N GATE3
sudo iptables -N PASSED
 
# Accepting all current connections.
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
 
# Accept all connections from the local machine,
sudo iptables -A INPUT -i lo -j ACCEPT
 
# Accept HTTP traffic (optional).
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
...
 
# Transfer all traffic that was not handled in the above rules to our KNOCKING chain
sudo iptables -A INPUT -j KNOCKING
 
 
 
# Configure the KNOCKING chain.
 
# Pass traffic from clients that have successfully completed all the knocks directly into the PASSED chain.
sudo iptables -A KNOCKING -m recent --rcheck --seconds 30 --name AUTH3 -j PASSED
 
# Test for each of the flags from the most restrictive to the least.
# There is a 10 second time limit before the previous knock expires.
# This requires completing the next stage of the knock within 10 seconds, and then connect to the SSH daemon in another 30 seconds.
sudo iptables -A KNOCKING -m recent --rcheck --seconds 10 --name AUTH2 -j GATE3
sudo iptables -A KNOCKING -m recent --rcheck --seconds 10 --name AUTH1 -j GATE2
 
# Send all traffic that has not matched back to GATE1.
sudo iptables -A KNOCKING -j GATE1
 
 
 
# Configure first gate.
# This rule will match when the protocol being used is "tcp" and when the port it is trying to access is "1111".
# If true, the recent module (called with -m recent), flags the requesting IP address with the name AUTH1. 
sudo iptables -A GATE1 -p tcp --dport 1111 -m recent --name AUTH1 --set -j DROP
 
# Drop all other packets,
sudo iptables -A GATE1 -j DROP
 
 
# Configure Second Gate.
# Simply strip the current flag and send it to the next rule.
sudo iptables -A GATE2 -m recent --name AUTH1 --remove
 
# Set the AUTH2 flag, indicating that the requesting address passed the second test, if the correct port was knocked on.
sudo iptables -A GATE2 -p tcp --dport 2222 -m recent --name AUTH2 --set -j DROP
 
# Second test not passed, so return to Gate1 in case this is beginning of a new knock sequence.
# This caters for instances where user accidentally hits first port twice. 
sudo iptables -A GATE2 -j GATE1
 
 
# Configure Third Gate.
# Clears all flags that have been given to our address.
sudo iptables -A GATE3 -m recent --name AUTH2 --remove
 
# Test whether the connection attempt matches the third knock target.
# If it does, we set the AUTH3 flag.
# Drop the packet afterwards.
sudo iptables -A GATE3 -p tcp --dport 3333 -m recent --name AUTH3 --set -j DROP
 
# Third test not passed, so return to Gate1 in case this is beginning of a new knock sequence.
# This caters for instances where user accidentally hits first port twice. 
sudo iptables -A GATE3 -j GATE1
 
 
# Configure Passed Chain.
# Reset the flags.
sudo iptables -A PASSED -m recent --name AUTH3 --remove
 
# Accept SSH connections from the users who have made it into this chain:
sudo iptables -A PASSED -p tcp --dport 22 -j ACCEPT
 
# Send all traffic that does not match back through our first chain.
sudo iptables -A PASSED -j GATE1

Test the Port Knocking

There are a number of utilities that can be used to generate the TCP packets that we are requiring for our port knocking configuration. We will use the nmap command, because it is present on most systems by default.

Nmap uses TCP packets by default. We need to tell it to drop to host discovery portion of its default behavior though so that those packets do not interfere with our knocking. In addition, we want our connection to time out after only a second, so that we can continue on to the next knock.

To do the first knock target as an example:

nmap -Pn --host_timeout 201 --max-retries 0 -p 1111 your_server

Therefore, our entire knock sequence could be represented by these commands:

nmap -Pn --host_timeout 201 --max-retries 0 -p 1111 your_server
nmap -Pn --host_timeout 201 --max-retries 0 -p 2222 your_server
nmap -Pn --host_timeout 201 --max-retries 0 -p 3333 your_server

We would then have 30 seconds to connect with our SSH client.

To automate this a bit, use a “for” loop to iterate through our port sequence and then pass it off to the SSH client:

for x in 1111 2222 3333; do nmap -Pn --host_timeout 201 --max-retries 0 -p $x your_server && sleep 1; done && ssh user@your_server

This will knock on the first port, wait for a second, knock on the next, and so on until the sequence is complete. It will then attempt to connect the the server's SSH daemon.


Script this

vi knock.sh

and populate as:

knock.sh
#!/bin/bah
 
ports="1111 2222 3333"
host="your_server"
 
for x in $ports
do
    nmap -Pn --host_timeout 201 --max-retries 0 -p $x $host
    sleep 1
done
ssh user@${host}

Save and close the file.


Make the file executable with this command:

chmod 755 knock.sh

Now, we can connect to our server by typing:

./knock.sh