Misconceptions About TCP Wrappers
Both reading through the articles and discussion forums on security, and in discussing security with friends, I have encountered some misconceptions surrounding hosts.deny/hosts.allow and TCP Wrappers. The purpose of this article is to clear up this confusion, and hopefully raise some awareness about security. This document is not intended as a "how to," but more as an explanation of the theory behind hosts.deny and ipchains. This is aimed at Linux 2.2.x, but should translate well to other UNIX platforms.
hosts.deny and hosts.allow are the controlling configuration files for Wietse Venema's TCP Wrappers, with which you can, "Monitor and filter incoming requests for the SYSTAT, FINGER, FTP, TELNET, RLOGIN, RSH, EXEC, TFTP, TALK, and other network services." A brief intro can be found at "ftp.porcupine.org/pub/security/tcp_wrappers_7.6.BLURB
TCP Wrappers can be a useful tool, and most beginning security tutorials will state that you must have TCP Wrappers installed if your system is going to be secure. However, I have also found that many of these tutorials will describe methods of securing your system that eliminate the usefulness of TCP Wrappers, such as disabling inetd and along with it, shutting down all the services that are wrapped by TCP Wrappers.
Daemons that are "wrapped" by TCP Wrappers are started by inetd in conjunction with tcpd. Some examples are telnetd, ftpd, talk, finger, etc. The majority of these programs are the insecure daemons that just about every security tutorial will tell you to immediately comment out of inetd.conf, shutting them down on your system (once you restart inetd, of course). For the most part, this is good advice. Many of these services are not used by the common administrator, and serve to create the potential for future exploit by an attacker.
Once the average person is done editing their inetd.conf file, they generally are down to just FTP and Telnet being run by inetd. However, they may also be running other services like a web server, mail server, or DNS server, which aren't being started by inetd. If this is the case, it is very important to understand how TCP Wrappers works, or else you may have a false sense of security.
Ignoring libwrap for the moment, services which are not started by tcpd are not protected by TCP Wrappers. Because of this, if your security policy is to add hosts/networks to hosts.deny when you want to block them from accessing your server, then you are not actually blocking them from contacting many of your services, or the server in general. You may have a false sense of confidence that you are protected from this attacker, meanwhile, they are busy tracking down the latest BIND exploit, which will slip right past your hosts.deny rules and you'll never even know it. Lets take a look at how this work:
Here is the default configuration for in.telnetd from a standard RedHat 6.1 install:
telnet stream tcp nowait root /usr/sbin/tcpd in.telnetd
When a host attempts to connect to the Telnet server on this system, this is what happens (in a reasonable amount of detail):
1.) inetd detects a connection to port 23 on the system. It recognizes that this is the port for Telnet (based on the entry in /etc/services), and goes to start the server.
2.) /usr/sbin/tcpd is called by inetd, to start in.telnetd. tcpd will check hosts.deny and hosts.allow against the inbound connection. /usr/sbin/tcpd is the wrapper.
3.) If hosts.deny/hosts.allow permit the connection, in.telnetd is started. Otherwise, the connection is refused and logged through syslogd.
In the case of BIND, which is generally not started from inetd, the connection does not get intercepted by inetd, does not get passed to tcpd, and hosts.deny is never consulted. Also, simply starting a service from inetd does not ensure that it is protected via TCP Wrappers; there must be a wrapper designed for that particular daemon.
If you are using hosts.deny as your only means of blocking inbound traffic, you are not protecting yourself!
In order to block your Linux system from accepting data from a particular address, or fitting some other rules (like destination or source port, etc), you will have to use ipchains or block the traffic before it reaches your host via a hardware firewall or router. For most home users, ipchains is the only real option.
ipchains blocks traffic at the kernel level (this is why, if you have a packet logged by ipchains, it will be the kernel sending the message to the logger), far before it is interpreted by inetd or tcpd.
The configuration for ipchains is more complicated than hosts.deny, and since the rules are stored in memory, rather than in a file, it gets reinitialized on every reboot. However, it is quite easy to build an ipchains ruleset to be executed on startup (e.g., the traditional rc.firewall), and the extra work is well worth the added security. Alternatively, firewall software like PortSentry may be configured to automatically create ipchains rules in the case of unexpected connection attempts.
So why not just start up all your daemons from inetd? This is possible, but if you are getting a lot of traffic to your site, the overhead may be more than your system can handle. inetd would have to intercept every inbound connection and start up a new server daemon. This requires processor time and memory for the initial work where inetd recognizes an inbound connection, where it kicks off to tcpd, where tcpd checks hosts.allow and hosts.deny, and then you have to deal with the startup of the server daemon for each new connection. This is hardly an elegant option, and in many cases it just isn't possible.
Additionally is the potential for exploit of inetd. While I am not aware of any recent security issues directly affecting inetd, it does run as root, and so could potentially become the target of future exploits. For example, inetd might be vulnerable to the security problem that affected Linux kernel 2.2.15, where programs could become unable to alter their effective UID. This is conjecture on my part, but it does seem reasonable.
1.) Some daemons can be made aware of tcp_wrappers by inclusion of libwrap. In these cases, it is not necessary to start the program through inetd for hosts.deny to be checked. libwrap is not addressed in this article for two reasons: first, libwrap is a more advanced topic than this article was intended to be; second, a lack of information available to me at the time of writing prevents me from making any educated statements on the topic.
2.) SSH can be used to provide a secure replacement for Telnet. SFTP and SCP are secure replacements for FTP. There are even free, easy to use client programs for SSH and SCP for windows such as PuTTY and WinSCP.
3.) RedHat introduced a shell script in version 6.2 that lets you interact with ipchains in the System V init style, including an option to save the current rules. This takes some of the work out of maintaining ipchains, but you will still need to custom-craft your ipchains rule set.
4.) As an example of this startup overhead, consider the SSH daemon. Each time sshd starts, it generates a new host key, which is very processor intensive. If the server was forced to generate a new host key for each inbound connection, the connection could possibly time out before the host key was ready. (Thanks to Matthew Block for pointing this out.)
For more information:
TCP Wrappers: www.linuxdoc.org/LDP/LG/issue46/pollman/tcpwrappers.html
The current version of this document can be found at: www.bhodisoft.com/bswopes/nhf/ipchains-vs.-hosts.deny.html