Port Knocking
Protect your Network with Port Knocking
Knock It Off
At this point, I'll mention that we deployed port knocking across three bastion hosts . In other words, we used servers, sitting mainly on the network perimeter (but not always), which were hardened to the extent that they could withstand most types of attacks and still hold the fort. These servers each had access to a particular segment of the network that could reach the rest of the network at large, but in between segments were several addition layers of security, in case one were to suffer a compromise.
Therefore even if one server, and therefore a network segment, was breached, in theory we would know about it before other parts of the network could be affected. We used three servers in this case for redundancy. If any of the servers were to fail for any of a number of reasons (e.g., hardware failure, connectivity, or attacks), we still hoped to have remote access.
Knocking On
TCP Wrappers mainly pays attention to the /etc/hosts.allow
file these days because its counterpart, /etc/hosts.deny
, essentially just says, "no one is allowed in"; that is, it uses a sensible and pragmatic default deny
policy. You can either let IP addresses or DNS names gain access (please see the article that discusses TCP Wrappers [1] in greater detail).
In this case, I explicitly allow access by adding IP addresses in the /etc/hosts.allow
file, prepended by the service I want to allow them to use. Conversely, I simply remove them from that file to ban access.
If the key tcpd files don't exist, then you can create them with the permissions shown in Listing 1. By way of comparison, the file /etc/hosts.deny
might look like Listing 2 by default, depending on the version (just copy these lines in the ALL:ALL
section if the file doesn't exist), and its close relation, /etc/hosts.allow
, looks something like Listing 3 by default (simply copy in the SSHD:
line if your file doesn't exist).
Listing 1
Permissions for Key tcpd Files
-rw-r--r-- 1 root root 351 Nov 3 2012 /etc/hosts.deny -rw-r--r-- 1 root root 348 Jun 17 08:23 /etc/hosts.allow
Listing 2
/etc/hosts.deny
# hosts.deny This file describes the names of the # hosts which are *not* allowed to use the local # INET services, as decided by the '/usr/sbin/ # tcpd' server. # # The portmap line is redundant, but it is left to # remind you that the new secure portmap uses hosts.deny # and hosts.allow. In particular you should know that # NFS uses portmap! ALL:ALL
Listing 3
/etc/hosts.allow
# hosts.allow This file describes the names of the hosts # which are allowed to use the local INET services, # as decided by the '/usr/sbin/tcpd' server. # SSHD: 12.34.56.78, 78.56.43.12
I should mention that I've been caught out in the past when adding TCP Wrappers to a server already running other services (back when inetd
controlled many of the available services on a server) without realizing that ALL:ALL
within /etc/hosts.deny
had locked a service down. If my memory serves me, it was access to a POP3 mail server. If that trips you up, then change ALL:ALL
to something like SSHD:ALL
so it only affects SSH – at least temporarily while you're finding your way.
Someone's at the Door
Now you can consider how to trigger SSH access by opening its port, once port knocking has approved the secret door-knocking sequence. Ideally, it's as simple as adding a line to /etc/hosts.allow
with the approved IP address to allow an IP address through, and then reversing the process by deleting that IP address when the magic closing sequence has been received, in much the same way as the default iptables setup shown in Figure 2.
Another consideration is to avoid triggering additional scripts that need their permissions set correctly on each server on which the script is deployed. Perl, the undisputed heavyweight champion of one-liners, will be useful here, so make sure it is installed on your system.
The default config file (Listing 4) begins with UseSyslog
, which implies that the file /var/log/syslog
(or /var/log/messages
on some Linux flavors) receives logging information. On Debian I'm becoming more accustomed to looking inside /var/log/auth.log
for all things relating to logins, but I don't like to mess with that file because it's too important.
Listing 4
Default /etc/knockd.conf File
01 [options] 02 03 UseSyslog 04 05 [openSSH] 06 sequence = 7000,8000,9000 07 seq_timeout = 10 08 tcpflags = syn 09 command = /usr/sbin/iptables -A INPUT -s \ %IP% -p tcp --dport 22 -j ACCEPT 10 11 [closeSSH] 12 sequence = 9000,8000,7000 13 seq_timeout = 10 14 tcpflags = syn 15 command = /usr/sbin/iptables -D INPUT -s \ %IP% -p tcp --dport 22 -j ACCEPT
In testing, I would put a hash symbol in front of the UseSyslog
entry to disable it and use a knockd-specific logfile, such as:
# UseSyslog logfile = /var/log/knockd.log
The /etc/knockd.conf
file in Figure 3 shows this change. Looking at this file closely, you need to pay most attention to the %IP%
variable, a built-in variable used by knockd that is populated with the connecting IP address, which you can manipulate within your open
and close
access commands.
Clearly it would be madness to leave the default port-knocking sequence in place to secure a production server, almost rendering the use of port knocking useless and practically leaving your front door keys on the doormat.
However, you can configure that later; now is the time to make use of the %IP%
variable in a magical Perl one-liner:
/usr/bin/perl -ni -e 'print unless /SSHD: %IP%/' /etc/hosts.allow
The -i
says run the file in place
(i.e., Perl opens the file, makes a quick temporary file as a backup, and uses that to replace the original file). The -e
simply means to execute
this command. Perl loops through your file with the -n
switch and asks Perl to print what's in the file unless
it matches the string of characters that follow. Because there's no substitution of characters, the line is deleted cleanly rather than adding whitespace. In other words, you don't want to use something like this,
/usr/bin/perl -pi -e 's/SSHD: %IP%/ /' /etc/hosts.allow
which replaces the matched pattern with a single space. Although this will work, it would not work as well as the print
alternative, because it adds extra lines to the /etc/hosts.allow
file.
In the past, I have seen /etc/hosts.allow
break so that no one can log in if unusual characters are added the file. Once I had to dial in to the back of a server to get remote access again using mgetty
. There's a very good reason I don't intend to add and remove commas and extra CIDR (network notations) in the /etc/hosts.allow
file. All I want is a straightforward IP address appended to the bottom of that file once and then cleanly (and I mean really cleanly) removed just once from the end of the file afterward.
With the ever so slightly tweaked line
/usr/bin/perl -ni.bak -e 'print unless /SSHD: %IP%/' /etc/hosts.allow
Perl can create a potentially life-saving backup file. However, enough Perl for now. If you've altered the /etc/knockd.conf
file to use TCP Wrappers, as shown in Figure 3, then you can begin to test your config.
Buy this article as PDF
(incl. VAT)