Securing SSH with tcp wrappers

From Sfvlug

Just about everything on a modern Linux distro these days has tcp_wrappers support compiled in. I'll start with some basics on tcp_wrappers configuration. You should definitely also read the hosts_access(5) and hosts_options(5) man pages.

The basic flow of tcp_wrappers is, anything listed in /etc/hosts.allow is allowed. If it isn't found there, processing moves on to /etc/hosts.deny and anything found there is denied. Finally, anything not in either is simply allowed.

To configure tcp_wrappers, start with a policy that denies everything by default.

Contents

/etc/hosts.deny

ALL: ALL

By setting up a default deny policy in this way, you move all important processing instructions to the /etc/hosts.allow file. It is possible to add deny rules to hosts.allow by ending a line with an additional colon and the DENY command. I start by allowing access to localhost and my LAN. Notice, IPv4 networks must be specified with the full netmask, but IPv6 networks must be designated with CIDR notation.

/etc/hosts.allow

ALL: localhost 127.0.0.1 ::1
ALL: 192.168.1.0/255.255.255.0 [fe80::]/10 [fec0::]/10

sshd: /etc/hosts.deny.sshd : DENY
sshd: /etc/hosts.allow.sshd : spawn ( denyhosts )

I have included two rules to govern sshd specifically. The first denies any address listed in /etc/hosts.deny.sshd. The second only allows addresses listed in /etc/hosts.allow.sshd. The unspoken final rule is in hosts.deny, anything that hasn't matched so far will be denied. The allow line also shows an example of launching a command when the rule is triggered.

/etc/hosts.allow.sshd

I live in the United States. I work in the United States. I have never left the United States without knowing it was going to happen at least days in advance. So the likelihood of finding myself outside my country's borders and suddenly needing to SSH into my server at home is as close to nil as I can imagine. But, I do travel several times a year within my country's borders, and I don't know what IP I might be coming from. So within this file, I have listed all the IPs which are assigned inside the US.

How do you get this kind of information? I don't know how the people I got it from compiled their list, but there are a few sources on the Internet. I used to get this from a site which seems not to be in service any longer, blackholes.us. A few more sites have stepped up to fill the niche. Currently, I'm using http://countries.nerd.dk. There are other sites that offer this information as well, like http://software77.net/geo-ip/. If you search for "country by IP address," you will find more.

The files downloaded from countries.nerd.dk are in a format for a particular DNS server. I had to convert them to address/netmask format, one per line. Note that the version of ipcalc I have returns values for use in shell scripts; if I give the command ipcalc -m 10.0.0.0/8 its output is NETMASK=255.0.0.0.

awk 'NR > 1 {print $1}' us.countries.nerd.dk.rbldnsd | while read LINE ; do
    eval `ipcalc -m $LINE`
    echo ${LINE%%/*}/$NETMASK
done > /etc/hosts.allow.sshd

/etc/hosts.deny.sshd

By default, DenyHosts appends IPs which attempt to violate your security to the end of /etc/hosts.deny. Since this file already contains a rule that denies every IP, this strategy is flawed.

What we will do is configure denyhosts to drop offending IPs into a different file. One more concern is that denyhosts prepends "sshd:" to each line, and this won't work for a file included in hosts.allow.

After installing denyhosts, edit denyhosts.conf and modify the following lines. Change

HOSTS_DENY = /etc/hosts.deny

to

HOSTS_DENY = /etc/hosts.deny.sshd

And

BLOCK_SERVICE  = sshd

to

BLOCK_SERVICE  = 

Additionally, denyhosts can expire entries after a period, and there is a synchronization service offered, so IPs that attack other hosts on the Internet can be blocked by your computer. But do think about this as a potential denial of service before enabling this feature.

iptables

In spite of the fact denyhosts will eventually drop offending IPs into /etc/hosts.deny.sshd, it only runs every minute by default. I have seen brute force attempts on the order of dozens of attempts per minute. So, even if denyhosts will block an IP with only a few failed login attempts, the attackers might get 20 or more attempts in that first minute. Here is some iptables magic I found to limit those attempts to one every five seconds.

iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 5 --hitcount 2 -j DROP
iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW -j ACCEPT

Actually, this is better than only allowing one connection every five seconds. What this actually does is block the second and subsequent new connection to port 22, until no more connections come in for five seconds. So a legitimate user can retry after five seconds. An automated attack is not likely to understand this timing, and just keep trying to get back in, or give up.

One final note about iptales. You may be wondering why I do all this with tcp_wrappers and not with iptables. Simple, really. If I configure denyhosts to execute iptables, then first of all, the accept and drop rules lists would be really huge, and iptables stores all of its rules in memory. Also, I want my protection to be preserved across reboots. If denyhosts executes iptables directly, it doesn't store dropped IPs to disk anywhere, which means I have to rely on iptables-save, which wouldn't necessarily run in the event of a power failure. So, it's a trade off: increased disk access for reduced overall memory consumption and reliability.


Jeff

Personal tools

Parse error: syntax error, unexpected ':' in /home/editthis/www/editthis.info/extensions/custom_code/skins_MonoBook.php_after_toolbox.php on line 16