Blacklist IPs that attack SSH and FTP services on a Linux machine

I manage a Linux server (Redhat EL4, kernel version 2.6.9-34). Yesterday,  I found the server was attacked relentlessly from 121.101.223.211. It seemed to be a brutal force dictionary attack. What to do then? The obvious way is to manually add that IP to host.deny, that is, append “sshd:  121.101.223.211” to /etc/host.deny in a separate line.

But that is so un-Linux and so unsustainable.

It was time to excise my bash-fu and sed-fu. The attacks were logged in /var/log/messages and looked like this (part):

sshd(pam_unix)[6548]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=121.101.223.211  user=root

The idea is to periodically check (hourly by cron) the log file and find IPs with a large number (≥ 16) of unsuccessful SSH login attempts. With the following line, the IPs can be parsed out, sorted, and counted:

sed -n "/${SERVICE}.*authentication failure/ s/^.*rhost=\([0-9.]*\).*$/\1/p" "${LOGFILE}" | sort | uniq -c

where SERVICE=sshd and LOGFILE=/var/log/messages. The output is like this:

     19 112.187.163.188
    140 121.101.223.211
     45 211.139.10.219

The leading numbers are counts of unsuccessful SSH login attempts, the latter are corresponding IPs. Then echo the IPs to /etc/host.deny if they are not there yet and their counts are no less than 16. Of course, I should also protect FTP service (vsftpd).  Not too hard, ha!

The following is the complete bash script (ip_block). Put it into /etc/cron.hourly and it will work for you 24/7, for free!

#! /bin/bash
# add ips with more invalid login attempts than permitted to hosts.deny
# usage: ip_block [invalid login limit, default to 16]

if [ "$EUID" -ne 0 ]
then
    echo " You must be root to run this script!"
    exit -1
fi

SYSLOG_FILE="/var/log/messages"
HOSTDENY_LIST="/etc/hosts.deny"
[ "$#" -ge "1" ] && LOGIN_LIMIT="$1" || LOGIN_LIMIT=16

function add_to_list ()
{
    SERVICE="$1"
    LOGFILE="$2"
    LSTFILE="$3"
    MAXCOUNT="$4"

    sed -n "/${SERVICE}.*authentication failure/ s/^.*rhost=\([0-9.]*\).*$/\1/p" "${LOGFILE}" | sort | uniq -c | while read line
    do
        count=`echo $line | sed 's/  *.*$//'`
        ip=`echo $line | sed 's/[0-9]*  *//'`
        n=`grep -c -e "${SERVICE}: *$ip" "${LSTFILE}"`

        [ "$count" -ge "$MAXCOUNT" -a "$n" -eq "0" ] && echo "${SERVICE}: $ip" >> "${LSTFILE}"
    done
}

add_to_list sshd   "${SYSLOG_FILE}" "${HOSTDENY_LIST}" $LOGIN_LIMIT
add_to_list vsftpd "${SYSLOG_FILE}" "${HOSTDENY_LIST}" $LOGIN_LIMIT

About Shuhua Zhang

Born in Anhui, China. Awarded a doctor's degree in electronic engineering from Tsinghua University. Now a postdoc researcher working on audio and speech signal processing, especially source separation, in gipsa-lab, Grenoble-INP, France.
This entry was posted in coding and tagged , , , , , . Bookmark the permalink.

Leave a comment