Port Knocking allows you to open or close a remote port in a server’s firewall using a secret sequence of ports (with approximately 141 trillion possible combinations).

It creates an extra layer of security for sensitive services such as SSH.


SERVER-SIDE

sudo apt update
sudo apt install knockd -y
sudo nano /etc/knockd.conf

Edit the configuration accordingly:

[options]
        UseSyslog

[openSSH]
        sequence    = 54321,12345,10101
        seq_timeout = 5
        command     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
        tcpflags    = syn

[closeSSH]
        sequence    = 12345,10101,54321
        seq_timeout = 5
        command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
        tcpflags    = syn

Note: the port will only be open to connections from the %IP% that knocked with the correct sequence, remaining closed to everyone else. The timeout is set to 5 seconds from the first to the last knock, with no other ports knocked in between.

Then,

sudo nano /etc/default/knockd

Change the following:

START_KNOCKD=1

Start the service.

sudo systemctl start knockd
sudo systemctl enable knockd
sudo ufw enable
sudo reboot

Ensure that port 22 is not open in the firewall.


CLIENT-SIDE

sudo apt update
sudo apt install knockd -y

Send the knock sequence to open the port:

knock -v myserver.com 54321 12345 10101 --delay 100

Send the knock sequence to close the port:

knock -v myserver.com 12345 10101 54321 --delay 100

Note: a delay of 100 milliseconds is recommended because packets may arrive out of order depending on the route they take.

The sequence can also be sent using telnet:

timeout 0.1 telnet myserver.com 54321 ; timeout 0.1 telnet myserver.com 12345 ; timeout 0.1 telnet myserver.com 10101
timeout 0.1 telnet myserver.com 12345 ; timeout 0.1 telnet myserver.com 10101 ; timeout 0.1 telnet myserver.com 54321

AUTHENTICATED PORT KNOCKING

LetMeIn offers authenticated port knocking with a simple authentication mechanism [Link].

Note: it requires nftables (not compatible with iptables) and the Rust toolchain. Encryption is based on a symmetric pre-shared key.

Build and install on the Server.

sudo apt update
sudo apt install git cargo build-essential -y
git clone https://github.com/mbuesch/letmein.git
cd letmein
cargo build --release --bin letmeind
sudo cp target/release/letmeind /usr/local/bin/ 
sudo mkdir -p /etc/letmein 
sudo cp install-server.sh /etc/letmein/
sudo nano /etc/systemd/system/letmeind.service

Add the following content.

[Unit]
Description=Letmein port-knocking daemon
After=network.target

[Service]
ExecStart=/usr/local/bin/letmeind -c /etc/letmein/letmeind.conf
Restart=on-failure

[Install]
WantedBy=multi-user.target

Start the service.

sudo systemctl daemon-reload
sudo systemctl enable --now letmeind

Configure nftables.

sudo apt install nftables
sudo systemctl enable --now nftables
sudo nano /etc/nftables/letmein.conf

The following is an example for reference and should be tailored to your system’s requirements.

table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;
    iif "lo" accept
    ct state established,related accept

    # jump to letmein chain
    jump letmein-input
  }

  chain letmein-input {
    # LetMeIn will insert rules here to allow per-IP access to protected resources.
    # Defines the default to reject
    tcp dport 22 reject
  }
}

Apply the rules.

sudo nft -f /etc/nftables/letmein.conf
sudo systemctl restart nftables

Notify LetMeIn of the new rule set.

sudo nano /etc/letmein/letmeind.conf

The content below is an example that will need to be updated once the client shares its key.

[KEYS]
00000000 = "PLACE_HOLDER_SECRET_KEY"

[RESOURCES]
00000022 = "port:22"

On the Client side, after installing LetMeIn, generate a key (secret + user ID).

letmein gen-key -u 00000000
  1. Copy the output (key) to /etc/letmein/letmein.conf or ~/.config/letmein/
  2. Paste the same key into the Server configuration.

Then test knocking and connecting to the service.

letmein knock -u 00000000 server.example.com 22
ssh [email protected]

BONUS

Automate port knocking in the SSH workflow.

nano ~/.ssh/config
Host server
  HostName server.example.com
  ProxyCommand bash -c "letmein knock -u 00000000 %h 22; nc %h %p"