OpenWrt自动封禁爆破IP

本文最后更新于1 年前,文中所描述的信息可能已发生改变。

需求

  • OpenWrt没有自动封禁IP的功能,在GitHub找到遍历Log封禁IP脚本。

个性化需求

  • 添加封禁IP脚本DenyPwdHack.sh(无邮件通知,且遍历logread系统日志。导致IP存在就会封禁无法过期删除。)
  • 配置封禁IP脚本DenyPwdHack.sh的遍历logread系统日志最后100行。
  • 添加封禁IP脚本DenyPwdHack.sh的封禁IP通知发送至邮箱。

安装邮件通知功能

  • OpenWrt安装msmtp、mutt。
  • msmtp添加发送邮件功能。
  • mutt邮件客户端,使邮件正常发送,对端不误报垃圾邮件。

配置msmtp

  • vim /etc/msmtprc
php
# Example for a system wide configuration file

# A system wide configuration file is optional.
# If it exists, it usually defines a default account.
# This allows msmtp to be used like /usr/sbin/sendmail.
account default

# The SMTP smarthost
host smtp.163.com // 163邮箱smtp地址

# Use TLS on port 465
port 465
tls on
tls_starttls off

# Construct envelope-from addresses of the form "[email protected]"
from xxxxxxxx@163.com // 邮件的发件方地址
auth login // 自动登录
user xxxxxxxx@163.com // 邮箱用户名,有些是不要@邮箱地址
password XXXXXXXXXXXXX // 邮箱开启SMTP给的授权码


# Syslog logging with facility LOG_MAIL instead of the default LOG_USER
logfile /etc/msmtp.log

配置mutt

  • vim /etc/Muttrc
php
set charset="utf-8"

set rfc2047_parameters=yes

set envelope_from=yes

set sendmail="/usr/bin/msmtp" //发邮件的程序

set use_from=yes

set from=xxxxxxxx@163.com //发件方

set realname="OpenWrt" //发件方名称

set editor="vim"

项目地址

初始源码

php
#!/bin/ash


## OpenWRT 版本判断
Vfile=/etc/banner
OWTV=`awk 'BEGIN{IGNORECASE=1}/openwrt/ {split($2,v,"."); print v[1]}' $Vfile`
[[ $OWTV -lt 18 ]] && echo "OpenWRT version must be >= 18" && exit 1


## 黑名单所在iptables链表
ChainName=DenyPwdHack


## 日志路径
LOG_DEST=/tmp/DenyPwdHack.log


## 检测到攻击时需要针对攻击IP封禁的端口,可以将ssh/luci/ftp等端口加上
Deny_Port="22,443"
INPUT_RULE="INPUT -p tcp -m multiport --dports $Deny_Port -j $ChainName"


## 日志关键字,每个关键字可以用"|"号隔开,支持grep的正则表达式

## 注: SSH 攻击可以大量出现四种关键字:Invalid user/Failed password for/Received disconnect from/Disconnected from authenticating

##     Luci 攻击可以出现"luci: failed login on / for root from xx.xx.xx.xx"
LOG_KEY_WORD="auth\.info\s+sshd.*Failed password for|luci:\s+failed\s+login|auth\.info.*sshd.*Connection closed by.*port.*preauth"


## 白名单IP可以用"|"号隔开,支持grep的正则表达式
exclude_ip="192.168.|127.0.0.1"


## 失败次数
Failed_times=5


## 黑名单过期时间,单位小时,3个月2160小时
BlackList_exp=2160


## 日志时间
LOG_DT=`date "+%Y-%m-%d %H:%M:%S"`


## 判断链是否存在
iptables -n --list $ChainName > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
  iptables -N $ChainName
  echo "[$LOG_DT] iptables -N $ChainName" >> $LOG_DEST
fi


## 判断INPUT跳到链的规则是否存在
iptables -C $INPUT_RULE > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
  iptables -I $INPUT_RULE
  echo "[$LOG_DT] iptables -I $INPUT_RULE" >> $LOG_DEST
fi

DenyIPLIst=`logread \
  | awk '/'"$LOG_KEY_WORD"'/ {for(i=1;i<=NF;i++) \
  if($i~/^(([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/) \
  print $i}' \
  | grep -v "${exclude_ip}" \
  | sort | uniq -c \
  | awk '{if($1>'"$Failed_times"') print $2}'`
IPList_sum=`echo "${DenyIPLIst}" | wc -l`
if [[ $IPList_sum -ne 0 ]];then
  for i in ${DenyIPLIst}
    do
    iptables -vnL $ChainName | grep -q $i
    [[ $? -ne 0 ]] && iptables -A $ChainName -s $i -m comment --comment "Added at $LOG_DT by DenyPwdHack" -j DROP \
     && echo "[$LOG_DT] iptables -A $ChainName -s $i -j DROP" >> $LOG_DEST
    done
fi


## 黑名单过期删除
ChainList=`iptables --line-numbers -nL $ChainName |\
  awk '/Added at/ {for(i=1;i<=NF;i++) if($i~/[0-9]{4}(-[0-9]{2}){2}/) print $1","$i" "$(i+1)}' |\
  sort -rn`


## 链表必须从后端删除,如果从前端删除,后端的实际rulenum会变
ChainList_num=`echo "${ChainList}" | grep -v "^$" | wc -l`
if [[ ${#ChainList} -ne 0 ]] && [[ $ChainList_num -gt 0 ]] ; then
for tl in `seq 1 $ChainList_num`
do
  Dtime=`echo "${ChainList}" | sed -n ''"$tl"'p' | awk -F, '{print $2}'`
  Stime=`date -d "$Dtime" +%s`
  Ntime=`date +%s`
  if [[ $(($Ntime - $Stime)) -ge $(($BlackList_exp * 3600)) ]] ; then
    RuleNum=`echo "${ChainList}" | sed -n ''"$tl"'p' | awk -F, '{print $1}'`
    iptables -D $ChainName $RuleNum
    if [[ $? -eq 0 ]] ; then
      echo "[$LOG_DT] iptables -D $ChainName $RuleNum" >> $LOG_DEST
    else
      echo "[$LOG_DT] execute delete failed: iptables -D $ChainName $RuleNum" >> $LOG_DEST
    fi
  fi
done
fi

实际使用源码(邮件通知BUG,后续修复)

添加修改自定义功能

读取logread系统日志最后100行

php
#!/bin/bash

# 获取文件最后100行数据
tail -n -100 /tmp/system.log

封禁IP通知发送至邮箱

php

## 通知邮件发送
if [[ $? -eq 0 ]] ; then //判断上一步命令(封禁IP或初始创建iptables链)是否成功,成功则发送邮件
  # 获取文件包含“iptables -A DenyPwdHack -s”且读取最后10行
  DenyIpList=`cat /root/DenyPwdHack.log | grep "iptables -A DenyPwdHack -s" | tail -n -10`

  # 发送至个人邮箱
  echo "$DenyIpList" | mutt -s "IP封禁提醒" xxxxxxxx@qq.com
fi

DenyPwdHack.sh(集成自定义功能版)

php
#!/bin/ash


## OpenWRT 版本判断

## Vfile=/etc/banner

## OWTV=`awk 'BEGIN{IGNORECASE=1}/openwrt/ {split($2,v,"."); print v[1]}' $Vfile`

## [[ $OWTV -lt 18 ]] && echo "OpenWRT version must be >= 18" && exit 1


## 黑名单所在iptables链表
ChainName=DenyPwdHack


## 日志路径
LOG_DEST=/root/DenyPwdHack.log


## 检测到攻击时需要针对攻击IP封禁的端口,可以将ssh/luci/ftp等端口加上
Deny_Port="36875"
INPUT_RULE="INPUT -p tcp -m multiport --dports $Deny_Port -j $ChainName"


## 日志关键字,每个关键字可以用"|"号隔开,支持grep的正则表达式

## 注: SSH 攻击可以大量出现四种关键字:Invalid user/Failed password for/Received disconnect from/Disconnected from authenticating

##     Luci 攻击可以出现"luci: failed login on / for root from xx.xx.xx.xx"
LOG_KEY_WORD="auth\.info\s+sshd.*Failed password for|luci:\s+failed\s+login|auth\.info.*sshd.*Connection closed by.*port.*preauth|Received disconnect from"


## 白名单IP可以用"|"号隔开,支持grep的正则表达式
exclude_ip="192.168.\|127.0.0.1\|112.45.125."


## 失败次数
Failed_times=10


## 黑名单过期时间,单位分钟
BlackList_exp=20


## 日志时间
LOG_DT=`date "+%Y-%m-%d %H:%M:%S"`


## 判断链是否存在
iptables -n --list $ChainName > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
  iptables -N $ChainName
  echo "[$LOG_DT] iptables -N $ChainName" >> $LOG_DEST
fi


## 判断INPUT跳到链的规则是否存在
iptables -C $INPUT_RULE > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
  iptables -I $INPUT_RULE
  echo "[$LOG_DT] iptables -I $INPUT_RULE" >> $LOG_DEST
fi

DenyIPLIst=`tail -n -100 /tmp/system.log \
  | awk '/'"$LOG_KEY_WORD"'/ {for(i=1;i<=NF;i++) \
  if($i~/^(([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/) \
  print $i}' \
  | grep -v "${exclude_ip}" \
  | sort | uniq -c \
  | awk '{if($1>'"$Failed_times"') print $2}'`
IPList_sum=`echo "${DenyIPLIst}" | wc -l`
if [[ $IPList_sum -ne 0 ]];then
  for i in ${DenyIPLIst}
    do
    iptables -vnL $ChainName | grep -q $i
    [[ $? -ne 0 ]] && iptables -A $ChainName -s $i -m comment --comment "Added at $LOG_DT by DenyPwdHack" -j DROP \
     && echo "[$LOG_DT] iptables -A $ChainName -s $i -j DROP" >> $LOG_DEST
    done
fi


## 通知邮件发送
if [[ $? -eq 0 ]] ; then
  # 获取文件包含“iptables -A DenyPwdHack -s”
  DenyIpList=`cat /root/DenyPwdHack.log | grep "iptables -A DenyPwdHack -s" | tail -n -10`

  # 发送至个人邮箱
  echo "$DenyIpList" | mutt -s "IP封禁提醒" xxxxxxxx@qq.com
fi


## 黑名单过期删除
ChainList=`iptables --line-numbers -nL $ChainName |\
  awk '/Added at/ {for(i=1;i<=NF;i++) if($i~/[0-9]{4}(-[0-9]{2}){2}/) print $1","$i" "$(i+1)}' |\
  sort -rn`


## 链表必须从后端删除,如果从前端删除,后端的实际rulenum会变
ChainList_num=`echo "${ChainList}" | grep -v "^$" | wc -l`
if [[ ${#ChainList} -ne 0 ]] && [[ $ChainList_num -gt 0 ]] ; then
for tl in `seq 1 $ChainList_num`
do
  Dtime=`echo "${ChainList}" | sed -n ''"$tl"'p' | awk -F, '{print $2}'`
  Stime=`date -d "$Dtime" +%s`
  Ntime=`date +%s`
  if [[ $(($Ntime - $Stime)) -ge $(($BlackList_exp * 60)) ]] ; then
    RuleNum=`echo "${ChainList}" | sed -n ''"$tl"'p' | awk -F, '{print $1}'`
    iptables -D $ChainName $RuleNum
    if [[ $? -eq 0 ]] ; then
      echo "[$LOG_DT] iptables -D $ChainName $RuleNum" >> $LOG_DEST
    else
      echo "[$LOG_DT] execute delete failed: iptables -D $ChainName $RuleNum" >> $LOG_DEST
    fi
  fi
done
fi

添加计划任务

  • */10 * * * * /root/DenyPwdHack.sh

查询

封堵日志

  • cat DenyPwdHack.log

查询iptables条目

  • iptables -L DenyPwdHack

删除链

  • iptables -D DenyPwdHack 1

邮件通知BUG修复

  • 运行一段发现,在无封禁IP时,任然触发邮件通知。

改进

  • 改变增加iptables DenyPwdHack条目的日志位置。
  • shell判断文件MD5是否有变化。

检测MD5脚本

php
#!/bin/bash

## 检测DenyPwdHackIP.log是否有新增IP。发送通知邮件
#读取封禁IP日志
DenyIpList=$(cat /root/DenyPwdHackIP.log)
cd /root
#判断DenyPwdHackIP.log文件有无改变,如果改变了需要发送通知邮件
md5_old=`cat DenyPwdHackIP.md5`
md5_new=`md5sum DenyPwdHackIP.log |awk '{print $1}'`
if [[ $md5_old != $md5_new ]];then
  echo "$md5_new" > DenyPwdHackIP.md5
  echo "$DenyIpList" | mutt -s "IP封禁提醒" xxxxxxxx@qq.com
fi

DenyPwdHack.sh(改进后命令)

php
#!/bin/bash


## OpenWRT 版本判断

## Vfile=/etc/banner

## OWTV=`awk 'BEGIN{IGNORECASE=1}/openwrt/ {split($2,v,"."); print v[1]}' $Vfile`

## [[ $OWTV -lt 18 ]] && echo "OpenWRT version must be >= 18" && exit 1


## 黑名单所在iptables链表
ChainName=DenyPwdHack


## 日志路径
LOG_Deny_IP=/root/DenyPwdHackIP.log
LOG_DEST=/root/DenyPwdHack.log


## 检测到攻击时需要针对攻击IP封禁的端口,可以将ssh/luci/ftp等端口加上
Deny_Port="36875"
INPUT_RULE="INPUT -p tcp -m multiport --dports $Deny_Port -j $ChainName"


## 日志关键字,每个关键字可以用"|"号隔开,支持grep的正则表达式

## 注: SSH 攻击可以大量出现四种关键字:Invalid user/Failed password for/Received disconnect from/Disconnected from authenticating

##     Luci 攻击可以出现"luci: failed login on / for root from xx.xx.xx.xx"
LOG_KEY_WORD="auth\.info\s+sshd.*Failed password for|luci:\s+failed\s+login|auth\.info.*sshd.*Connection closed by.*port.*preauth|Received disconnect from"


## 白名单IP可以用"|"号隔开,支持grep的正则表达式
exclude_ip="192.168.\|127.0.0.1\|112.45.125."


## 失败次数
Failed_times=10


## 黑名单过期时间,单位分钟
BlackList_exp=20


## 日志时间
LOG_DT=`date "+%Y-%m-%d %H:%M:%S"`


## 判断链是否存在
iptables -n --list $ChainName > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
  iptables -N $ChainName
  echo "[$LOG_DT] iptables -N $ChainName" >> $LOG_DEST
fi


## 判断INPUT跳到链的规则是否存在
iptables -C $INPUT_RULE > /dev/null 2>&1
if [[ $? -ne 0 ]] ; then
  iptables -I $INPUT_RULE
  echo "[$LOG_DT] iptables -I $INPUT_RULE" >> $LOG_DEST
fi

DenyIPLIst=`tail -n -100 /tmp/system.log \
  | awk '/'"$LOG_KEY_WORD"'/ {for(i=1;i<=NF;i++) \
  if($i~/^(([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/) \
  print $i}' \
  | grep -v "${exclude_ip}" \
  | sort | uniq -c \
  | awk '{if($1>'"$Failed_times"') print $2}'`
IPList_sum=`echo "${DenyIPLIst}" | wc -l`
if [[ $IPList_sum -ne 0 ]];then
  for i in ${DenyIPLIst}
    do
    iptables -vnL $ChainName | grep -q $i
    [[ $? -ne 0 ]] && iptables -A $ChainName -s $i -m comment --comment "Added at $LOG_DT by DenyPwdHack" -j DROP \
     && echo "[$LOG_DT] iptables -A $ChainName -s $i -j DROP" >> $LOG_Deny_IP
    done
fi


## 黑名单过期删除
ChainList=`iptables --line-numbers -nL $ChainName |\
  awk '/Added at/ {for(i=1;i<=NF;i++) if($i~/[0-9]{4}(-[0-9]{2}){2}/) print $1","$i" "$(i+1)}' |\
  sort -rn`


## 链表必须从后端删除,如果从前端删除,后端的实际rulenum会变
ChainList_num=`echo "${ChainList}" | grep -v "^$" | wc -l`
if [[ ${#ChainList} -ne 0 ]] && [[ $ChainList_num -gt 0 ]] ; then
for tl in `seq 1 $ChainList_num`
do
  Dtime=`echo "${ChainList}" | sed -n ''"$tl"'p' | awk -F, '{print $2}'`
  Stime=`date -d "$Dtime" +%s`
  Ntime=`date +%s`
  if [[ $(($Ntime - $Stime)) -ge $(($BlackList_exp * 60)) ]] ; then
    RuleNum=`echo "${ChainList}" | sed -n ''"$tl"'p' | awk -F, '{print $1}'`
    iptables -D $ChainName $RuleNum
    if [[ $? -eq 0 ]] ; then
      echo "[$LOG_DT] iptables -D $ChainName $RuleNum" >> $LOG_DEST
    else
      echo "[$LOG_DT] execute delete failed: iptables -D $ChainName $RuleNum" >> $LOG_DEST
    fi
  fi
done
fi


## 检测DenyPwdHackIP.log是否有新增IP。发送通知邮件
#读取封禁IP日志
DenyIpList=$(cat /root/DenyPwdHackIP.log | tail -n -10)
cd /root
#判断DenyPwdHackIP.log文件有无改变,如果改变了需要发送通知邮件
md5_old=`cat DenyPwdHackIP.md5`
md5_new=`md5sum DenyPwdHackIP.log |awk '{print $1}'`
if [[ $md5_old != $md5_new ]];then
  echo "$md5_new" > DenyPwdHackIP.md5
  echo "$DenyIpList" | mutt -s "IP封禁提醒" xxxxxxxx@qq.com
fi

#openwrt #linux

Linux系统批量安装
成都情歌