利用SHELL脚本实现文件完整性检测程序,


一.、开发背景

因时势所逼,需要对服务器的文件系统实行监控。虽然linux下有不少入侵检测和防窜改系统,但都比较麻烦,用起来也不是很称手。自己琢磨着也不需要什么多复杂的功能,写个脚本应该就可以满足基本需求。于是整理了一下思路,编写了一个简单的文件完整性检测程序。

 

二、实现功能

1、能监测多个不同目录中文件的增删改变化。

2、允许监测的特定类型文件,并可根据具体情况设定是否需要生成摘要等。

3、对于变动的文件,新增或修改的可以生成摘要。删除的文件要有日志记录。

4、当监测到文件发生变动时,能生成简报以邮件方式提醒管理员。

 

三、设计思路

建立监测项配置文件,将需检测的目录路径以及相关设置记录在文件中。利用crontab定时执行脚本,第一次运行时按配置文件加载检测项,通过find命令获取文件列表,利用md5sum为符合条件的文件生成hash值,并保存到文件中做为以后校验的基准数据。脚本在以后的运行中,首先会将待测目录中符合条件的文件列出,并于基准数据中的文件进行比对,以发现新增文件。再利用md5sum检测目录中文件是否有被修改或删除。对修改或新增文件生成摘要,将检测结果生成简报存入日志文件,并将简报发送到指定邮箱。

 

四、监测项配置文件说明

配置文件每一行为一个检测目录项,配置项之间以逗号分隔,配置定义如下:

<待查目录>,<过滤条件>,<检测类型>

1、待查目录

需检测的目录完整路径,例如:/var/test

2、过滤条件

写在此处的内容会做为find命令中-name的参数,以过滤特定文件。例如:*.txt

3、检测类型

取值为0或1。

0:发现变更仅记录文件名不做摘要;(一般针对二进制文件检测)

1:发现变更则做摘要

 

五、程序运行方式

脚本可以通过crontab定时执行。同时为了方便日常工作,脚本支持参数运行。具体参数及用途如下:

-c 清空日志
-i 生成扫描项的验证码文件,做为原始校验范本。(一般用于文件正常变更后,管理员手动重新生成基准检验文件)
-s 生成所有扫描项的摘要文件。(一般用于初次生成HASH值之前,生成所有符合文件摘要,供管理员做检测)

 

六、程序实现

#!/bin/bash
#
#Name:CheckFile
#Ver:1.0
#Author:lykyl
#
#程序说明:
#文件完备性检测程序
#
#实现功能:
#监测指定目录的指定类型文件变动情况,
#对于删除的文件做日志记录
#对于添加或修改的文件做内容取样

BASEDIR=`dirname $0`    #程序所在目录
CONFFILE="$BASEDIR/checkfile.cfg"     #任务配置文件
LOGFILE="$BASEDIR/event.log"     #运行记录文件
CHECKDIR="$BASEDIR/chk/"     #原始校验码存放文件保存目录
SUMMARYDIR="$BASEDIR/summary/"     #快照文件保存目录
REMINDFLGFILE="$BASEDIR/remind.flg"     #邮件发送标志文件
REMINDINTERVAL=7200     #邮件提醒最短间隔时间

declare -a aCheckList
declare -a aFileList
IFS=$'\n'
FindCmd="/usr/bin/find"
CheckCmd="/usr/bin/md5sum"

#获取任务配置信息
function GetConf()
{
  if ! [ -f $CONFFILE ]; then
    echo "ERROR:can not find checkfile">>$LOGFILE
    return
  fi
  local ConfigList=`grep -v '^\s*#' $CONFFILE|sed '/^\s*$/d'|uniq`
  aCheckList=()
  for i in $ConfigList
  do
    local Search=`echo $i|awk -F, '{print $1}'`     #Search为查询参数
    local Arg=`echo $i|awk -F, '{print $2}'`      #查询附加参数,会添加到find的-name参数值中
    local Type=`echo $i|awk -F, '{print $3}'`     #Type为操作类型,0:发现变更仅记录文件名不做摘要; 1:发现变更则做摘要
    if (( ${#Search} < 2 )); then
      continue
    fi
    if (( ${#Type} < 1 )); then
      Type="1"
    fi
    #利用MD5为每个任务生成唯一标识,也做为校验码存放文件的文件名
    local tn=`echo "$Search"|$CheckCmd|awk '{print $1}'`
    
    aCheckList=(${aCheckList[@]} "$Search,$Arg,$Type,$tn")
  done
}

#校验符合条件文件内容
function CheckFile()
{
  if (( ${#1} < 2 )); then
    echo "ERROR:can not analyse check item $1">>$LOGFILE
    return
  fi
  local Search=`echo $1|awk -F, '{print $1}'`
  local Arg=`echo $1|awk -F, '{print $2}'`
  local Type=`echo $1|awk -F, '{print $3}'`
  local Fn=$CHECKDIR`echo $1|awk -F, '{print $4}'`".chk"
  local NeedCreate=0
  local NewFile=()
  local aFCount=()
  if [ -f $Fn ]; then
    if (( `wc -l $Fn|awk '{print $1}'` < 1 )); then
      NeedCreate=1
    fi
  else
    NeedCreate=1
  fi
  NFlist=$(mktemp)
  $FindCmd $Search -name "$Arg" ! -type d |sort >$NFlist 2>>$LOGFILE
  if (( $NeedCreate > 0 )); then
    `cat $NFlist |xargs $CheckCmd >$Fn 2>>$LOGFILE`
    chmod 600 $Fn
    return
  else
    #判断是否新增了文件
    OFlist=$(mktemp)
    `cat $Fn|awk '{print $2}'|sort >$OFlist`
    NewFile=`comm -23 $NFlist $OFlist`
    for i in $NewFile
    do
      if (( ${#i} < 2 )); then
        continue
      fi
      if [ -f $i ]; then
        aFCount=(${aFCount[@]} $i"_Increased!")
        echo $i" Increased!" >>$LOGFILE
        if [ $Type != "0" ]; then
          GetSummary $i
        fi
      fi
    done
    rm -f $OFlist
    rm -f $NFlist
  fi
  local FailedList=`$CheckCmd -c $Fn 2>/dev/null|grep FAILED|sed 's/: FAILED.*$//g'|sed '/^\s*$/d'`
  for i in $FailedList
  do
    if (( ${#i} < 2 )); then
      continue
    fi
    if [ -f $i ]; then
      #对修改过的文件进行记录
      aFCount=(${aFCount[@]} $i"_has_been_modified!")
      echo $i" has been modified!" >>$LOGFILE
      if [ $Type != "0" ]; then
        GetSummary $i
      fi
    else
      #对删除的文件进行记录
      aFCount=(${aFCount[@]} $i"_can_not_found!")
      echo $i" can not found!" >>$LOGFILE
    fi
  done
  #在合理的间隔时间内发送检测异常报告邮件
  #此处可以进一步对检测结果的HASH值与上次结果的HASH值做对比,发现不同再报告.可以根据自身需求自行修改
  if (( ${#aFCount[@]} > 0 )); then
    local NowTime=`date +%s`
    local SendTime=0
    if [ -f $REMINDFLGFILE ]; then
      SendTime=`cat $REMINDFLGFILE`
    else
      SendTime=0
    fi
    local tDiff=$[ $NowTime - $SendTime ]
    if (( $tDiff > $REMINDINTERVAL )); then
      echo ${aFCount[@]}|sed "s/\s/\n/g" | mail -s "Check FAILED!" test@test.test    #此处要换成自己的邮箱地址
      echo $NowTime >$REMINDFLGFILE
    fi
  else
      echo $Search" Check OK!" >>$LOGFILE
  fi
}

#对指定文件做摘要
function GetSummary()
{
  if (( ${#1} < 1 )); then
    return
  fi
  local SummFile=$SUMMARYDIR`date +%Y%m%d`".summ"
  if (( ${#2} > 3 )); then
     SummFile=$2
  fi
  echo "">>$SummFile
  echo `date`>>$SummFile
  echo $1>>$SummFile
  echo "--------------------------------------------------" >>$SummFile
  if  [ -f $1 ]; then
    #例程只是简单取文件前5行做摘要,可以根据实际情况自行修改摘要方法
    head -n 5 $1 >>$SummFile
  else
    echo "can not found file" >>$SummFile
  fi
  echo "==================================================" >>$SummFile
  chmod 600 $SummFile
}


#main
if ! [ -d $CHECKDIR ]; then
  mkdir $CHECKDIR
  chmod 700 $CHECKDIR
fi
if ! [ -d $SUMMARYDIR ]; then
  mkdir $SUMMARYDIR
  chmod 700 $SUMMARYDIR
fi
GetConf
OutputFile=""
while getopts :ichsm opt
do
  case $opt in
  i)
    for i in ${aCheckList[@]}
    do
      Search=`echo $i|awk -F, '{print $1}'`
      Arg=`echo $i|awk -F, '{print $2}'`
      Fn=$CHECKDIR`echo $i|awk -F, '{print $4}'`".chk"
      $FindCmd $Search -name "$Arg" -exec $CheckCmd {} \; >$Fn 2>>$LOGFILE
      chmod 600 $Fn
    done
    aCheckList=()
    ;;
  c)
    `cat /dev/null`>$LOGFILE
    aCheckList=()
    ;;
  s)
    for i in ${aCheckList[@]}
    do
      Search=`echo $i|awk -F, '{print $1}'`
      Arg=`echo $i|awk -F, '{print $2}'`
      t=`$FindCmd $Search -name "$Arg"`
      for j in ${t[@]}
      do
        GetSummary $j
      done
    done
    aCheckList=()
    ;;
  h)
    echo "-h 获取帮助"
    echo "-c 清空日志"
    echo "-i 生成扫描项的验证码文件,做为原始校验范本"
    echo "-s 生成所有扫描项的摘要文件"
    aCheckList=()
    ;;
  *)
  ;;
esac
done  
if (( ${#aCheckList[@]} > 0 )); then
  echo "checkfile start at "`date`>>$LOGFILE
  for i in ${aCheckList[@]}
  do
    #echo $i
    CheckFile $i
  done
  echo "checkfile finished at "`date`>>$LOGFILE
fi

六、题外话

1、本人才疏,原打算将所有find的过滤条件做为一个字串传给find执行,比如“ -name "*.txt"  -type f ”。但实际运行时总是报错,不得已只能将-name的具体参数传入,请参看具体代码:$FindCmd $Search -name "$Arg"。如果有更好解决办法,请一定告之,谢谢!

2、生成摘要的方式可以根据实际情况自行修改。

3、可以根据具体情况,自己扩展程序实现对变更文件做进一步检测的功能。

 

完整代码点此下载




相关内容

    暂无相关文章