DevOps Linux

Practical Guide to Port Knocking

March 14, 2023

If you’re running a remote server over the internet, you know that it’s crucial to maintain security to prevent unauthorized access. Most remote servers are protected by a firewall that only allows data to pass through specific, numbered ports. However, having open ports may expose your server to potential risks as attackers can easily spot them and try to gain unlawful access.

That’s where port knocking comes into play - it’s a technique that enables you to keep your ports closed while still allowing authorized users to connect to your server. In this article, we’ll delve deep into the concept of port knocking, how it works, and how to implement it using Knockd, a Linux-based port-knocking utility.

How Does Port Knocking Work?

In general, when you want to connect to a remote server, you send some data over the network to an IP address of the server, specifically to a numbered port at that IP address. The open ports on the server’s network are then exposed to the internet.

With port knocking, you can configure your server to have no open ports at all. Instead, it will only open a port for a brief period when there is a specific sequence of connection requests sent to it. This makes it challenging for an attacker to identify the open ports and gain access to your system.

Using Knockd to Configure Port Knocking

To get started with implementing port knocking, we will need to use Knockd, a lightweight port-knocking utility. Knockd is widely used in the Linux community and is easy to configure.

Firstly, we need to configure Knockd by specifying the sequence of connection requests that it needs to listen for.

Here is an example of a configuration file with two sequences that will open port 8888 if Knockd sees a sequence of requests on port 7000, 8000, and then 9000, all within five seconds. The second rule does the opposite, closing port 8888 if it receives the opposite sequence.

[options]  
    logfile = /var/log/knockd.log  
  
[openSSH]  
    sequence      = 7000,8000,9000  
    seq_timeout   = 5  
    tcpflags      = syn  
    command       = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT  
    tcpport       = 22  
  
[closeSSH]  
    sequence      = 9000,8000,7000  
    seq_timeout   = 5  
    tcpflags      = syn  
    command       = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT  
    tcpport       = 22

As you can see from the file, the rules listen for a specific sequence of connection requests, then use IPtables to add or remove rules to open or close the specified port.

Example Implementation of Knockd

Now that we understand how Knockd works, let’s see how we can implement it in a real-world scenario. We’ll then use Knockd to configure port knocking so that the port is only open when we send a specific sequence of connection requests.

Create a new directory where you want to store your files. Then, create a file called Dockerfile will following content

FROM python:3.8-alpine

RUN apk add --no-cache \
  iptables \
  knock

COPY ./entrypoint.sh ./
RUN chmod +x ./entrypoint.sh

COPY knockd.conf /etc/knockd.conf

COPY ./index.html ./

CMD [ "./entrypoint.sh" ]

The Dockerfile installs Knockd and IPtables, then copies in our configuration files and starts everything up using an entrypoint script.

Next, create a simple Makefile with the following content

build:
	docker build -t port-knock-demo .

run:
	docker run -d \
	  --rm --privileged \
		-p 8000:8000 \
		-p 7000:7000 \
		-p 9000:9000 \
		-p 8888:8888 \
		--name port-knock-demo \
		port-knock-demo

unlock:
	-telnet 127.0.0.1 7000
	-telnet 127.0.0.1 8000
	-telnet 127.0.0.1 9000

lock:
	-telnet 127.0.0.1 9000
	-telnet 127.0.0.1 8000
	-telnet 127.0.0.1 7000

test:
	curl -m 1 127.0.0.1:8888

view-logs:
	docker exec port-knock-demo cat /var/log/knockd.log

Next, create a file called entrypoint.sh with the following content

#!/bin/sh

# NOTE: Docker opens up port 8888 because I am port forwarding
# to it in my docker run command so I have to reclose it here
/sbin/iptables -A INPUT -p tcp --dport 8888 -j DROP & \
  knockd & \
  python -m http.server 8888

The contents of knockd.conf are the same as the example above.

Finally, create a file called index.html with the following content

<!DOCTYPE html>
<html>
  <head>
    <title>Port Knocking Demo</title>
  </head>
  <body>
    <h1>Port Knocking Demo</h1>
    <p>This is a demo of port knocking.</p>
  </body>
</html>

Now you can build and run the Docker container using the following commands.

make build
make run

Once the container is up, we can test our port knocking process by attempting to connect to our Python server on port 8888.

make test

However, since that port is closed currently, it won’t work. We then fire off a sequence of telnet requests on port 7000, 8000, and 9000.

make unlock

Now, we can test the connection again and it should work.

make test

To re-lock the port, we just send those same telnet requests in the reverse order, 9000,8000, 7000.

make lock

Now, the server will no longer listen to the port and our request will timeout.

make test

Conclusion

Port knocking is a simple yet effective way to enhance network security by closing your server’s ports until it receives a specific sequence of connection requests. It can be implemented quickly and easily using Knockd, which is lightweight and easy to configure.

By following the steps above, you should be able to implement port knocking on your own systems with ease. However, it’s good to remember that port knocking should be viewed as one component of broader network security strategy rather than the sole solution.

© All rights reserved — cs.fyi