Gift-Wrapped Security
Many years ago I remember somebody mentioning that rather than running a firewall, they were just using TCP Wrappers. This piqued my interest because all my customers talked about when it came to Internet security was how much their proprietary firewall had cost them or which bundled features with their firewall guaranteed greater security for their servers.
Admittedly, it goes against the grain – and more than just a little – to totally dismiss firewalls, but you might be surprised to hear that I’ve successfully run several sets of production servers for many years with the absence of a firewall entirely. If you’re wondering what I mean by “successfully,” I mean without the servers being compromised.
My brief addendum to the last two sentences has to be that running Netfilter – or, to most peoples minds, the tool that controls Netfilter, iptables – on a Linux server brings a great number of benefits, such as automatically dropping illegitimately formed traffic that might pose a threat to your applications or catching traffic to a port you forgot to close. A word to the wise, therefore, is that if you fail to implement correctly the approach I run through in this article, iptables is the perfect hero to come to your rescue and make that tiny mistake less disastrous to your servers’ security.
Firewalls Are Overrated
With a little planning and some consideration it is possible to connect Linux boxes to the Internet safely without anything but some Access Control Lists (ACLs) combined with an eye for minimalism. I’m referring to keeping the number of packages (and more specifically network services) to a minimum. By only having, say, three ports open on your server, such as HTTP, SMTP and SSH, you’re significantly limiting the number of attack vectors on your system.
Aside from network ports posing a threat there’s a loosely followed rule of thumb that for every thousand lines of code you add to a server you potentially add another security hole. This is important because you can lock all the network-facing ports down but make your applications prone by adding screeds of unneeded code to your server. Amongst other benefits keeping packages at a minimum means not only that your backups are leaner (and therefore easier to store because they’ll use less storage space and in addition because they’re smaller they should also be quicker to restore in an emergency) but also that the code on your server is less vulnerable to attack. Another side effect is that fewer packages might mean less services that can accidentally open up a network port (which would otherwise add to your risk; such a scenario might only be mitigated against by running only a comprehensive firewalling policy like a efficacious IPtables default-deny ).
Gift Wrap Your Security
I mentioned ACLs, and in this case, I’m talking about limiting access to services installed on your server, such as SSH, to specific IP addresses or restricting access with other rulesets. The open ports example I used was that of HTTP, SMTP, and SSH. It’s highly unlikely that you would want to restrict all HTTP traffic to your server by IP address (e.g., unless the web server was on an Intranet), but you might only want email from specific mail servers locked down by IP address, and to my mind, almost every Internet-facing SSH server should be locked down by IP address. You can achieve this easily using a fantastic piece of software called TCP Wrappers. TCP Wrappers uses tcpd to log, check, restrict, and avoid spoofed network connections efficiently and in real time. It also handles hostname lookups with ease, whether they’re entered in a local database or a public DNS database.
TCP Wrappers have been around since the early days of the Internet and were written by a programmer named Wietse Venema, a Dutchman who is most famous for writing the truly excellent mail server software, Postfix. The story goes that the talented Venema needed to keep track of attacks on workstations at a University and wrote a piece of software capable of limiting port access by rules.
Ignoring the use of a somewhat ancient network service, namely identd , I’ll focus on the use of IP addresses and names for configuring who is allowed to connect to ports.
How Do You Use It?
On many of the popular Linux distributions, two files live inside the /etc directory: hosts.allow and hosts.deny . For the sake of simplicity, I’ll start by denying all access to a server over the SSH service and then explicitly allow certain users access. It’s similar to the deny by default approach I touched on earlier.
To achieve this, the /etc/hosts.deny file would look like this:
sshd: ALL
To allow some IP addresses to connect /etc/hosts.allow , the file would simply look like this:
sshd: 10.10.10.10, 1.2.3.4, 21.21.21.21
TCP Wrappers works nicely, even if you change the standard SSH port (it’s usually TCP port 22) to port 2222, for example, to stop port scans filling up your logs. Without TCP Wrappers enabled, scans might run dictionary attacks on your server where password combinations are guessed by one of many automated attack methods.
As well as being able to take individual IP addresses, hosts.allow can happily handle the CIDR notation of classless IP address ranges, such as:
sshd: 10.10.10.0/24, 1.2.3.4/32, 21.21.21.0/19
IP addresses hosts.allow can be set to accept connections from hostnames, too.
One example might be
ALL: *.domain.tld
or you might have an administrative group of machines that live under a set of hostnames:
ALL: *.admin.domain.tld
For clarity, even without the wildcard asterisk at the start of those examples, you can still allow any machine under that hostname umbrella, even if the line just starts with a period. Turning that example on its head entirely, ending with a period
ALL: 12.34.
would allow all machines with the first two octets in their IP address starting 12.34 , so, for example, 12.34.56.78 will be allowed to connect to ALL services and not just SSH. As well as these flexible options, you can also declare old school subnets directly:
sshd: 1.2.3.4/255.255.255.0
As well as the ALL parameter, you can use LOCAL , which allows any machine without a dot in its hostname. You could edit your local hostname database (somewhat confusingly called /etc/hosts ) to declare that an oft-used computer called friend had the IP address 10.10.10.10 . With the LOCAL switch in your allow file then, access to that service could be granted to all local machines:
sshd: LOCAL
By accepting connections from any hostname without a dot, this might also cover any machine under the same domain name as the server you’re working on.
The KNOWN operator also exists, but I think it should be avoided, in case hostnames aren’t served correctly when name servers are unavailable.
Stop
As you might have gathered, as your configuration files grow there’s a reasonable chance for making a typographical error. In many cases, this is harmless, but I once had a near miss over a dial-up connection into the back of a Linux router. Unfortunately the console I was using wasn’t set up properly, and in the process of speed typing, I hit an extra carriage return by mistake on the sshd line in hosts.allow . This meant that all SSH access was broken until I spotted it.
To say that the order of the rules with TCP Wrappers is sensitive is a monumental understatement, and if you decide to mix allow and deny rules in hosts.allow (which I strongly recommend against), then take a moment to check that your allow rules are declared well in advance of your deny rules; otherwise, you might lose remote access. Luckily, the modem I dialed into over to the Linux router reset as expected within a minute or so, and I could dial back in to fix the broken hosts.allow file and circumvent networking altogether before using SSH again. Not that many back door dial-up connections exist any more; however, I was lucky in some respects.
Mixing Up the Configuration Files
As I mentioned, I wouldn’t recommend ignoring hosts.deny and putting deny configurations in your hosts.allow file, but (and I suspect this format harks back to older versions of TCP Wrappers) if you decided to do so for the sake of convenience, the syntax might look something like this:
sshd : localhost : allow sshd : 192.168. : allow sshd : .mydomain.tld : allow sshd : ALL : deny
Is Your Service Enabled?
Many Linux services almost surreptitiously enable TCP Wrapper usage by default. If you’re unsure and the man page doesn’t shed any light, then this command should let you know if you can lock the software down using TCP Wrappers.
# ldd /path-to/software | grep libwrap.so
I’ve also seen libwrap0.so work in some cases, so try that if the first command (just libwrap.so ) isn’t successful. In many cases, you’ll know you’ve hit the jackpot if any output comes back from that command at all.
Not So Static
You might be surprised that it’s possible to go one step further with hosts.deny and trigger commands when a nefarious connection is tracked by TCP Wrappers. In the two examples below, the trailing backslashes indicate continuation lines follow.
This first example means all banned connections are logged nicely to /var/log/tcp-wrappers.log :
ALL : .example.com \ : spawn (/bin/echo %a from %h triggered an alarm %d >> \ /var/log/tcp-wrappers.log) \ : deny
Going one step further this next example involves further action:
ALL: ALL: SPAWN ( \ echo "\n\ TCP Wrappers\: Connection refused\n\ By\: $(uname -n)\n\ Process\: %d (pid %p)\n\ User\: %u\n\ Host\: %c\n\ Date\: $(date)\n\ " | /usr/bin/mail -s "Connection to %d blocked" chrisbinnie@email.com) &
Each time a running service port is probed, you can do far more than just logging the attack details; instead, you could receive a nicely formatted email. Try it out and see if you can add to its functionality. The SPAWN command has lots of other uses, including communicating the attack to other servers, which would also instantly ban the attacking IP.
Be warned that generating email for every illegal service access could bring your mail server down, never mind the server being attacked (and fill your Inbox quota up, too), so use this script with some consideration to prevent creating your own specially crafted Denial of Service tool.
The End
Even though I only looked at TCP Wrappers very briefly, I hope you will agree that they are versatile, sophisticated, and yet surprisingly easy to use. There’s little excuse for not using TCP Wrappers in some form for Telnet access, and I’d be reluctant to deploy SSH on any server of value without them.
Finally, one outstanding feature is that the configuration files are “live,” which means you don’t have to stop and start services to refresh changes to the rulesets. This makes scripting around TCP Wrappers very simple and incredibly powerful. Popular packages such as Fail2ban and PortSentry have used them to their advantage very effectively.
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Most Popular
Support Our Work
ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.