As many of you may know, a TCP connection is not considered established until a series of packets have been exchanged, the well-known TCP-Handshake, which consists of an initial SYN packet, a response SYN/ACK packet, and a final ACK packet. At that moment, the bound application to that port is responsible for sending information.
Portknocking consists of using this type of traffic to perform certain operations on the server, so that by combining connection attempts, we can apply firewall rules or execute any other command.
The first thing we will do is install the portknocking service on the server:
Now we perform the configuration according to our needs. In my case, when I send the magic combination 7000 8000 9000, I will allow access via ssh by inserting an iptables rule for this purpose:
[options]
logfile = /var/log/knockd.log
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 5
command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 5
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
We finish the configuration by indicating on which interface knockd should remain listening:
IFACE="eth0"
OPTS=""
We start the service and add it to the startup:
rc-update add knock default
We insert the ACL that will be responsible for blocking ssh traffic if the portknock has not been sent:
If we have the default rules to block traffic, we must open the ports used by knockd. Otherwise, iptables, which is at the kernel level, will intercept the traffic before knockd, and it will be as if the knock had never been sent:
iptables -I INPUT 1 -p tcp –dport 8000 -j ACCEPT
iptables -I INPUT 1 -p tcp –dport 9000 -j ACCEPT
We can see the logs in /var/log/knockd.log:
On the client side, we will have to install some software capable of generating the expected traffic for knockd, in my case netcat, but any other would be equally valid:
We generate the expected traffic for knockd:
On the server, we can see how the firewall rule has been applied:
[2014-10-30 16:46] E.F.G.H: openSSH: Stage 1
[2014-10-30 16:46] E.F.G.H: openSSH: Stage 1
[2014-10-30 16:46] E.F.G.H: openSSH: Stage 2
[2014-10-30 16:46] E.F.G.H: openSSH: Stage 3
[2014-10-30 16:46] E.F.G.H: openSSH: OPEN SESAME
[2014-10-30 16:46] openSSH: running command: /sbin/iptables -I INPUT 1 -s E.F.G.H -p tcp --dport 22 -j ACCEPT
Also, if we check the iptables rules:
ACCEPT tcp -- E.F.G.H 0.0.0.0/0 tcp dpt:22
When we have finished performing administrative tasks, we can close the gap by:
NOTE: One possible way to increase the security of this system could be to reconfigure the port combination according to the time slot or using a custom algorithm. This way, a valid combination may no longer be valid after X time.
There is no doubt that this technique can be very useful in environments where paranoia prevails over functionality. This way, the service would only be exposed for a limited time to a specific source IP ;)