Skip to content

How to keep your ports private — Port knocking with knockd

In this article we will be looking at knockd, a port knocking tool, and how we can utilize it on our bastion/firewall server. This guide can be used for a production Linux server or our home Linux-based router. The concepts are the same as well as the purpose.

There are many cogs in the proverbial wheel within the computer security tool kit that all work together to make a more complete security solution for us. Computer security is important for servers and for our home networks.

One of these cogs is to hide things or otherwise make them inaccessible until we want access to it. How can we hide things? I’m glad you asked! Enter knockd, an open source port-knock server that blocks access to a port unless it receives a special “knock” on the door so to speak. This knock is actually a series of connections to specific ports in a specific order. One of the great features is that we can have it only open access to the IP address that we are connecting from!

What is this magic you ask? Well, knockd accomplishes all of this by listening on all the traffic on a machine’s network interface, be it ethernet, ppp, etc., for a sequence of port hits. We can configure knockd to listen for TCP, UDP, or both types of packets. In addition, we can configure the port numbers used in the port knocking sequence, and we can even define how much time is allowed to pass before it stops considering a sequence to be valid.

So you might be thinking “why would anyone want to hide a port?” or “isn’t the point of a server to provide services?” and even “we already have authentication, why do we need to hide the service at all?” These are all valid questions and to answer them it requires us to step back for a moment and remember that sometimes there are vulnerabilities in protocols, services, as well as compromised user accounts. There is also the annoyance of having tons of bots, script kiddies, and the like trying to look for vulnerabilities in our system and spamming our logs with their connection attempts. Anyway, enough of that… let’s move on to installing and configuration of knockd.

We need to install knockd on our server before we can configure and make use of it. In this article I will use Ubuntu in the examples as it is a commonly used operating system, but it should be straightforward to convert the steps to work with a different Linux distribution.

Install the repository package containing knockd:
sudo apt install knockd


The configuration file location is /etc/knockd.conf and using your favorite editor open that file:
sudo vim /etc/knockd.conf


The defaults will look something similar to this:
[options]
logfile = /var/log/knockd.log
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 10
tcpflags = syn
command = /usr/sbin/iptables -A INPUT -s %IP% -j ACCEPT
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 10
tcpflags = syn
command = /usr/sbin/iptables -D INPUT -s %IP% -j ACCEPT


The first section titled “options” controls where the logfile for knockd is stored. This default is fine unless you have a specific requirement.

The remaining two sections are an example of setting up knockd to open and close access to SSH (Secure SHell) as two separate sequences. Let’s go ahead and comment out everything starting with the “openSSH” line by adding pound/hash “#” to the beginning of each line. Alternatively you can delete these lines, but I like to keep them as a quick reference when making edits. Your config should now look like this:
[options]
logfile = /var/log/knockd.log
#[openSSH]
# sequence = 7000,8000,9000
# seq_timeout = 10
# tcpflags = syn
# command = /usr/sbin/iptables -A INPUT -s %IP% -j ACCEPT
#[closeSSH]
# sequence = 9000,8000,7000
# seq_timeout = 10
# tcpflags = syn
# command = /usr/sbin/iptables -D INPUT -s %IP% -j ACCEPT


Now we will want to decide the port knocking sequence we want to utilize. Traditionally the ports used in TCP/IP need to be equal to or greater than 1000 (ports below 1000 are reserved) and less than 65535. For this article’s example, I am going to use 1941, 1942, and 1945. These numbers represent dates when events took place in history. Specifically, 1941 is the year that the code the German Enigma machine used was broken. While 1942 is the year that the Japanese Navy’s cipher (designated JN-25) was broken and finally, 1945 is the year that World War II ended. Yes, I am a history nerd… among other things!

Now we need to decide what port or ports we would like to hide? In my environment I typically hide access to SSH on port 22 and RDP (Remote Desktop Protocol) on port 3389. This scenario applies to both a business as well as a home setup. I need to allow access via SSH for developers and RDP for GUI access for either Windows Terminal Server (multiuser business) or a single user desktop at home. Also, as our society as a whole is generally a forgetful and lazy bunch, it would seem like a good idea to make the ports that are opened to automatically be blocked again after a certain amount of time has elapsed. Note, this will not affect active connections, i.e. you won’t break a running SSH session and only applies to new connection attempts.

Our updated config will now look like this:
[options]
logfile = /var/log/knockd.log
[openSSHandRDP]
sequence = 1941,1942,1945
seq_timeout = 5
start_command = /usr/sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT; /usr/sbin/iptables -I INPUT -s %IP% -p tcp --dport 3389 -j ACCEPT
cmd_timeout = 500
tcpflags = syn
stop_command = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT; /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 3389 -j ACCEPT


Let’s break down what each line is doing. Under “options” we have our logfile location. Under “openSSHandRDP” we have our knock sequence, followed by the sequence timeout. The sequence timeout is how long in seconds knockd will consider a knock on the ports to be part of one attempt. Next we have our start_command and this is where the opening of the ports happens in the kernel’s firewall. The cmd_timeout is how long in seconds the start_command will be run before we run the stop_command. Which brings us to the stop_command where we delete the two iptables rules from start_command and thereby close the ports back up.

So bringing it all together what we now have is the ability to knock on ports 1941, 1942, and 1945 in that specific order and knockd will do its thing.

Now we need to set a default rule to be in place that denies access to SSH and RDP by default. The place to put this command depends on your firewall. On my bastion I write the firewall rules myself and interact via the iptables command line tool. So on my machine I edit /etc/iptables/rules.v4 which contains two lines that looks like this:
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j DROP
-A INPUT -i eth0 -p tcp -m tcp --dport 3389 -j DROP


Where “eth0" is the primary network connection. Without this knockd is rather pointless as the ports are always open. If you want to make these changes take affect immediately without a reboot you can simply run “iptables …” where the “…” is each of the lines above:
sudo iptables -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j DROP
sudo iptables -A INPUT -i eth0 -p tcp -m tcp --dport 3389 -j DROP


At this point you should be wondering “this is all fine and dandy, but how do I actually initialization a knock?” This part is easy! We can use a variety of tools: nmap, telnet, knock, and others — basically any utility/command that is capable of opening either a TCP or UDP connection to a given port. The easiest is probably going to be knock since that is its sole purpose.

Let’s get knock installed:
sudo apt install knock


Now we can run it from the command line and send our secret sequence to our remote bastion machine:
knock 1941 1942 1945


If you have a slower connection (i.e. tethered cell phone) you can also add the “-d” command line flag to knock. This will add a delay in milliseconds between the knocks on each port. I often use 100 as the option to the “-d” flag. This, in all its glory, would look like:
knock -d 100 1941 1942 1945


We now find ourselves with hidden SSH and RDP services on port 22 and 3389 that will help reduce our attack vector. These ports are only open when we instruct knockd to open them and only open to the IP address we are connecting from. There are many more things you can do with knockd and while in my example I put both 22 and 3389 in the same section (openSSHandRDP), we could have put them into separate sections each with a different port sequence.

I hope this article finds you well and has provided some useful information on what knockd is and how to get started with it. I enjoy receiving feedback; be it suggestions, corrections, or questions. Feel free to drop some love, be safe, and hack away!

New blogs!

It has been a while since I wrote a blog post. I have been super busy and do have things to write about.

First about this blog: I have migrated from WordPress hosted blog to a self-hosted blog running on Serendipity.

My other (paywalled) blog is on Medium.

You will see some cross-posting between the two platforms.

ping (ICMP) blocked? No problem, enter hping!

When traveling while working we often find ourselves using networks that have certain restrictions. For example, many free WiFi hotspots will block things like ICMP echo requests (ping) which makes things a bit difficult when you are trying to figure out problems with connectivity, where packet loss is occurring, and what is reachable and what is not. Enter hping, a TCP/IP utility that can do far more than the name suggests.

First an example using standard ping (ICMP):

ping -c1 http://www.google.com
PING http://www.google.com (172.217.14.228) 56(84) bytes of data.

— http://www.google.com ping statistics —
1 packets transmitted, 0 received, 100% packet loss, time 0ms


As we can see in the above output, we never got a reply. Is the network down in between? Is there a firewall that’s blocking our requests? We don’t know.. So let’s take a look at using hping to send a TCP packet, instead of ICMP, to port 80 on a well known website:

sudo hping -c 1 -S -p 80 http://www.google.com
Password:
HPING http://www.google.com (wlp2s0 172.217.14.228): S set, 40 headers + 0 data bytes
len=44 ip=172.217.14.228 ttl=127 id=13972 sport=80 flags=SA seq=0 win=11680 rtt=51.0 ms

— http://www.google.com hping statistic —
1 packets tramitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 51.0/51.0/51.0 ms


Ah-ha! We got a reply! So TCP connections to port 80 at least are not blocked. We can work with that (hint this is where having your own custom, configurable OpenVPN server comes in handy: configure it to answer on port 80 and you are back to work).

Breakdown of the options used: as with the standard ping tool, the “-c” flag tells it we only want to send N count requests. In this example we are only sending one (1) request. Without this command line flag, hping (and ping) will ping non-stop until you stop it with ctrl-c. The next flag is “-S” which sets the SYN flag as by default it will not be set by hping. The “-p” flag tells hping we want to send our request to a specific port, in the above example we use port 80 which is the standard non-SSL webserver port. The last option is the hostname or IP address we want to interact with (www.google.com in our example.)

I am going to try something new and use a terminal utility named asciinema to record the examples above. Please let me know what you think: Watch a video of the above example made with asciinema!