Setting Up "hosts.allow"
The syntax of /etc/hosts.allow is actually quite simple and easy to understand, unlike, say, sendmail.cf or the scattered nature of qmail. Most of the rules and settings are used in the following form:
service : host/network : option [: option] ...
The name of the dæmon or service program that rule will be written for. Examples include popa3d, imapd, and sshd. You can also use the ALL wild card to cover all services and dæmons.
The host or network that the rule will apply to. You can use the any of the following notations to best suit your needs.
* IP Address: 10.4.2.5 or 172.16.7.4
* IP Class A/B/C Subnet: 10.4. or 172.16.7.
* IP Network with Subnet Mask: 10.4.0.0/255.255.0.0 or 172.16.7.0/255.255.255.0
* IPv6 Addresses: [2f3f::]/10 or [4c20:fe37:1:1::]/64
* Fully Qualified Domain Name: foo.insecure.net or bad.hairday.insecure.net
* Domain Name: insecure.net or hairday.insecure.net
* Relative Hostname: foo or bad
* Wild Cards: Wild card optionss that can be used are:
o ALL: All clients regardless of IP address or domain name.
o PARANOID: Clients that have hostnames that don't match its ident/domain lookup names. This does not apply to machines that do not have any reverse domain lookup names.
o LOCAL: A client that comes from the same machine or domain as the host.
o UNKNOWN: A client that cannot be resolved to anything known.
o KNOWN: A client who's name and addresses can be resolved.
A command or an option telling it how to treat the connection request. In majority of the cases, you will see or use allow or deny for the command. spawn or twist commands can also be used to send a customized error to the requester or send an alert to the administrator. Some of the most commonly used options will be covered a bit later in this article.
The option field for each of the rules provides the basic allow and deny directives, plus several other options that allow you to customize how it logs the connection attempt (via syslog or e-mail), run a system command and other options that control the connection. Below are the most common options that you may use in /etc/hosts.allow and how they affect the connection.
As the name implies, it simply lets the connection through; this option must be at the end of a rule.
Denies the connection without providing the requester any chance to try again. All denied connections are logged to syslog with the service and host that the deny rule matched.
Generates a new process that will run the command(s) given, but the spawn commands themselves will not allow or deny the connection, so you must have either " : allow" or " : deny" at the end of the rule in order to make the rule truly effective.
Like spawn, it will create a new process to allow you to run the necessary commands, with the common standard input, output and error objects are linked to the twist option. This option can be used to print out a customized message when someone tries to connect to a service like ftpd. Instead of allowing or denying the connection immediately, the twist commands determine if the requester should be let through or not. A twist option followed by only an echo command will send the echo'd text to the client and closes the connection.
This option does a username lookup of the requester via ident, and checks to see if the username and the information povided by the requester match up. The timeout option is used to limit the amount of time that the machine will wait to receive a ident response.
Like the nice in BSD, this option allows you to change a process' priority; thus provides means to limit how much processor a guest or a not-quite-so-trusting client can use.
This option searches for a file named after the specified service in /dir/path, reads in the contents, resolves expansions (a list of expansions can be found in hosts_access(5) manual page. This option is valid only for TCP services and dæmons, not for UDP services.
Comments in the /etc/hosts.allow are very similar to shell scripts where each comment line starts with a hash mark (#) and extends to the end of the line. There is one significant difference in how comments are handled in hosts.allow and shell scripts is that comments are not allowed after a rule. The following example shows valid uses of comments in the file.
# this is a valid comment
portmap : 188.8.131.52 : deny
# so is this
sshd : 184.108.40.206 : deny # but not this one
Tightening Down Your Services
Once you understand the syntax of setting up access controls and options, you are now ready to write your own hosts.allow file. Without the use of deny rules all running TCP-wrapped services or enabled in inetd will be wide open until you explicitly. The example below allows shows some of the possible ways to configure the hosts.allow file.
portmap : localhost : allow
portmap : 10. : allow
portmap : .insecure.net : allow
portmap : ALL : deny
sshd : ALL : allow
sshd : bad.host : deny
sshd : 88.4.2. : deny (1)
ALL : ALL : deny
Taking a look at the portmap section of the configuration, we are allowing localhost, any machines with an IP address starting with 10. and any machines that have .insecure.net for a domain name or ident name. All other machines and hosts are disallowed. If you do not include localhost in a rulset, you will lock the host out which may or may not be a good thing.
The sshd section states that all machines are allowed at first, but then blocks any machine with the relative hostname of bad.host or any machines with an IP address starting with 88.4.2 (1). The last rule in the configuration pretty does what it says, which is to deny all machines access to the other "tcp wrapped" services and dæmons that do not have any explicit rules stating otherwise. If you want to allow all hosts to the other services without intervention, replace the last line with:
ALL : ALL : allow
If you do include a rule that denies all hosts access to a particular service and forget to include the localhost or management hosts, you may end up inadvertantly lock yourself (and others) out. If you setup a rule that allows all hosts to use a service and deny specific hosts, you may still leave yourself open to other untrusted hosts. To limit such exposures, check /etc/inetd.conf for any services that are enabled but really do not need to be enabled, and disable them. One perfect example on restricting access to services that are known to be inherently insecure is to only allow clients from the internal network access to the telnet dæmon (telnetd).
Words Of Caution
No matter how tempting it is to setup a rule that says
ALL : ALL : deny
at the very top if the hosts.allow file, DO NOT DO IT!. The access control facility takes each rule literally, meaning that it will see that rule and deny the connection before going through the rest of the rules. The same applies to denying access to all hosts to a particular service first, then allowing the trusted hosts; the deny rule will be enacted upon and the connection is rejected. For example, a bad way of writing a ruleset would be:
sshd : ALL : deny
sshd : localhost : allow
sshd : 10.0.3. : allow
Instead, re-write the ruleset to be:
ssh : 10.0.3. : allow
ssh : localhost : allow
ssh : ALL : deny
That way, the hosts that should be allowed will be allowed through, the rest will fail the first two rules and then finally gets denied when it reaches the deny rule.
If you write any rules that use domain or hostnames for the second field, you could be denying access to hosts that do have a valid reverse domain lookup name (this includes IP addresses that do not have any PTR records setup on the domain name server that is responsible for that subnet) or do not have any ident dæmons running, if you do not include any allow rules for them before a ": ALL : deny" rule. Instead, you may want to allow specific subnets or IP address ranges to allow access to those services.
Any changes that are made to /etc/hosts.allow take effect immediately (there is no need to restart inetd or restart the network services). There are some options that will only work with TCP services or cause problems when used with UDP services. Some of the caveats and warnings are documented in both hosts_access(5) and hosts_options(5) manual pages. By using rules to allow or deny connections, each connection made to the host requires some additional overhead to check the validity of the client information and to compare that information with each of the applicable rules. Depending on the average load on the host, number of connection attempted in a given time, and the available resources, using hosts.allow may not be a viable option.
Some services or dæmons provide more detailed facilities to allow or deny connections based on IP address, domain name and other criteria that could be easier to configure than what is possible with hosts.allow. In addition, hosts.allow cannot act as a filter for malicious code or application level denial-of-service attacks (like the file-globbing exploit in earlier versions of ProFTPD or a man-in-the-middle attack with ssh).
hosts.allow is quite an interesting and useful facility that can help increase the security of the host, but typos and poorly written rules can make the host as or more susceptable to exploits than without those rules. Typos could also end up locking yourself out of the box when you least expect it. As stated above, using the access control facility for "tcp wrapped" services and dæmons can and probably will take a hit on the host's performance and possibly limit the response rate for the client.
The facility is not a complete security solution nor should it be treated as such, rather it's a compliment to packet filtering and firewall solutions available (be it gratis, free-speech or commercial). There are some features that are available only by using the hosts.allow facility such as redirecting specific clients to other services, or returning specific error messages, and banners specific to the service and/or client. Nonetheless, this is a great facility that some people overlook as a method to provide a simple means to deny access to specific servies to specific hosts.
If the last line of a hosts access file is not a newline character (created by pressing the Enter key), the last rule in the file fails and an error is logged to either /var/log/messages or /var/log/secure. This is also the case for a rule that spans multiple lines without using the backslash. The following example illustrates the relevant portion of a log message for a rule failure due to either of these circumstances:
warning: /etc/hosts.allow, line 20: missing newline or line too long