*

Simple Portknocking in Linux

Introduction - What is Port Knocking
Port knocking is a simple but clever way to hide services that are listening for network connections from would-be attackers. The idea is that you have to "knock" at a predetermined random port in order to then connect to the service at a different port, such as SSH or VNC. Some more information on port knocking can be found on Wikipedia: Wikipedia Port Knocking Artical

Please note that my method is not the most flexible since it uses pure IPTABLE rules for the setup, but because of this you will get to know iptables, as well as the theory behind why and how port knocking works. Plus there are no packages to install and once you understand how IPTABLES works, it becomes so simple even a caveman can do it.

Why would you even bother?
Recently, I noticed my log files that log SSH activity filling up with entries such as: "Failed password for illegal user xxxxxxx". This was basically a string of brute force SSH attacks. Brute force attacks try a list of commonly used passwords and usernames to see if the attacker can gain access to the system by guessing the login information. To do this they typically use a program that can try thousands of usernames and passwords per minute, which can be quite annoying if it's your system being attacked.

Although my passwords are randomized and so the attacks were not a real threat, I decided to try and secure my server a little to stop these brute force SSH attacks (mainly coming from china). What I needed was a simple way to stop the attacks altogether, but still allow ssh access from anywhere for myself since I log in to check things to tweak my server a lot. In order to do this though, it became obvious that I needed a way to make it look like my computer did not accept SSH connections at all, but still accept connections from myself. This required a simple way to "authenticate" myself to the server if you will. SSH already authenticates itself though, so a software solution that I would log into and authenticate to and then open up SSH for me was not really what I was looking for.

What I decided on was the Port Knocking solution. With this setup, I could simply "knock" on a port that only I knew was doing anything, and thereby open up the SSH port to listen to my IP address only. This also has a bonus feature of being able to "knock" on a different port to then make SSH stop allowing my IP address to connect anymore.

Please note that this does have serious limitations though. Most notably is that if you are connecting through a NAT (Network Address Translation) anyone from behind the NAT will then be able to try and log in as well, but unless China is behind your NAT I think the normal SSH authentication will still be fine protection here :)

Introducing IPTABLES
IPTABLES is the firewall in the linux operating system. It will be installed by default on 99% of all Linux Installations. There are several modules within IPTABLEs which can be used for various things. For this setup, we will use the module named "recent" which is designed to detect malicious access attempts and then block or track the IP address of the potential attacker.

Here are the commands used to build my IPTABLES rules to enable port knocking. These are run from the command prompt, but you can just as easily modify them to run as a script on boot or add them inside an iptables configuration file.

iptables -N PORTKNOCK
iptables -A INPUT -j PORTKNOCK
iptables -A FORWARD -j PORTKNOCK

iptables -A PORTKNOCK -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 22 -m recent --rcheck --name SSH -j ACCEPT
iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 22 -j DROP

iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 1600 -m recent --name SSH --set -j DROP
iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 1601 -m recent --name SSH --remove -j DROP

And now I will go through a brief explaination of each rule above. For further information, please view the full IPTABLES manual page located at the web address below (I noticed many others online are far from complete): IPtables Manual

iptables -N PORTKNOCK
This command creates a new Chain named "PORTKNOCK". A Chain is like a set of rules within the firewall. You can tell the firewall to sent certain packets to one chain, and other packets to another chain, with each chain having different rules so that you don't have to duplicate rules accross multiple chains, etc. By default, there are 3 chains already built-in. Those are "INPUT", "OUTPUT", and "FORWARD".

iptables -A INPUT -j PORTKNOCK
This tells all packets that are in the "INPUT" Chain to set them to apply to the "PORTKNOCK" chain instead. This will let us create a set of rules in our PORTKNOCK chain so that it is seperated from the rest of the IPCHAINS structure. The "INPUT" Chain is where all packets start at that have their source IP address as your server.

iptables -A FORWARD -j PORTKNOCK
This tells all packets that are in the "FORWARD" Chain to set them to apply to the "PORTKNOCK" chain instead. This will let us create a set of rules in our PORTKNOCK chain so that it is seperated from the rest of the IPCHAINS structure, and will also let us create a set of rules once instead of having to create them for both the "INPUT" and the "FORWARD" chains. The "FORWARD" Chain is where all packets start at that have their source IP address as a computer other than your server (if your computer does routing for instance). You can probably skip this command if you have routing going on, otherwise anyone connecting through the router on port 22 would have to "knock" on the router to get through!

iptables -A PORTKNOCK -m state --state ESTABLISHED,RELATED -j ACCEPT
This allows all existing connections and new connections that spawn from existing connections to flow through the firewall freely. Without this, your SSH connection would be terminated if the SSH port was closed on you. With this command, even if the SSH port is told not to allow new connections from your IP address, you're existing connections will still work just fine.

iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 22 -m recent --rcheck --name SSH -j ACCEPT
This is the most important command. It says that for any NEW connection (not already existing like the previous command), and if the destinaton port is TCP 22 (the SSH port) then check to see if the source IP address is in the list "SSH" and if they are then allow the connection. IP addresses can be added to the "SSH" IP address list or removed from the list with IPTABLES rules as well, which is why this whole process works (see commands below).

iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 22 -j DROP
This simply says to drop all traffic that is part of a NEW connection (not existing connections) and is on TCP port 22. This is applied after the above command, so basically if the source IP address is not in the "SSH" list as defined in the previous command, then drop the packets (don't allow the connection).

iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 1600 -m recent --name SSH --set -j DROP
This says that if a NEW packet comes in on TCP port 1600, add the source IP address to the "SSH" list so that they can then have access to the SSH port. Remember that a previous command checked the "SSH" list for the IP address and allowed the packets in that case. This adds the IP address to that list.

iptables -A PORTKNOCK -m state --state NEW -m tcp -p tcp --dport 1601 -m recent --name SSH --remove -j DROP
You'll notice this is almost the same as the previous command, except "--set" became "--remove" and the port changed from 1600 to 1601. As you probably guessed, this will remove the source IP address from the "SSH" list if you open a TCP connection to port 1601.

Checking it out
Okay, so it's set up right? First lets test it out! Since you havn't knocked on port 1600 yet, try opening a SSH connection to port 22. You should get a timeout error and have the connection fail. Also, you can view the "SSH" list of allowed IP addresses by viewing the "SSH" list located in /proc/net/ipt_recent/*

shell> cat /proc/net/ipt_recent/SSH

Now lets knock on port 1600 by sending a TCP packet to that port in the form of a telnet connection. This should unlock the SSH port for us by adding us to the "SSH" IP address list.

shell> telnet your.server.ip.address 1600

Next, check out the SSH list in /proc/net/ipt_recent/ and you should see your IP address there with other information about the connection (note, I "knocked" from a computer with IP address 192.168.0.1).

shell> cat /proc/net/ipt_recent/SSH

src=192.168.0.1 ttl: 46 last_seen: 281809649 oldest_pkt: 2 last_pkts: 281809369, 281809649

Then try to open the SSH connection, it should succeed! Also note that although the SSH port is open for you, it's still closed to all other IP addresses that have not "knocked" on port 1600 yet. You can even "knock" on port 1601 to hide the SSH port from your IP address again:

shell> telnet your.server.ip.address 1601

Check out /proc/net/ipt_recent/SSH and notice that your IP address is gone now. Also notice the SSH connection you opened still works, but you cannot open a new connection anymore.

Conclusions, and Taking it Further
You can also have one port knock open another port you have to knock at, which openes another port you have to knock at, which finally opens the SSH port. You can also have it work with any other port, not just SSH of course.

As a final thought though, do not rely just on this alone for security. In fact, a simply nmap scan of your system would show the following:

shell> nmap localhost

Starting Nmap 4.03 ( http://www.insecure.org/nmap/ ) at 2006-12-31 03:28 EST
Interesting ports on localhost.localdomain (127.0.0.1):
(The 1668 ports scanned but not shown below are in state: closed)
PORT STATE SERVICE
1600/tcp filtered issd

Nmap finished: 1 IP address (1 host up) scanned in 1.812 seconds

Notice the 1600 port listed there? Not only can someone see this port open, but a scan could easily open the SSH port for that IP address! For this reason, I would recommend NOT using the above ports, and choosing ports that are not typically scanned by default scanners. Also, I would recommend having several "close" knocking ports, so that the scanner will probably hit one of those and re-close the port before scanning the SSH port even if it does hit the port that opens the SSH port. For scanners that scan the ports in sequence you will probably want to have the ports just below and above the "open" knock port to be "close" knock ports.

In reality though, this protection is best against people scanning only for the specific service you are trying to protect. If they are singling your server out, you've got bigger problems than brute force attacks and other security measures should be in place.

Another option is to use something like portsentry. Portsentry can listen on certain ports or can be configured to listen to all ports. More importantly, portsentry can be configured to do something when someone hits a port it is listening for. By default this is to block the source address with a route rule, but I have set mine to block using an IPTABLES rule. For my configuration, I have portsentry listening on ALL ports (not recommended for a real server though since this adds a decent load when you have multiple connections to the server). Any connections to any port except 80, 443, and 22 are logged and the source IP address is blocked via IPTABLES and the deny.hosts file. What that means is that if an attacker tries to connect to ANY port except 80, 443, or 22, portsentry will add rules to block ALL traffic from that host from then on.

What makes that setup so nice is that people cannot figure out the "knock" port by trial and error. One slip-up and they are banned! Maybe you don't want to be as harsh though. Well, portsentry claims to be able to only block after a certain number of attempted connections, but I have not been successful in configuring that so I can't confirm, but it should be possible.









reece
home
history
baby
photos
calendar
addresses
wall
projects
4006
word
flickr
monitor
chat
lolmail
work
cocard
ibm
resume
dev
sudoku
security
portsentry
portknock
badbot
setuid
web
greasemonkey
visitors
links
downloads
misc
art
vote
influence
waffles