- A+
所属分类:linux技术
用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把22 80端口映射到公网,发现经常被暴力破解,自己写了个临时封禁ip功能的脚本,实现5分钟内同一个ip登录密码错误10次就封禁这个ip 5分钟,并且进行邮件通知
使用步骤
openwrt为19.07.03版本,其他版本没有测试过
-
安装bash msmtp
- opkg update;opkg install bash msmtp
-
添加脚本/root/port-security.sh,内容参考下面附件,主要修改通知的邮箱
-
脚本增加执行权限
- chmod a+x /root/port-security.sh
-
添加定时任务 crontab -e,10分钟执行一次脚本,如果脚本已经在跑会自动退出当前进程
- */10 * * * * /root/port-security.sh &
-
修改/etc/msmtprc 文件,内容参考下面附件,封禁ip后发送邮件通知
解释
- 通过logread查看日志确定被登录的错误次数并且提取要封禁的ip,ssh登录密码错误通过"Bad password attempt for"匹配,web登录密码错误通过"luci: failed login on",具体可以根据实际修改,logread日志过多有用信息有可能被冲掉的可能,可以加大日志缓存或者修改日志等级减少日志
- ip封禁是通过ipset配合iptables处理,当要封禁的ip很多的时候,比直接多条iptables性能高,封禁时间由ipset来处理,不需要另外处理封禁时间
- 用了bash字典,脚本需要用/bin/bash,不能用/bin/sh,bash低版不支持字典
- 内网192.168.0.0/16加入了白名单,如果要用内网来测试,需要注释iptables -I INPUT -s 192.168.0.0/16 -j ACCEPT
/root/port-security.sh
#! /bin/bash #允许尝试错误的次数 tryErrNum=10 #允许尝试错误在多长时间范围,单位秒,与tryErrNum一起生效 tryTimeOut=300 #需要被封禁的多个ip字符串,逗号隔开,这里为空 dropIpList="" #logread里面标识字符串,Bad password attempt for 是ssh root登录密码错误log,luci: failed login on 是登录web错误log errStr=("Bad password attempt for" "luci: failed login on") #查询logread 日志的时间间隔,单位秒 timesleep=30 #从根据字符串从日志里面提取要封禁的ip,追加到 dropIpList 变量 getdropip() { #对过滤的日志倒叙 lines=$(logread | grep $1 | sed -n '1!G;h;$p') linesNum=$(echo -n "$lines" | wc -l) #定义字典变量 declare -A dict # if [ "$linesNum" -gt "$tryErrNum" ];then while read line; do ip=$(echo -n "$line" | awk '{print $NF}' | awk -F":" '{print $1}') time=$(echo -n "$line" | awk '{print $4}' | awk -F":" '{sum += $1*3600 + $2*60 + $3};END {print sum}') ok=${dict[$ip]} if [ -z $ok ];then dict[$ip]=1 time=$(date | awk '{print $4}' | awk -F":" '{sum += $1*3600 + $2*60+ $3};END {print sum}') dict[${ip}_endtime]=$time else if [ ${dict[$ip]} -lt "$tryErrNum" ];then dict[$ip]=$(expr ${dict[$ip]} + 1) dict[${ip}_starttime]=$time fi fi done < <(echo -n "$lines") for key in ${!dict[@]}; do if ! [[ "$key" =~ "starttime" ]] && ! [[ "$key" =~ "endtime" ]];then if [ ${dict[$key]} -eq "$tryErrNum" ];then timeDiff=$(expr ${dict[${key}_endtime]} - ${dict[${key}_starttime]}) if [ "$timeDiff" -gt 0 ] && [ "$timeDiff" -lt "$tryTimeOut" ];then #追加需要封禁的ip到变量 dropIpList=$dropIpList,$key fi fi fi done fi } #errStr 字符串遍历,分别从log里面匹配,提取要封禁ip startGetDropIp(){ i=0 while : do errstr=${errStr[$i]} if [ -z "$errstr" ];then break fi i=$(expr $i + 1) getdropip $errstr done } #初始化ipset iptable intEnv(){ ipset list BlockIpList if ! [ "$?" == 0 ]; then ipset create BlockIpList hash:net timeout 300 iptables -I INPUT -m set --match-set BlockIpList src -p tcp -m tcp --dport 22 -j DROP iptables -I INPUT -m set --match-set BlockIpList src -p tcp -m tcp --dport 80 -j DROP iptables -I INPUT -s 192.168.0.0/16 -j ACCEPT fi } #把要封禁的ip添加到ipset BlockIpList集合 ipsetAddIp(){ dropIpListNew="" for ip in $(echo -n "$dropIpList" | sed 's/,/ /g') do ipset add BlockIpList $ip if [ "$?" -eq 0 ];then dropIpListNew=$dropIpListNew,$ip fi done } sendmail(){ if [ -n "$dropIpListNew" ];then #邮箱填自己常用邮箱,方便及时收到通知 echo -e "subject:MyOpenwrt IP封禁提醒nn$dropIpListNew" | msmtp 1234567@qq.com fi } #检查当前进程是否存在 PocessNum=$(ps | grep $0 | grep -v grep | wc -l) if [ "$PocessNum" -gt 2 ];then #已经存在进程,直接退出当前进程 kill -9 $$ else intEnv while : do sleep $timesleep dropIpList="" startGetDropIp ipsetAddIp sendmail done fi
/etc/msmtprc 这里用的163邮箱,其他邮箱这里不列举
defaults account 163 host smtp.163.com port 25 from <这里填你发邮件的邮箱> auth plain tls on user <这里填你发邮件的邮箱> password <这里填你发邮件的邮箱的密码> account default : 163