![見出し画像](https://assets.st-note.com/production/uploads/images/99429567/rectangle_large_type_2_cab949f0d277181d2bac3d05c7ac643b.png?width=1200)
【Linux】 ログ監視の設定
Linuxにおけるログ監視の具体例を記す。
ファイルを監視して、特定の文字列が出力された場合に別ファイルを作成して記録する
#!/bin/bash
# 検出対象ログファイル
TARGET_LOG="/var/log/messages"
# 検出文字列
_error_conditions="error hogehoge"
# ログファイルを監視する関数
hit_action() {
while read i
do
echo $i | grep -q "${_error_conditions}"
if [ $? = "0" ];then
# アクション
touch /tmp/hogedetayo
fi
done
}
# main
if [ ! -f ${TARGET_LOG} ]; then
touch ${TARGET_LOG}
fi
tail -n 0 --follow=name --retry $TARGET_LOG | hit_action
tailコマンド
-n : 末尾から表示する行数を指定。標準出力しないように"0"を指定。
--follow=name :ファイルの内容が増え続けても ファイルの最終部分の文字を読み続けようと無限にループする。 (-fと同じ)
実際の利用では、スクリプトをバックグラウンドで動作。
/usr/local/bin/logchecker.sh > /dev/null 2>&1 &
同じタイミングで一気にログが追加されても、末尾だけでなく追加された分を逃さず見ることができる。
簡易的に作成
#!/bin/bash
tail -fn0 /var/log/messages
while read line
do
echo "$line" |
grep "error hogehoge" >/dev/null
if [ $? = 0 ]
then
touch /tmp/hogedetayo
fi
done
tailコマンドで/var/log/messagesを監視して、新しい行が出力されるたびにreadコマンドで読み取る。
grepコマンドを使用して、行が"error hogehoge"と一致するかどうかを確認し、一致する場合にはtouchコマンドで/tmp/hogedetayoファイルを作成。
ログローテーションに対応し、複数のログファイルを監視。特定の文字列が出力されるとメールで通知
#!/bin/bash
LOG_FILES=("/var/log/messages" "/var/log/syslog")
for LOG_FILE in "${LOG_FILES[@]}"
do
tail --retry --follow=name --quiet "$LOG_FILE" | \
while read line
do
echo "$line" | grep -q "error hogehoge" && \
echo "Error found in $LOG_FILE: $line" | mail -s "Error log" user@example.com
done
done
監視対象のログファイルを配列変数(LOG_FILES)に設定し、forループで順次処理。
tailコマンドのオプション(--retry, --follow=name, --quiet)を使って、ログファイルをリアルタイムに監視。
$line変数に格納された新しい行に対して、grepコマンドで"error hogehoge"という文字列が含まれているかを確認し、含まれている場合には、メールで通知する。
ログローテーションに対応している。
複数のログファイルを監視し、特定の文字列があった場合にアラートを出す
#!/bin/bash
# ログ監視対象のログファイルとキーワードの定義
declare -A logs=(["/var/log/messages"]="error" ["/var/log/nginx/access.log"]="404")
ALERT="ALERT!!!" # アラート通知のメッセージ
for log in "${!logs[@]}"; do
# 監視対象のログファイルを1行ずつ読み込み、キーワードが含まれた場合にアラートを出す
while read line; do
if [[ "$line" == *"${logs[$log]}"* ]]; then
echo "$ALERT ${logs[$log]} found in $log"
fi
done < <(tail -n0 -F "$log")
done
declare -Aを使ってログファイルとキーワードの対応関係を配列で定義して。ログファイルが増えても対応でき、アラートメッセージはALERTという定数に設定し、必要に応じて変更できる。
forループで、logs配列に定義されたログファイルを1つずつ読み込み、whileループ内で、tail -n0 -Fコマンドでログファイルをリアルタイムに監視し、新しいログが追記されたらその行を読み込んで、if文でキーワードが含まれているかを判定。もしキーワードが含まれていた場合は、アラートメッセージを出力する。
コマンドも記録する
#!/bin/bash
# 監視対象のログファイル
LOG_FILE="/var/log/messages"
# 監視するキーワード
KEYWORD="error"
# ログを監視し続ける
tail -n 0 -F ${LOG_FILE} | while read line; do
# ログにキーワードが含まれる場合
if echo ${line} | grep -i ${KEYWORD} > /dev/null; then
# 指定のコマンドを実行する
touch /tmp/hogedetayo
echo "$(date) - ${KEYWORD} found - Command executed: touch /tmp/hogedetayo"
fi
done
grepコマンドに-iオプションを使用して、キーワードの大文字・小文字を区別しない。
touchコマンドの実行をログに残すために、echoコマンドを使用し、ログには日付とキーワードが見つかったこと、実行されたコマンドが記録される。
特定のIPアドレスからのログインが5回以上失敗したIPアドレスを自動的にブロック
# /var/log/auth.logファイルからFailed passwordという文字列を検索し、
# ログインに5回以上失敗したIPアドレスを取得。
# その後、iptablesコマンドを使って、対象IPアドレスをブロックする。
---------------------------------------------------------
#!/bin/bash
LOG_FILE="/var/log/auth.log"
IP_ADDR=$(grep "Failed password" $LOG_FILE | awk '{print $(NF-3)}' | sort | uniq -c | awk '{if($1>=5) print $2}')
BAN_TIME="24h"
for IP in $IP_ADDR
do
echo "Banning $IP for $BAN_TIME"
sudo iptables -A INPUT -s $IP -j DROP
done
---------------------------------------------------------
# ブロックの解除
iptables -L -n # ブロックされたIPアドレスを確認
# 特定されたチェインからブロックルールを削除します。ブロックルールが INPUT チェインに存在する場合
iptables -D INPUT -s 192.168.0.100 -j DROP
"Failed password" という文字列が含まれる行を検索し、それらの行から IP アドレスを抽出。awk コマンドを使用して、各行から $(NF-3) つまり4つ目のフィールドにある IP アドレスを抽出し、sort コマンドで重複を削除し、uniq -c コマンドで IP アドレスごとに件数をカウント。最後に、awk コマンドで件数が5以上の IP アドレスをフィルタリングして変数 IP_ADDR に格納している。
リクエスト数が多いIPアドレスを検出して、ブロックするiptablesルールを設定
#!/bin/bash
# ログファイルのパス
LOG_FILE="/var/log/httpd/access_log"
# アクセス数の閾値
ACCESS_THRESHOLD=1000
# ブロック時間(秒)
BLOCK_TIME=600
# iptablesに追加するチェーン名
CHAIN_NAME="BLOCKED_IPS"
# iptablesに対して、新しいチェーンを追加する
iptables -N $CHAIN_NAME
# Apacheのアクセスログファイルを監視する
tail -n0 -F $LOG_FILE | while read line; do
# 現在の日付時刻
DATE=$(date '+%d/%b/%Y:%H:%M')
# ログファイル中から、現在の日付時刻でのアクセス数が閾値を超えたIPアドレスを抽出する
IP_ADDRESSES=$(grep "$DATE" $LOG_FILE | awk '{print $1}' | sort | uniq -c | awk -v threshold=$ACCESS_THRESHOLD '$1 > threshold {print $2}')
# 抽出したIPアドレスに対して、iptablesルールを設定する
for IP_ADDRESS in $IP_ADDRESSES; do
# 既にブロックされたIPアドレスであれば、スキップする
if iptables -L $CHAIN_NAME -n | grep -q $IP_ADDRESS; then
continue
fi
# iptablesにルールを追加して、IPアドレスをブロックする
echo "Blocking IP address: $IP_ADDRESS"
iptables -A $CHAIN_NAME -s $IP_ADDRESS -j DROP
# ブロックが解除される時間を計算する
UNBLOCK_TIME=$(($(date +%s) + $BLOCK_TIME))
# ブロックが解除されるまで待機する
while [ $(date +%s) -lt $UNBLOCK_TIME ]; do
sleep 1
done
# iptablesからブロックルールを削除する
echo "Unblocking IP address: $IP_ADDRESS"
iptables -D $CHAIN_NAME -s $IP_ADDRESS -j DROP
done
done
ログファイルを監視し、現在の日付時刻でのアクセス数が閾値を超えたIPアドレスを抽出。
iptablesルールを追加してIPアドレスをブロックします。ブロックが解除される時間を計算して、ブロックが解除されるまで待機。
ブロックが解除されたら、iptablesからブロックルールを削除。
CPU使用率が90%を超えた場合に、トップ10プロセスを表示
# topコマンドを使ってCPU使用率を取得し、しきい値である90%を超えた場合に、
# トップ10プロセスを表示。その後、30秒ごとにCPU使用率を再度取得。
---------------------------------------------------------
#!/bin/bash
THRESHOLD=90
SECONDS=30
while true
do
CPU_USAGE=$(top -b -n 1 | awk '/^%Cpu/{print $2}')
if (( $(echo "$CPU_USAGE > $THRESHOLD" | bc -l) )); then
echo "CPU usage is above $THRESHOLD%!"
echo "Top 10 processes:"
ps aux --sort=-%cpu | head -n 11
fi
sleep $SECONDS
done
topコマンドの -b オプションを使用して、バッチモードで実行。
-n オプションを使用して一度だけ top を実行し、その結果を AWK で処理して、CPU使用率の値を抽出。
AWKコマンドは、%Cpuから始まる行を見つけて、その次のフィールド($2)の値を出力する。
if文は、bc -l コマンドを使用して、CPU 使用率としきい値の比較をする。bcコマンドは、echoコマンドで出力された式を計算し、-lオプションは、結果を10進数で表示する。
ps aux --sort=-%cpu は、CPU 使用率が高い順にプロセスをソートし、head -n 11 は最初の11行(ヘッダー行を含む)を出力する。
Apacheのアクセスログを監視し、1分間に100回以上アクセスされた場合に警告する
# tailコマンドで直近1000行のログから、1分以内のログを抽出し、
# その行数を取得。その後、しきい値である100以上の場合には、警告を表示
---------------------------------------------------------
#!/bin/bash
LOG_FILE="/var/log/apache2/access.log"
THRESHOLD=100
SECONDS=60
while true
do
ACCESS_COUNT=$(tail -n 1000 $LOG_FILE | grep "$(date +'%d/%b/%Y:%H:%M')" | wc -l)
if (( $ACCESS_COUNT >= $THRESHOLD )); then
echo "Warning: $ACCESS_COUNT accesses in the last minute!"
fi
sleep $SECONDS
done
grep "$(date +'%d/%b/%Y:%H:%M')"で、現在の時刻(分単位まで)に該当するログを抽出。
wc -lで、抽出されたログの行数を取得。
取得したアクセス数が、指定された閾値($THRESHOLD)以上であれば、警告メッセージを表示。
指定された秒数($SECONDS)だけスリープする。
ログファイルのサイズが大きくなりすぎないように、適宜ログローテーションを行う必要がある
ディスク使用量を監視し、一定の閾値を超えた場合に、最もディスク使用量が高いディレクトリを表示
#!/bin/bash
THRESHOLD=90
DIR_USAGE=$(du -h / | awk '$1 >= "10G" {print}')
TOP_DIR=$(echo "$DIR_USAGE" | sort -hr | head -n 1)
if (( $(echo "$TOP_DIR" | awk '{print $1}' | sed 's/%//') > $THRESHOLD )); then
echo "Disk usage is above $THRESHOLD% in the following directory:"
echo "$TOP_DIR"
fi
THRESHOLD変数に90を設定し、MONITOR_DIR変数に/var/logを設定。
dfコマンドを使用して、$MONITOR_DIRのディスク使用量を取得し、awkコマンドとsedコマンドを使用してパーセンテージのみを取得。
閾値を超える場合は、duコマンドを使用してディレクトリごとのディスク使用量を計算し、sortコマンドとheadコマンドを使用して最大のディスク使用量を持つディレクトリを検索。最後に、そのディレクトリを表示。
指定した特定のユーザーがログインした場合にメールで通知する
#!/bin/bash
LOG_FILE="/var/log/secure"
MAILTO="admin@example.com"
KEYWORD=("root" "sshd" "alice")
for user in "${KEYWORD[@]}"
do
if grep -q "Accepted.*$user" "$LOG_FILE"; then
echo "User $user has logged in" | mail -s "SSH Login Alert" $MAILTO
fi
done
$KEYWORDで指定したユーザー名がログインした場合に、grep -qコマンドを使用してログファイル中にAcceptedと$userが含まれているかを検索。
-qオプションは、検索に成功した場合には何も出力せずにスクリプトを終了する。検索に成功した場合は、echoコマンドでメール本文を作成し、mailコマンドでメールを送信。
-sオプションでメールの件名を指定し、$MAILTOでメールの宛先を指定。
ログローテーション
よろしければサポートお願いします!よりいい情報を発信します。