Securing SSH Using Denyhosts

Sponsored Link
SSH is a great way to remotely administer a server. However, it still has a number of issues when you open it up to the world. The server and client communications are secure but that doesn't mean the hosts involved are. Opening an SSH service to the world allows for brute force attacks and means that the carbon interface is still the weakest link.

There are some very simple steps you can take to really harden remote access over SSH, especially if you can't simply tie the service down to a limited number of source ports.

First things first, sshd_config. In Ubuntu this is usually found in /etc/ssh and can be used to configure a great number of features. The simplest ones to deal with are always the best. Restricting the users who can login via SSH is a first principle. This can be done in one of two ways, by user or by group. AllowGroups allows any user in this group authenticated access to the server via SSH. A more fine grained approach is to use the AllowUsers option.

Another easy win is by moving the listen port from 22 to some other randomly assigned port. This reduces the likelihood of a scan showing SSHD running.

Other steps you might want to take include disabling root access, disable password authentication and using keys only.

The next step is a wee tool called Denyhosts ( Make sure you've got the additional sources enabled in /etc/apt/source.list and then type:

sudo apt-get update

sudo apt-get install denyhosts

DenyHosts is a python script intended to be run by Linux system administrators to help thwart SSH server attacks (also known as dictionary based attacks and brute force attacks).

Denyhosts acts as a dynamic blocker for SSH and other services. It relies on the /etc/hosts.deny and hosts.allow. It dynamically builds a list of hosts that repeatedly connect to your server. By default the service will block connections from IP sources that are repeated attempting to connect and access your host. The denyhosts process is configured in /etc/denyhosts.conf

There are additional things that can be done using iptables to rate limit connections and you should always run a firewall on your hosts but I'll deal with that in a separate post.

Once you've put these steps in place you can rest assured the SSH on a public facing host is much more secure, there's no guarantees but every little helps.

Sponsored Link

Related posts

10 thoughts on “Securing SSH Using Denyhosts

  1. This is great but I also recommend sshblack – a perl script that tails your syslog or messages and sees the sshd failed login attempts. You can then set a number of failed attempts before adding the host to a deny rule in your iptables firewall. This will block any traffic from this host for a set amount of time. Works wonders! I’ve adapted it to check my apache logs for certain know virus/worms and block those hosts too.

  2. All great, I’ve used DenyHosts year or two ago. But I have much simpler and more effective solution. Just start SSH service on another port. Yeah, that’s. Instead of 22 use anything else, for example 10022. In /etc/ssh/sshd_config change Port 22 to Port 10022, restart and you’re done. I guarantee you that not one probe will reach your port. DenyHosts will still pass a few, before it blocks the port. And you can remember the new port easily, or just put it into ~/.ssh/config and be done.

    The effectiveness of this approach is based on the fact that port 22 is hardcoded in all those cracker scripts, and they don’t bother searching for ssh on another port. It’s much harder for them if they also need to find the port, so they count on many people who leave ssh at default port.

    Try this approach and you won’t regret it.

  3. I don’t know about that !!
    in my /var/log/secure I see crackers going after
    a number of different ports to find my ssh port,
    It seems they are checking other ports more than
    port 22.
    I also use fail2ban, and ban IPs for about 20 minutes and i don’t seem to see any repeats of same IP after that.

  4. Many solutions to the same problem: I use sshguard, which basically does the same.

    There are other things you can do as well, like limiting the number of IP addresses you can connect from in your authorized_keys file (I do that for several users): the account of our service desk to access the machines has been limited to internal IP addresses only, since this account has the power (sudo) to shutdown or reboot the machine. I don’t want that to happen from ‘the outside’.

    The authorized_keys file looks a bit like this:
    from=”192.168.1.*,192.168.2.*,192.168.200.*” ssh-rsa [key goes here] [user]

    The file itself is immutable for the user himself.

  5. All of the above suggestions are fine, except that they all leave a small window of opportunity for a brute force scan to suceed. If during the first few trys, they randomly find a matching user/pass pair, they are in, even with all of the above options. Yes, low likelyhood, but not zero likelyhood.

    A better solution is ssh-faker:

    It is setup so that only the set of IP addresses you actually use to connect to it are allowed through to the sshd deamon. Everyone else stops at ssh-faker and needs to enter a password here first, before they even get to the sshd deamon the very first time. Result, hacker attack scans for sshd ports (no matter what port) are stopped dead because they think they find an sshd, but it does not work like an sshd. So they don’t even get one try to brute force a user/pass to sshd.

    You, when you connect from a new IP, simply telnet to your machine and port (telnet machine 22) and enter your ssh-faker password. Ssh-faker then puts an entry into /etc/hosts.allow that then lets you connect straight through to sshd from that IP.

    If you usually connect from only 2 or 3 static ip locations, you’ll only have 2 or 3 IP’s in hosts.allow. If you connect from varied different locations, all you have to do is periodocally clean the entries from hosts.allow to make sure no one else gets a try.

  6. DenyHosts is great! I’m so glad I came across this article. Not 10 minutes after installing it, I’ve already got IP’s in my deny list. It’s not like I run a server or anything, it’s my desktop machine at home. I had no idea this scanning happens so much so often. Thanks for bringing this to our attention (or at least mine).

  7. denyhosts is working great for me, blocks all kinds of bad guys. The only problem I am having is since I changed from Mandriva to Ubuntu, I cannot get a reprt sent to ADMIN_EMAIL. I had a little trouble with this in Mandriva as well, but cannot remember how I overcame it.

    Any suggestions would be welcome!

  8. It turns out you can easily block whole countries and domains in hosts.deny. So I’m now blocking all countries except the US, plus some other specific domains. Combined with denyhosts (Python script that looks for bad SSH login requests in /var/log/messages and shares attackers in a global database), it has cut my incoming SSH attacks from dozens per day to essentially zero.

    I can’t switch ports because at work they only let me out on port 22.

    I have a very simple hosts.allow file, just 2 entries. This ensures that I can always get in via my employer’s VPN, which gives me the same outside IP all the time.

    # hosts.allow This file contains access rules which are used to
    # allow or deny connections to network services that
    # either use the tcp_wrappers library or that have been
    # started through a tcp_wrappers-enabled xinetd.
    # See ‘man 5 hosts_access’ and ‘man 5 hosts_options’
    # for information on rule syntax.
    # See ‘man tcpd’ for information on tcp_wrappers.

    ALL: # Local network
    ALL: a.b.c.d # My employer’s VPN

    # hosts.deny This file describes the names of the hosts which are
    # the new secure portmap uses hosts.deny and hosts.allow. In particular
    # you should know that NFS uses portmap!



    ALL: .edu #Educational institutions (e.g. colleges)

    ALL: .ac
    ALL: .ad
    ALL: .ae
    ALL: .af
    ALL: .ag
    ALL: .ai
    ALL: .al
    ALL: .am
    ALL: .an
    ALL: .ao
    ALL: .aq
    ALL: .ar
    ALL: .as
    ALL: .at
    ALL: .au
    ALL: .aw
    ALL: .az
    ALL: .ax
    ALL: .ba
    ALL: .bb
    ALL: .bd
    ALL: .be
    ALL: .bf
    ALL: .bg
    ALL: .bh
    ALL: .bi
    ALL: .bj
    ALL: .bm
    ALL: .bn
    ALL: .bo
    ALL: .br
    ALL: .bs
    ALL: .bt
    ALL: .bv
    ALL: .bw
    ALL: .by
    ALL: .bz
    ALL: .ca
    ALL: .cc
    ALL: .cd
    ALL: .cf
    ALL: .cg
    ALL: .ch
    ALL: .ci
    ALL: .ck
    ALL: .cl
    ALL: .cm
    ALL: .cn
    ALL: .co
    ALL: .cr
    ALL: .cs
    ALL: .cu
    ALL: .cv
    ALL: .cx
    ALL: .cy
    ALL: .cz
    ALL: .de
    ALL: .dj
    ALL: .dk
    ALL: .dm
    ALL: .do
    ALL: .dz
    ALL: .ec
    ALL: .ee
    ALL: .eg
    ALL: .eh
    ALL: .er
    ALL: .es
    ALL: .et
    ALL: .eu
    ALL: .fi
    ALL: .fj
    ALL: .fk
    ALL: .fm
    ALL: .fo
    ALL: .fr
    ALL: .ga
    ALL: .gb
    ALL: .gd
    ALL: .ge
    ALL: .gf
    ALL: .gg
    ALL: .gh
    ALL: .gi
    ALL: .gl
    ALL: .gm
    ALL: .gn
    ALL: .gp
    ALL: .gq
    ALL: .gr
    ALL: .gs
    ALL: .gt
    ALL: .gu
    ALL: .gw
    ALL: .gy
    ALL: .hk
    ALL: .hm
    ALL: .hn
    ALL: .hr
    ALL: .ht
    ALL: .hu
    ALL: .id
    ALL: .ie
    ALL: .il
    ALL: .im
    ALL: .in
    ALL: .io
    ALL: .iq
    ALL: .ir
    ALL: .is
    ALL: .it
    ALL: .je
    ALL: .jm
    ALL: .jo
    ALL: .jp
    ALL: .ke
    ALL: .kg
    ALL: .kh
    ALL: .ki
    ALL: .km
    ALL: .kn
    ALL: .kp
    ALL: .kr
    ALL: .kw
    ALL: .ky
    ALL: .kz
    ALL: .la
    ALL: .lb
    ALL: .lc
    ALL: .li
    ALL: .lk
    ALL: .lr
    ALL: .ls
    ALL: .lt
    ALL: .lu
    ALL: .lv
    ALL: .ly
    ALL: .ma
    ALL: .mc
    ALL: .md
    ALL: .mg
    ALL: .mh
    ALL: .mk
    ALL: .ml
    ALL: .mm
    ALL: .mn
    ALL: .mo
    ALL: .mp
    ALL: .mq
    ALL: .mr
    ALL: .ms
    ALL: .mt
    ALL: .mu
    ALL: .mv
    ALL: .mw
    ALL: .mx
    ALL: .my
    ALL: .mz
    ALL: .na
    ALL: .nc
    ALL: .ne
    ALL: .nf
    ALL: .ng
    ALL: .ni
    ALL: .nl
    ALL: .no
    ALL: .np
    ALL: .nr
    ALL: .nu
    ALL: .nz
    ALL: .om
    ALL: .pa
    ALL: .pe
    ALL: .pf
    ALL: .pg
    ALL: .ph
    ALL: .pk
    ALL: .pl
    ALL: .pm
    ALL: .pn
    ALL: .pr
    ALL: .ps
    ALL: .pt
    ALL: .pw
    ALL: .py
    ALL: .qa
    ALL: .re
    ALL: .ro
    ALL: .ru
    ALL: .rw
    ALL: .sa
    ALL: .sb
    ALL: .sc
    ALL: .sd
    ALL: .se
    ALL: .sg
    ALL: .sh
    ALL: .si
    ALL: .sj
    ALL: .sk
    ALL: .sl
    ALL: .sm
    ALL: .sn
    ALL: .so
    ALL: .sr
    ALL: .st
    ALL: .sv
    ALL: .sy
    ALL: .sz
    ALL: .tc
    ALL: .td
    ALL: .tf
    ALL: .tg
    ALL: .th
    ALL: .tj
    ALL: .tk
    ALL: .tl
    ALL: .tm
    ALL: .tn
    ALL: .to
    ALL: .tp
    ALL: .tr
    ALL: .tt
    ALL: .tv
    ALL: .tw
    ALL: .tz
    ALL: .ua
    ALL: .ug
    ALL: .uk
    ALL: .um
    #ALL: .us
    ALL: .uy
    ALL: .uz
    ALL: .va
    ALL: .vc
    ALL: .ve
    ALL: .vg
    ALL: .vi
    ALL: .vn
    ALL: .vu
    ALL: .wf
    ALL: .ws
    ALL: .ye
    ALL: .yt
    ALL: .yu
    ALL: .za
    ALL: .zm
    ALL: .zw

  9. @Peter Kuykendall

    In general it is better to have

    ALL:ALL in hosts.deny and have your allowed hosts or domains in hosts.allow. Then you do not need that big list of things in hosts deny just a much smaller, more maintainable list in hosts.allow.

    I allow a few specific IP addresses and one a few domains here and that works nicely.

Leave a comment
