Weekend Project: Secure Your System with Port Knocking

782

Port knocking is an authentication system that allows a server to keep ports closed by default, and open them up only when clients send a pre-determined sequence of connection requests aimed at particular TCP or UDP ports. as a result, you can, for example, keep SSH both invisible and inaccessible to passersby, but still allow clients armed with the secret knock to connect. setting up port knocking on your Linux system is easy, but make sure you set aside time to familiarize yourself with the security and practical risks.

Server-side setup

The most popular port knocking package on Linux system is knockd. It works in concert with Linux’s iptables firewall, running as a daemon that listens on a network interface for a pre-configured sequence of connection requests. When a matching sequence is detected, knockd executes an associated command from its configuration file — canonically, an iptables command that either opens or closes a particular port on which a service is already running.

In the most common example configuration, OpenSSH (or another equally-securable service) is running on a standard port (such as TCP port 22), but iptables is configured to block access to it by dropping packets. Knockd listens on eth0 for TCP SYN packets sent to a particular sequence of ports (such as port 9000, port 6501, port 1234, then port 4321). When a matching knock is detected, knockd executes an iptables command that tells the firewall to start accepting packets on TCP port 22, and the client can begin an SSH session as normal.

You can download knockd from the project’s Web site; source code as well as Debian, RPM, and Slackware packages are provided. Knockd is commonly distributed by the major Linux distributions, though, so check your package manager first. Once installed, you configure knockd’s behavior in the file /etc/knockd.conf.

At the top of knockd.conf is a global [options] stanza. Here you can put system-wide directives, the most common of which would be to specify the network interface on multi-interfaces systems. For example, add a line containing Interface = eth1 to bind knockd to eth1 instead of the default eth0.

Below this, each knockd “event” receives its own stanza, starting with a bracket-enclosed name. The default file contains two:

[openSSH]
	sequence    = 7000,8000,9000
	seq_timeout = 5
	command     = /sbin/iptables -A INPUT -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

In each case, the sequence directive lists the order of the TCP ports that make up the secret knock. Numbers by themselves are taken to mean TCP ports, however you can specify TCP or UDP ports by appending :tcp or :udp. A combined knock might specify both, e.g. sequence = 3333:tcp,9999:udp,1010:udp,8675:tcp.

The seq_timeout specifies a maximum time for the knock to take — start to finish — in order for it to be accepted, and the command directive lists the command that is triggered by the knock. In the example, you see that opening and closing port 22 require two different knocks. An alternative configuration allows you to run a start_command when the knock is heard, then wait a specified amount of time, then run a stop_command. This will open up port 22 for exactly one minute, then close it again:

	start_command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --syn --dport 22 -j ACCEPT
        cmd_timeout   = 60
        stop_command  = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --syn --dport 22 -j ACCEPT

The tcpflags directive tells knockd only to listen for packets that match the specified TCP flags (in the example, just SYN). You can add multiple flags, such as syn,urg,fin or even negate flags with the exclamation point character, such as !ack. The advantage to using TCP flags as part of the knock is that under normal circumstances, knockd ignores malformed packets (which is probably what you want).

You can start knockd manually by running /etc/init.d/knockd start, or configure it to run at init time by editing /etc/default/knockd and setting START_KNOCKD=1.

Obviously, under absolutely no circumstances should you deploy knockd using the default knock of 7000,8000,9000. Similarly, because all port knocking provides is a layer of security when the underlying service in inactive, you must make sure that your SSH server is secure in its own right — only use SSH version 2, use DSA public key authentication, and so on.

Client setup

You can test your basic port knocking setup from another machine with a simple telnet client. With the server running, run telnet 192.168.1.100 3333 to send a connection request to 192.168.1.100 on TCP port 3333, followed by one on each of the other ports in your secret knock.

For regular usage, however, you will want a port knocking client. Knockd comes with a command-line client named knock that uses the same syntax as knockd.conf, so knock 192.168.1.100 3333:tcp 9999:udp 1010:udp 8675:tcp will issue the knock described a few paragraphs above. With the knock entered, initiate an SSH connection as you would normally.

If your secret knock depends on TCP flags, though, you will need to look at a more advanced tool, such as SendIP or packit, which can send arbitrarily-constructed packets.

If you use port knocking on a routine basis, you may be interested in finding a client for your mobile devices in addition to full-fledged Linux systems. There are at least two knockd-compatible clients written for the iPhone platform: Port Knock Lite and KnockOnD, and one for Android: knock-android. There does not seem to be an active client for Maemo or MeeGo at the present, although these platforms similarity to desktop and server Linux should mean that the standard knock will compile without too much additional trouble. There is also a PHP client that is knockd-compatible.

Practical implementation concerns

One of the reasons you do not see many special-purpose port knocking clients that store and recall the secret knocks for you is that that would be akin to storing the login password — the secret knock needs to be something that you memorize and know by heart in order to be effective.

Of course, one of port knocking’s major criticisms is that an attacker sniffing the network connection could discover your secret knock and replay it later. Naturally, the more frequent you use the knock, the bigger the risk. One solution to this is to send dummy knocks along with the real knock, but really it just reveals the importance of securing SSH or the other service in addition to concealing it. If your secret knock is compromised, changing it is at least a simple affair.

A far more sophisticated protection against this attack is to combine port knocking with one-time-passwords, creating one-time-knocks. Knockd supports this through the one_time_sequences directive in knockd.conf. Rather than explicitly describing a knock, one_time_sequences is used to specify a file location, in which are stored a list of knock sequences, one per line. Each time a client connects using a knock, knockd comments-out that knock from the file and listens for the next one.

You also need to be aware that port knocking has its disadvantages. First, if knockd dies, you are locked out of remote access to the hidden service (unless, of course, you manage to crack the firewall subsequently). Just as importantly, because IP packets may get routed in unpredictable ways, unless you are close to the server machine on the network there is always a chance that your knock packets will arrive out of sequence; for this reason it is a good idea to not send them too rapidly, and to set a reasonable value for seq_timeout.

In full appreciation for port knocking, it is also important to understand that by no means is knockd limited to changing firewall rules as an event response. You can use a secret knock to remotely trigger any sort of event, from an rsync backup all the way to a script that erases your home directory and sends a resignation email to your boss, all without opening up a single port to the outside world.

Extra credit: more sophisticated servers

There are other port knocking (and conceptually related) servers available besides knockd. A few you might want to check out include sig2knock, which builds on the idea of non-fixed knock sequences and adds port randomization, portkey, which tightens down the port knocking idea to work exclusively with Linux iptables, and reverse remote shell (rrs), which is an SSL-secured shell that builds in port knocking as its remote access method. If you don’t want users running arbitrary commands on a server you adminster, you may find portkey or rrs a better fit than knockd. On the other hand, if security is your main concern, sig2knock offers some compelling enhancements.

Several projects attempt to overcome the replay attack problem by delving more into standard cryptographic key-based authentication. Examples include cryptknock, COK, and tariq. The disadvantage of these systems is that you cannot rely solely on your ability to memorize the secret knock to provide you with access. However, the more sophisticated systems do introduce other beneficial security factors, such as blacklists and whitelists to restrict which IP addresses can connect. More projects related to port knocking are listed on the portknocking.org site, along with research papers and other resources for further study.

When serious security professionals will tell you that port knocking only adds one relatively thin layer of security to a system, they are correct. You still need to have strong access controls and multi-factor authentication in order to secure your machine for remote access. However, port knocking makes your service invisible to onlookers, which is a valuable asset in many computing environments, and if you take care to change your knock, it can prevent brute-force “dictionary” attacks against your SSH users. After all, you can never be too secure, can you?