logo

SSH knock knock

Sun February 10th 2019

I used to have a quite elaborate setup to protect my ssh server from brute force attacks. All iptables based, with increasing drops depending on the time passed and count of ssh connect attempts. However, last week my rpi server was under attack of quite a large and distributed brute force attempt. Nothing to worry about, but it annoyed me ;-) I ended up with huge failed login attempt logs (check your lastb) of over 25k attempts per day ..
After initially trying to find a solution with geoip (too complex, required to add tcp wrappers around ssh etc, did not want to go there), and changing outside ssh listening port (too simple and easily re-discovered), I decided to implement a ‘knock’ before entering setup. Brilliantly simple, hard to discover and easy to extend to a more complex setup when required.

old: So instead of ssh rate limiting (relevant part of iptables-restore script):

# ratelimit accept ssh
-N SSHBF
-A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -m recent --name sshbf --set -j SSHBF
-A SSHBF -m recent --name sshbf --rcheck --hitcount  4 --seconds    300 -j DROP
-A SSHBF -m recent --name sshbf --rcheck --hitcount  6 --seconds   7200 -j DROP
-A SSHBF -m recent --name sshbf --rcheck --hitcount 12 --seconds 345600 -j DROP
-A SSHBF -m recent --name sshbf --rcheck -j LOG --log-prefix "NFssh: " --log-level info
-A SSHBF -m recent --name sshbf --rcheck -j ACCEPT

new: I setup a ssh port knock, replace the your-knock-port in below lines with your preferred knock port, eg any port between 1025 and 65535.

# ssh knock knock
-A INPUT -p tcp --dport <add your-knock-port here> --syn -m recent --name knock --set -j DROP
-A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -m recent --name knock --rcheck --seconds 30 -m limit --limit 6/hour --limit-burst 1 -j ACCEPT
-A INPUT -p tcp --dport 22 -j DROP

The above lines drop all ssh traffic from ip’s that try to access my ssh server without knocking first, knock before you enter ;-) As an extra any traffic coming through is still rate limited but in a simpler setup with just one -m limit.
Daily use is pretty simple. Use telnet, ssh - anything capable of sending a tcp syn to the knock port - and connect to your ssh port as usual.

telnet <your-host> <your-knock-port> 

Stop this session with Ctrl-C; you have just knocked on the door, and ssh will be open for 30secs. Follow up by:

ssh <your-host> -p <your-ssh-port>

and login as usual. The ssh port will remain accessible for 30secs in my setup, you can change this if needed.

Result: Where the rate limiting setup still had unsolicited access from outside, this simple knock setup reduced it to zero attempts coming through!! And since nothing was visible on the outside ports anymore even all unsolicited ssh login attempts reduced to almost nothing :-)

Note: Although this setup will help in further hiding your ssh server you still should follow all the SSH hardening howto’s (no PermitRoot, use keys etc) and not rely on ssh knocking alone!