What is port knocking?
The term "port knocking" refers to a technique where a client must first make connection attempts to a specific sequence of closed ports before being able to access the service/port they want to connect to. This works similar to using a secret knocking sequence for your front door, only opening it for people who knocked the correct sequence.
Until a correct knocking sequence is sent, the real target service is not reachable, thus hidden from port scanning or reconnaissance attacks. When the correct knocking sequence is registered, the port is opened only to the knocking client, no other IP address, keeping it invisible to others.
While port knocking can be a powerful technique to hide active services on your servers, be aware that simple network sniffing or man-in-the-middle attacks render it useless. Its primary advantage is to keep automated scanners and bots away from service like SSH, to prevent automated attacks.
Setting up basic port knocking
We assume you are running a debian-like linux distribution for this example; adjust for other operating systems as needed. We also assume you already have an SSH service installed and running on port 22.
Start by installing knockd, a small but powerful port knocking service and client:
sudo apt install knockdthen we create a basic knocking configuration in:
/etc/knockd.conf
[options]
logfile = /var/log/knockd.log
[openSSH]
sequence = 1234,2345,3456
seq_timeout = 15
command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 6543,5432,4321
seq_timeout = 15
command = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = synThe file defines two sequences, one to open the SSH port and a reversed version to close it again. Clients must first knock on ports 1234, 2345 and 3456 in order within 15 seconds, otherwise they cannot connect to the SSH service.
After the configuration file is created, start the knockd service:
sed -i 's/^START_KNOCKD=.*/START_KNOCKD=1/' /etc/default/knockd
sudo systemctl enable --now knockdAs a last step, configure your firewall to reject connection attempts to the ssh service:
sudo iptables -A INPUT -p tcp --dport 22 -j DROPConnecting via port knocking
On a client machine that wants to connect to the port knocking-enabled server we just set up, you will also need to install knockd:
sudo apt install knockdBut instead of enabled the knocking service, we only use the knock command contained in the package. The client can now knock on the configured ports:
knock your.server.ip 1234 2345 3456This opens the SSH port for the client machine, who can now SSH normally into the server. When the access is no longer needed, the client can knock the reverse sequence to close the SSH port again:
knock your.server.ip 6543 5432 4321Now the port is closed for the client's IP again and they will need to perform the knocking sequence again if they want to connect.
Automatically closing idle ports
Requiring a second manual knocking sequence to close the port can be annoying for users and is easily forgotten, potentially weakening the service protection. To fix these issues, we can write a script to check if a specific IP address is still connected over SSH, closing the port for them once they are gone:
/usr/local/bin/close_ssh_if_unused.sh
#!/bin/bash
IP=$1
CHECK_INTERVAL=180 # check every 3 minutes
while true; do
sleep $CHECK_INTERVAL
# Check if there are any established SSH connections from this IP
if ! ss -o state established '( dport = :ssh )' | grep -q "$IP"; then
# No active connections, remove the iptables rule and exit
/usr/sbin/iptables -D INPUT -s "$IP" -p tcp --dport 22 -j ACCEPT
exit 0
fi
doneDon't forget to make the script executable:
sudo chmod +x /usr/local/bin/close_ssh_if_unused.shNow we can reduce the knockd config to a single knocking sequence:
/etc/knockd.conf
[openSSH]
sequence = 1234,2345,3456
seq_timeout = 15
command = /usr/sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT && /usr/local/bin/close_ssh_if_unused.sh %IP% &
tcpflags = synBe careful to keep the & at the end, making sure the command keeps running in the background instead of blocking indefinitely. Since the script checks every three minutes, you also only have three minutes for your initial SSH connection, or the port will be closed again.
Using this script ensures that you cannot accidentally forget to close the SSH port after connecting to it, while causing almost no system load.