禁止针对SSH暴力破解IP的脚本

一、背景

暴露在公网的服务器或多或少都会受到一些企图爆破连接服务器的攻击,ssh的连接是其中常见的一种。一般都是通过指定用户名字典和密码字典的方式来进行统计的。这种攻击通过linux服务器通过lastb的登陆失败记录就可以很容易发现,针对这种攻击,我写了一个简单粗暴的禁用这些攻击IP的脚本,一旦发现同一个IP登陆失败达到指定的次数,就将该IP添加到系统的hosts.deny文件中,并且在系统的定时任务中指定脚本的运行时间,以此达到对该IP的禁止作用。

二、脚本运行效果预览

这是某个服务器执行脚本后的一些IP被添加到文件中的结果。

禁止针对SSH暴力破解IP的脚本

这是该服务器的lastb的最新10条的截图。

禁止针对SSH暴力破解IP的脚本

脚本的运行日志记录的内容。

禁止针对SSH暴力破解IP的脚本

三、脚本的全文解释

脚本全文

#!/bin/bash
# 基于安全的考虑,查询企图暴力破解登录的IP,将这些IP添加到/etc/hosts.deny里面。
# 如果该IP出现在登录失败的列表中次数超过5次,就会被添加到文件中。
# 将此脚本添加到周期定时任务中,以此实现每分钟检查一次数据。
# 查询登录失败的资料,并将其保持为一个临时文件
# 关于awk里面的if判断,因为在运行时发现,登录失败的情况中包括了一种是使用空的用户名,
# 这种情况下,ip实际是被解析成第二列,需要对行的列数进行判断,确保可以获取到的都是ip,
# 而非其他的信息。即只有行里面是10列和9列才是这些登录失败的信息。
lastb|awk '{if(NF==10) print $3;else if(NF==9) print $2}'|sort -n|uniq -c|sort -nr > /tmp/ip.tmp
# 读取上面存下来的文件里面的内容,以行为单位。每行的内容包括该IP地址和该IP出现多少次的统计
while read line
do
num=`echo $line|awk '{print $1}'` # IP出现的次数
ipaddr=`echo $line|awk '{print $2}'` # IP地址
# 如果IP出现的次数是小于5次,则不做任何处理,否则,进行添加到文件的相关处理。
if [ $num -lt 5 ]
then
continue
else
# 判断该IP是否存在于文件中,如果存在则跳出循环,否则将内容该IP添加到文件中。
if `grep -q "$ipaddr" /etc/hosts.deny`
then
continue
else
# 在将IP添加到文件之前,输出一条日志记录,记录了什么时间添加了什么IP到文件中。
echo $ipaddr于`date +%F" "%T`被记录到hosts.deny中。 >>./ipdeny.log
# 将该IP地址添加到文件中,禁止该IP的访问
echo "sshd:$ipaddr" >> /etc/hosts.deny
fi
fi
done < /tmp/ip.tmp

每行内容的解释

lastb|awk '{if(NF==10) print $3;else if(NF==9) print $2}'|sort -n|uniq -c|sort -nr > /tmp/ip.tmp

这行命令其实是多个命令的集合

lastb

查看登录失败的信息。

awk '{if(NF==10) print $3;else if(NF==9) print $2}'

使用awk命令对lastb的内容按照空格进行分隔处理(没有指定 “-F 分隔字符” 的情况下,awk默认按照空格进行分隔)。再判断分隔后的内容,如果是10列或者9列的,10列则输出其第3列的内容,9列则输出其第2列的内容。

为什么会有分隔后出现9列和10列两种情况呢?这是因为lastb输出的内容基本是长这个样子的。

禁止针对SSH暴力破解IP的脚本

第一列是用户名,第二列是登陆方式,第三列是ip地址,第四列是周,第五列是月,第六列是日,第七列开始的时间。

当爆破的时候使用了空用户名,就会导致列出来的该行分隔后变成9列

所以需要判断存在这两种情况下的取数列的不同,大写的NF是指行里面的总列数

sort -n

对上面得到的IP重新进行排序,需要排序是因为uniq命令做统计计数的时候,是相邻的相同内容才会计算为重复,所以用sort排序将这些相同的IP排到一起,再去做去重统计就会比较准确。

uniq -c

去重统计。uniq命令的去重统计,只是将相邻的,内容相同的

sort -nr

sort排序添加-r为反序排序,这样最的话,就可以得到那些重复次数多的排在前面

> /tmp/ip.tmp

将上面通过各个命令处理后的数据重定向(也就是写入)到/tmp/ip.tmp。 /tmp是一个绝对路径的目录,就是跟目录下的tmp目录,这个是linux系统默认的临时文件目录。

" > "这个符号对于后面输出的内容,是会被全部替换掉的。如果是使用" >> "的话,则是追加内容。

while read line
do
num=`echo $line|awk '{print $1}'` # IP出现的次数
ipaddr=`echo $line|awk '{print $2}'` # IP地址
# 如果IP出现的次数是小于5次,则不做任何处理,否则,进行添加到文件的相关处理。
if [ $num -lt 5 ]
then
continue
else
# 判断该IP是否存在于文件中,如果存在则跳出循环,否则将内容该IP添加到文件中。
if `grep -q "$ipaddr" /etc/hosts.deny`
then
continue
else
# 在将IP添加到文件之前,输出一条日志记录,记录了什么时间添加了什么IP到文件中。
echo $ipaddr于`date +%F" "%T`被记录到hosts.deny中。 >>./ipdeny.log
# 将该IP地址添加到文件中,禁止该IP的访问
echo "sshd:$ipaddr" >> /etc/hosts.deny
fi
fi
done < /tmp/ip.tmp

while read line

do

done < /tmp/ip.tmp

这个循环体是用来读取上面保存下来的数据临时文件。按照逐行读取的方式进行读取,直至读完文件里面的全部内容。

num=`echo $line|awk '{print $1}'`

将文件里面的每行第一列内容,也就是出现的次数赋值给num这个变量

ipaddr=`echo $line|awk '{print $2}'`

将文件里面的每行第二列内容,IP地址赋值给ipaddr这个变量

if [ $num -lt 5 ]

then

continue

else

fi

第一次的if循环,只要是判断出现的次数是否小于5次,如果次数小于5次就跳出本次循环。否则就进行下面另外一个判断。5这个次数可以任意修改为一个合理的数字

if `grep -q "$ipaddr" /etc/hosts.deny` 

then

continue

else

echo $ipaddr于`date +%F" "%T`被记录到hosts.deny中。 >>./ipdeny.log

echo "sshd:$ipaddr" >> /etc/hosts.deny

fi

这第二个循环则是判断获取到的IP是否已经存在与限制文件中,如果存在的话,则跳过该行的循环,否则就见该IP添加进hosts.deny文件中。

四、添加定时任务

添加任务之前,最好先确定需要执行定时任务的用户对文件有执行权可以通过下面的任务给脚本添加执行权。

ll 脚本名.sh

如果得到这样的输出,就是脚本有执行权。

-rwxr-xr-x. 1 root root 2127 Jan 12 19:36 脚本名.sh

如果结果是这样的前面都没有x,就是没有执行权

-rw-r--r--. 1 root root 2127 Jan 12 19:36 脚本名.sh

执行下面的命令来添加执行权

chmod +x 脚本名.sh

脚本使用下面的命令添加到系统的定时任务中去

crontab -e

然后在里面的最后一行添加类似以下的内容:

*/1 * * * * sh /绝对路径/脚本名称.sh

这个例子的*/1 * * * *是指每分钟执行一次,也有人喜欢写成* * * * *,这五个*是linux系统定时任务指定时间用的。分别代表分,时,日,月,周,需要是整数的数字,周的星期日则可以是0也可以是7来表示。