運維指令碼: 實時監測登入

小书童·阿杰發表於2024-11-28
引言
  • 背景介紹:在伺服器的運維管理中,及時監控系統的登入日誌對保障系統的安全至關重要。透過實時監控登入日誌,運維人員可以發現潛在的異常登入行為,防止系統被非法訪問。
  • 問題引入:如何實現實時監控登入日誌,並及時響應潛在的安全風險?
實時監控登入日誌的意義
  • 安全性:透過監控登入日誌,可以迅速發現惡意登入、暴力破解等異常行為。
  • 合規性:確保滿足各種合規要求,記錄所有使用者的登入行為。
解決方案概述
  • 監控目標:關注登入日誌中的關鍵資訊,例如登入時間、IP 地址、使用者名稱、登入方式等。
  • 技術選型:透過編寫 Bash 指令碼,結合inotifyawkgrep 等工具,來實現對日誌檔案的實時監控與分析。
指令碼實現原理
  • 實時監控:利用 inotify 命令動態監控日誌檔案的變動,並結合 sed 命令實時提取和輸出新增的登入日誌。
  • 日誌篩選:透過 grep 等工具過濾出登入失敗、異常登入等相關資訊。
  • 報警機制:指令碼可以配置成在監控到異常行為時,自動傳送通知郵件

指令碼示例

  1 #!/bin/bash
  2 # 作者: 阿杰
  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 # 初始化Map
 21 declare -A secureMap
 22 
 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             mkdir conf
 43         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_file
 56         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_name
 75         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_file
 89         ;;
 90         # 檔案被刪除或重新建立
 91         CREATE|DELETE)
 92             # 重置檔案行數
 93             line=0
 94             > $line_file_name
 95             check
 96         ;;
 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_name
108     # 不是指定資料退出
109     if [ $(sed -n "$line p" $file_name  | grep 'pam_unix(sshd:auth): authentication failure;' | wc -l ) -ne 1 ];then
110         return
111     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 $oneSecureKey
126 }
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 $key
141     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_file
151         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_file
157         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_inode
170 }
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_file
181             inode_file_status=1
182         else
183            inode_file_status=0
184         fi
185     else
186         # 第一次讀取
187         echo "$inode" > $inode_file
188         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                 break
200             fi
201             read_line_file 
202         done
203         wirte_all_secure
204     elif [  $line -ne $(wc -l < $file_name) ];then
205         # 使用迴圈將行數對齊
206         while true;do
207             if [ $line -eq $(wc -l < $file_name) ];then
208                 break
209             fi
210             read_line_file 
211             if [ $write_status -eq 0 ];then
212                 write_one_secure $oneSecureKey
213             fi
214             # 狀態設定為1
215             write_status=1
216         done        
217     # else
218     #     read_line_file
219     #     if [ $write_status -eq 0 ];then
220     #         write_one_secure $oneSecureKey
221     #     fi
222     #     # 狀態設定為1
223     #     write_status=1
224     fi
225 }
226 
227 test_main() {
228     init
229     check_secure_file_inode
230 
231 }
232 
233 main() {
234     # 初始化
235     init
236     # 內容檢查
237     check
238     start_read_file
239     log_info "[main] watch secure startd"
240     watch_file
241 }
242 
243 main

相關文章