利用SHELL脚本实现文件完整性检测程序,
利用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、可以根据具体情况,自己扩展程序实现对变更文件做进一步检测的功能。
评论暂时关闭