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.
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.
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.
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
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.