1、需求描述
-
服务器监控脚本,当发现某一台主机向服务器进行SSH远程管理,在1分钟内输错密码3次,使用firewalld进行响应,禁止此IP再次连接,6个小时候后再次允许连接
2、实现步骤
[root@localhost ~]# cat blockip.sh
#!/bin/bash
# 功能描述:分析系统登录日志,过滤异常IP地址,并通过防火墙禁用该IP
# 强制退出时关闭所有后台进程
#trap 'kill $one_pid exit' EXIT INT
# 日志文件路径
logfile=/var/log/secure
blockfile=/tmp/blockip.txt
one_minute(){
while :
do
# 获取计算机当前时间,以及1分钟前的时间,时间格式:
# %b(月份简写,Jan) %e(日期,1) %T(时间 18:00:00)
# LANG=C 的作用是否防止输出中文
# 使用local定义局部变量的好处是多个函数使用相同的变量名也不会冲突
local curtime_month=$(LANG=C date +"%b")
local curtime_day=$(LANG=C date +"%e")
local curtime_time=$(LANG=C date +"%T")
local one_minus_ago=$(LANG=C date -d "10 minutes ago" +"%T")
# 将当前时间转换为距离 1970-01-01 00:00:00 的秒数,方便后期计算
local curtime_seconds=$(LANG=C date +"%s")
# 分析1分钟内所有的日志,如果密码失败则过滤倒数第4列的IP地址
# 通过管道对过滤的IP进行计数统计,提取密码失败次数大于等于3次的IP地址
# awk 调用外部shell的变量是,双引号在外面表示字符串("''"),单引号在外边便是数字('""')
pass_fail_ip=$(awk '$1=="'$curtime_month'" && $2=='"$curtime_day"' && $3>="'$one_minus_ago'" && $3<="'$curtime_time'" { if($6=="Failed" && $9!="invalid") {print $(NF-3)}}' $logfile | awk '{IP[$1]++} END{ for(i in IP){ if(IP[i]>=3) {print i} } }')
#echo "$pass_fail_ip"
# 将密码失败次数大于3次的IP写入黑名单文件
# 每次写入前需要判断黑名单中是否已存在该IP
# 写入黑名单时附加时间标记,实现仅将IP放入黑名单特定的时间
for i in $pass_fail_ip
do
if ! grep -q "$i" $blockfile
then
echo "$curtime_seconds $i" >> $blockfile
fi
done
# 提取无效账户登录服务器3次的IP地址,并将其加入黑名单
user_invalid_ip=$(awk '$1=="'$curtime_month'" && $2=='"$curtime_day"' && $3>="'$one_minus_ago'" && $3<="'$curtime_time'" { if($9=="invalid") {print $(NF-3)}}' $logfile | awk '{IP[$1]++} END{ for(i in IP){ if(IP[i]>=3) {print i} } }')
for j in $user_invalid_ip
do
if ! grep -q "$j" $blockfile
then
echo "$curtime_seconds $j" >> $blockfile
fi
done
sleep 60
done
}
# 记录了违规IP后将违规IP加入到防火墙策略中拒绝,并设置生效时间
firewall_answer(){
#开启防火墙
systemctl start firewalld &> /dev/null
if [ -f $blockfile ]
then
for z in $(awk '{print $2}' $blockfile)
do
firewall-cmd --add-rich-rule="rule family=ipv4 source address=$z reject" --timeout=1200
echo "$z"
done
fi
}
# 每隔20分钟检查一次黑名单,清理大于20分钟的黑名单IP
clear_blockip(){
while :
do
sleep 1200
# 将当前时间转换为距离1970-01-01 00:00:00的秒数,方便后期计算
local curtime_seconds=$(LANG=C date +"%s")
# awk 调用外部shell变量的另一种方式是使用-v选项
# 当前时间去黑名单中的时间标记
# 大于等于1200秒(20分钟)则将其从黑名单中删除
tmp=$(awk -v now=$curtime_seconds '(now-$1)>=1200 {print $2}' $blockfile)
for i in $tmp
do
sed -i "/$i/d"
done
done
}
#> $blockfile
one_minute &
noe_pid="$!"
firewall_answer
clear_blockip