运维脚本: 实时监测登录
引言
- 背景介绍:在服务器的运维管理中,及时监控系统的登录日志对保障系统的安全至关重要。通过实时监控登录日志,运维人员可以发现潜在的异常登录行为,防止系统被非法访问。
- 问题引入:如何实现实时监控登录日志,并及时响应潜在的安全风险?
实时监控登录日志的意义
- 安全性
:通过监控登录日志,可以迅速发现恶意登录、暴力破解等异常行为。 - 合规性
:确保满足各种合规要求,记录所有用户的登录行为。
解决方案概述
- 监控目标
:关注登录日志中的关键信息,例如登录时间、IP 地址、用户名、登录方式等。 - 技术选型
:通过编写 Bash 脚本,结合
inotify
、
awk
、
grep
等工具,来实现对日志文件的实时监控与分析。
脚本实现原理
- 实时监控
:利用
inotify
命令动态监控日志文件的变动,并结合
sed
命令实时提取和输出新增的登录日志。 - 日志筛选
:通过
grep
等工具过滤出登录失败、异常登录等相关信息。 - 报警机制
:脚本可以配置成在监控到异常行为时,自动发送通知邮件
脚本示例
1 #!/bin/bash2 # 作者: 阿杰3 # 用途: 实时检测登录日志,统计异常登录4 # 脚本名称: watch_secure.sh 5 # 用法: bash watch_seacure.sh 6 7 # 日志记录8 log_err() {9 printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[31mERROR: \033[0m$@\n" 10 }11 12 log_info() {13 printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[32mINFO: \033[0m$@\n" 14 }15 16 log_warning() {17 printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[33mWARNING: \033[0m$@\n" 18 }19 20 # 初始化Map21 declare -A secureMap22 23 init() {24 # 行数记录文件25 line_file_name="conf/line_file.txt" 26 # inode存储文件27 inode_file="conf/inode.txt" 28 # 认证失败文件记录29 ssh_auth_failed_file="conf/ssh_auth_failed.csv" 30 31 # 文件列表32 file_array=("$line_file_name" "$inode_file" "$ssh_auth_failed_file")33 # inode 文件状态34 inode_file_status=0 35 # 控制是否进行写入 0为可写,1为不可写36 write_status=1 37 38 oneSecureKey="" 39 40 {41 if [ ! -d "conf" ];then 42 mkdirconf43 fi 44 # 检查文件是否存在45 for file in ${file_array[@]};do 46 check_file_exists $file 47 done 48 line=$(cat$line_file_name)49 if [ -z "$line" ];then 50 line=0 51 fi 52 # 认证失败文件第一次创建53 if [ $(wc -l < $ssh_auth_failed_file) -eq 0 ];then 54 # 时间以月天为单位(None为空账号或不存在账号)55 echo "登录认证失败时间,源IP地址,登录账号,连接认证失败次数" >$ssh_auth_failed_file56 fi 57 58 }59 60 file_name="/var/log/secure" 61 if [ -z "$(rpm -qa | grep 'inotify-tools')" ];then 62 yum install -y inotify-tools > /dev/null 2>&1 63 if [ $? -ne 0 ];then 64 log_err "[init] inotify-tools 安装失败!" 65 fi 66 fi 67 68 69 }70 # 检查文件是否存在,不存在则创建71 check_file_exists() {72 local file_name=$1 73 if [ ! -f "$file_name" ];then 74 touch$file_name75 if [ $? -ne 0 ];then 76 log_err "[check_file_exists] file: $file_name 文件创建失败!" 77 fi 78 fi 79 }80 81 82 83 # 监听文件事件84 watch_file() {85 inotifywait -mrq --format '%e' --event create,delete,modify $file_name | while read event ;do 86 case "$event" in 87 MODIFY)88 start_read_file89 ;;90 # 文件被删除或重新创建91 CREATE|DELETE)92 # 重置文件行数93 line=0 94 >$line_file_name95 check96 ;;97 *)98 log_warning "[watch_file] watch file event: $event" 99 ;;100 esac 101 done 102 }103 104 # 只读一行105 read_line_file() {106 ((line++))107 echo $line >$line_file_name108 # 不是指定数据退出109 if [ $(sed -n "$line p" $file_name | grep 'pam_unix(sshd:auth): authentication failure;' | wc -l ) -ne 1 ];then 110 return111 fi 112 # 控制是否进行写入113 write_status=0 114 oneSecureKey=$(sed -n "$line p" $file_name |awk -v dateNow=$(date +"%Y") '{ 115 split($0,rhost,"rhost=")116 split(rhost[2],rhost," ")117 split($0,user,"user=")118 if (length(user[2])==0) {119 user[2]="None" 120 }121 print dateNow":"$1":"$2","rhost[1]","user[2]122 }') 123 log_info "[read_line_file] line: $line data:[$oneSecureKey]" 124 125 send_map $oneSecureKey126 }127 128 # 往MAP中塞入数据129 send_map() {130 local key=$1 131 if [ -n ${secureMap[$key]} ];then 132 secureMap[$key]=`expr ${secureMap[$key]} + 1`133 else 134 secureMap[$key]=1 135 fi 136 }137 138 wirte_all_secure() {139 for key in ${!secureMap[@]};do 140 write_one_secure $key141 done 142 }143 144 write_one_secure() {145 local key="$@" 146 local data=$(grep -w -n "$key"$ssh_auth_failed_file)147 if [ -n "$data" ];then 148 local i=$(echo $data | awk -F: '{print $1}')149 local a=$(echo $data | awk -F, '{print $NF}')150 sed -i "${i} s#$a#${secureMap[$key]}#"$ssh_auth_failed_file151 if [ $? -ne 0 ];then 152 log_err "[write_secure] 写 $ssh_auth_failed_file 文件失败! data:[$key,${secureMap[$key]}]" 153 fi 154 else 155 # 新数据156 echo "$key,${secureMap[$key]}" >>$ssh_auth_failed_file157 if [ $? -ne 0 ];then 158 log_err "[write_secure] 写 $ssh_auth_failed_file 文件失败! data:[$key,${secureMap[$key]}]" 159 fi 160 fi 161 log_info "[write_secure] line: $line status: $write_status data:[$key,${secureMap[$key]}]" 162 }163 164 165 166 # 启动前应先检查是否读取过167 check() {168 # 检查预存Inode是否一致169 check_secure_file_inode170 }171 172 # 检查登录日志Inode是否一致173 check_secure_file_inode() {174 inode=$(ls -i $file_name | awk '{print $1}')175 inode_file_data="$(cat $inode_file)" 176 if [ -n "$inode_file_data" ]; then 177 if [ $inode -ne $inode_file_data ];then 178 log_warning "[check_secure_file_inode] secure file inode is inconsistency" 179 # inode不一致,重置180 echo "$inode" >$inode_file181 inode_file_status=1 182 else 183 inode_file_status=0 184 fi 185 else 186 # 第一次读取187 echo "$inode" >$inode_file188 inode_file_status=1 189 fi 190 }191 192 # 开始读取文件193 start_read_file() {194 # 第一次读取195 if [ $inode_file_status -eq 1 ] ;then 196 # 使用循环将历史内容读取197 while true;do 198 if [ $line -eq $(wc -l < $file_name) ];then 199 break200 fi 201 read_line_file202 done 203 wirte_all_secure204 elif [ $line -ne $(wc -l < $file_name) ];then 205 # 使用循环将行数对齐206 while true;do 207 if [ $line -eq $(wc -l < $file_name) ];then 208 break209 fi 210 read_line_file211 if [ $write_status -eq 0 ];then 212 write_one_secure $oneSecureKey213 fi 214 # 状态设置为1215 write_status=1 216 done 217 # else 218 # read_line_file219 # if [ $write_status -eq 0 ];then 220 # write_one_secure $oneSecureKey221 # fi 222 # # 状态设置为1223 # write_status=1 224 fi 225 }226 227 test_main() {228 init229 check_secure_file_inode230 231 }232 233 main() {234 # 初始化235 init236 # 内容检查237 check238 start_read_file239 log_info "[main] watch secure startd" 240 watch_file241 }242 243 main