Another port knocking solution

From juliano.info

Jump to: navigation, search

Port knocking is a mechanism that allows sensitive services provided by a server to be kept hidden and protected by a firewall until a specific sequence of requests are made to some other selected ports. This sequence of requests is called knocking. Which ports and the order that they must be knocked are a secret shared only to those authorized to access that service externally. After a successful knocking, the firewall opens the sensitive service port to the knocking host for a short time, so that a connection can be established.

Although not infallible (it was never intended to be), port knocking is a good mechanism to conceal sensitive services running on a server, like, for example, secure shell (SSH). Many people conceal their SSH service by changing the port it listens for new connections to something other than the default (TCP/22). This provides very little additional security and it is not enough when used alone.

There are daemon/tool-based solutions for port knocking, like knockd and The Doorman, but they have some inherent problems:

  • They are a single point of failure of the system: if the daemon dies or fails to load for whatever reason, you are locked out.
  • Not all networks allow egress TCP or UDP packets to arbitrary ports: it is a good security practice to only allow outgoing packets to known service ports, which the SSH port is usually included, but the other ports used for port knocking aren't.
  • Knocking a sequence of ports in a short time is not very easy: you either need a tool for that (usually provided with the port knocking solution), or you have to make it work with the available tools (usually netcat or telnet) and some scripting.

There is this iptables-only solution, which fixes the first problem. It uses only iptables and so is controlled directly from the firewall; you have no extra daemon running, which is a good thing. I decided to improve on that solution.

When you are behind a firewall that restricts the ports that you can communicate, it becomes difficult to rely on TCP or UDP port knocking. Restricting the set of ports used would reduce the number of possible knocking sequences, severely weakening the solution. Also, it is difficult to predict which ports you'd find open in every network you may happen to use.

So, I decided to look for something that should not be blocked: ICMP ping requests. ICMP is one of the core protocols of the Internet, and have fundamental importance for the operation of the network, from error signaling to link testing. No sane network administrator would block outgoing ICMP packets, since it has the potential to break a lot of things.

Ping requests can be easily created using the ping tool present on all *nix systems. Also, the ping tool present in all Linux, BSD and Mac OS X systems allows the user to supply a custom padding for the packet, up to 16 bytes. This pattern can be used to contain a secret, the "knocking sequence".

If 6 bits per byte are used for the knocking sequence (in order to avoid having binary data in iptables rules), it would result in 96 bits of entropy. Comparing it with the TCP port knocking solution, this solution provides approximately the same entropy of a 6-port TCP port knocking sequence.

So, after defining the 16-byte knocking sequence, these iptables rules should be applied:

iptables -N knock
iptables -N knock-ok

iptables -A knock -p icmp --icmp-type echo-request \
         -m string --string "[16-byte-secret]" --algo bm --from 60 --to 61 \
         -m recent --set --name knocked \
         -j LOG --log-prefix "KNOCK-OK: " --log-level 5
iptables -A knock -m recent --name knocked --update --seconds 60 -j knock-ok

These commands create two new chains:

  • knock: Contains the knocking test. This rule should be jumped from wherever you want to test for the knocking sequence and allow traffic from clients which correctly pass the test. Usually, this is the INPUT chain.
  • knock-ok: Should contain the rules that are to be applied to hosts that pass the knocking test. Passing hosts will be applied the rules in this chain for 60 seconds after knocking. Entering this rule resets the timer to 60 seconds; this is to allow connection-less traffic in the absence of connection tracking. Usually, this chain should contain the rule that will accept traffic to the concealed services.

In order to knock the server, these commands should be used in the connecting host:

knockseq=$(echo -n "[16-byte-secret]" | hexdump -e '16/1 "%02x" "\n"')
ping -c -p"$knockseq" hostname

The iptables rule is very strict with regards to the offset in the packet where the secret appears. It may be necessary to loose the restriction a little if you have a different protocol stack (IPv6 in particular). In this case, try --from 60 --to 76 or even --from 0 --to 96 for debugging. This has the side effect of cycled (rotated) secrets being accepted (for example, from "ABCDEFGHIJKLMNOP" to "EFGHIJKLMNOPABCD"), loosing some bits of entropy.

More information

Views