Running knockd

If you've ever run an Internet facing server you'll quickly discover people and/or bots trying to connect. Below are some stats generated by logging rejected inbound traffic with iptables for a week:

PortProtocolConnection attempts
TCP/22SSH792
TCP/80HTTP120
TCP/443HTTPS67

So that's just shy of a thousand connection attempts on a system that's passively listening. OK the Internet being what it is, none of this is very surprising. However if you do actually allow any of the connections through, there is a good chance your logs will quickly fill up with failed connection attempts.

Where do the connections come from?

The short answer is from all over the globe. The map below shows where connection attempts were made from:

Map showing the location of unwanted connections

Note: The map was generated using OpenHeatMap and freegeoip.net. The background map is taken from the OpenStreetMap project (© OpenStreetMap contributors).

Time to hide?

There are a few ways to avoid unwanted connection attempts including:

  • Whitelist IP address ranges and ignore other traffic.
  • Run daemons on non-standard ports.
  • Set up port knocking.

The rest of this post is going to look at port knocking. Before continuing it's worth noting that port knocking is effectively security through obscurity. Therefore port knocking should not be your only security measure. Make sure you take steps to secure services before they get anywhere near the Internet.

Port knocking and knockd

The basic idea behind port knocking is you deny connections by default, but listen for a special "knock" sequence of port-hits. When you see the correct sequence you can then perform an action, normally adding an extra firewall rule to allow connections in.

knockd is a port-knock server that can be used to implement port knocking. The rest of this post is going to go though setting up knockd on a Raspberry pi running Raspbian. More specifically a Raspberry Pi model B plus running Raspbian Jessie Lite (2016-05-27).

Change default password

By default Raspbian has the following login details:

Username: pi
Password: raspberry

Before doing anything else you should login and change the password using the passwd command.

Switch to root

The commands below will assume you are running as root. Either switch up to root or run all of the commands below using sudo. You can get a root shell by running:

sudo -s

Install knockd

knockd is already packaged in the Raspbian repositories. Therefore installing it is just a case of running apt-get:

apt-get install knockd

Set up iptables:

Run the following commands to configure iptables:

iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp -s 192.168.0.0/16 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 192.168.0.0/16 -j ACCEPT
iptables -N KNOCKD
iptables -A INPUT -j KNOCKD -m comment --comment 'Check rules added by knockd'
iptables -A INPUT -j REJECT
iptables -A FORWARD -j REJECT

This will allow traffic on the loopback interface and allow you to SSH to your Raspberry Pi from the LAN. Connections from outside the LAN will be rejected. You can check the rules by running:

iptables -L -v

At this point you might want to try starting a new SSH connection to your Raspberry Pi to make sure you have allowed SSH connections from your LAN. If you can't get in just reboot the Raspberry Pi and start again, none of the rules are persistent yet.

Making iptables persistent

Once your happy iptables is working run the following command to create a config file:

iptables-save > /etc/network/iptables

Then create a script called /etc/network/if-pre-up.d/iptables with the following contents:

#!/bin/sh
#
# Load iptables rules before the network comes online.
#
/sbin/iptables-restore < /etc/network/iptables

Note: Don't forget to add execute permissions to the script.

Configure knockd

The next step is to configure knockd. Add the following config to /etc/knockd.conf:

[options]
        UseSyslog

[openSSH]
        sequence    = 4438,1813,8235
        seq_timeout = 5
        command     = /sbin/iptables -A KNOCKD -s %IP% -p tcp --dport 22 -j ACCEPT
        tcpflags    = syn

UseSyslog will tell knockd to log using syslog. The next section sets what knockd should do once it sees the specified port sequence. In this case iptables is used to add an allow rule to the KNOCKD chain we set up earlier.

The last thing to do is enable and start knockd. To do this run the following commands:

sed -i '/^START_KNOCKD=0$/s/0/1/' /etc/default/knockd
systemctl enable knockd
systemctl start knockd

Finally make sure knockd is running correctly:

systemctl status knockd

Testing knockd

Once knockd is set up an running you will want to test it. From a remote client run the following command:

knock 192.168.1.129 4438 1813 8235

Alternatively if you haven't installed the knockd client you can use any command which will try to establish a TCP connection. For example the following curl commands can be used:

curl --max-time 1 http://192.168.1.129:4438 &
curl --max-time 1 http://192.168.1.129:1813 &
curl --max-time 1 http://192.168.1.129:8235 &

Note: You will want to change the IP address to match the address assigned to the Raspberry Pi.

If everything works correctly you should see messages similar to the following in /var/log/syslog:

Jul 06 19:46:33 raspberrypi knockd: 192.168.1.63: openSSH: Stage 1
Jul 06 19:46:34 raspberrypi knockd: 192.168.1.63: openSSH: Stage 2
Jul 06 19:46:35 raspberrypi knockd: 192.168.1.63: openSSH: Stage 3
Jul 06 19:46:35 raspberrypi knockd: 192.168.1.63: openSSH: OPEN SESAME
Jul 06 19:46:35 raspberrypi knockd: openSSH: running command: /sbin/iptables -A KNOCKD -s 192.168.1.63 -p tcp --dport 22 -j ACCEPT

You should also see a new iptables rule has been created to allow you to connect to the Raspberry Pi in the KNOCKD chain:

Chain KNOCKD (1 references)
 pkts bytes target     prot opt in     out     source          destination
     1   52 ACCEPT     tcp  --  *      *       192.168.1.63    0.0.0.0/0     tcp dpt:22

Tidying old rules

The iptables rules which knockd adds will, by default hang around until the network interface is restarted. To make sure old rules are tidied up overnight you can create /etc/cron.daily/clear_knockd_iptables with the following contents:

#!/bin/sh
/sbin/iptables -F KNOCKD

This will remove all rules from the KNOCKD chain. Note that if you have an active SSH connection, you won't get kicked out. This is because established and related traffic is explicitly allowed in the INPUT chain.