2022年12月現在、「Mod_Securityが検知したIPアドレスネガティブリストに放り込む」は達成しました。

  • これら検知したIPはどこの国からか?
  • 日々、どのぐらいの数、不審なアクセスが来ているのか?

を把握するため、シェルスクリプトを書きました。(今回で一端終了です)

やりたいこと

  1. Mod_securityが検知したエラーログからIPアドレスのみを抜き出す
  2. 検出したIPアドレスがどこの国からアクセスしたかを調査してcsvファイルに出力する
  3. 同じIPアドレスごとに件数を数え、降順で並べ替える
  4. cronで定期実行する

本項でやること

  1. cronで定期実行

前提

  • エラーログからIPアドレスを抜き出しアクセス元の国を抜き出すスクリプト
  • そのIPアドレスをカウントして降順に表示するスクリプト

が用意されていること。(前回前々回で提示)

また、

  • apacheのログが日ごとにローテーションされていること
  • redmineの稼働サーバに導入しているので、ログ表示プラグインで閲覧できる

も想定しています。

スクリプト作成

cron用に一部手を加えます。

エラーログからIPアドレスを抜き出してアクセス元の国を付与

ip_check.sh

スクリプト内容

#!/bin/bash

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# ログの格納場所に移動
cd /var/lib/redmine/log

# error.logからIPアドレスだけを抜き出します
cat ./error.log | awk 'match($0,/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) { print  substr($0, RSTART, RLENGTH) }' | sort > ip.`date +%Y%m%d`.csv

listfile="./ip.`date +%Y%m%d`.csv"
# ip.YYYYMMDD.csvファイルがない場合にエラーを返します
if [ ! -f $listfile ]; then
        echo "ファイル $listfile が存在しないので終了します。"
        exit 1
fi

country_list="./country_list.csv"
# 国コード,国の名前が書かれたcountry_list.csvがない場合にエラーを返します
# このCSVファイルはログの格納場所に配置します
if [ ! -f $country_list ]; then
        echo "ファイル $country_list が存在しないので終了します。"
        exit 1
fi

# IPアドレスに国名を付与したファイルを定義します。(ip_list.YYYYMMDD.csv)
result_file="./ip_list.`date +%Y%m%d`.csv"
cat /dev/null > $result_file

cat ${listfile} | while read line
do
                # IPアドレスを逆順に並び替えます。(例: 1.2.3.4 → 4.3.2.1
                ip_revers=`echo ${line}|awk -F'.' '{print $4,".",$3,".",$2,".",$1}'|sed -e 's/ //g'`
                # 並び替えたIPアドレスをcc.wariate.jpに並び替えて、国コードを抜き出します。(JP,CH,ESなど
                country_code=`nslookup -type=TXT ${ip_revers}.cc.wariate.jp | grep '"'|awk -F'"' '{print $2}' `
                # 国コードをcountry_list.csvから参照して国名を抜き出します。
                country_name=`cat ${country_list} | grep ${country_code} | cut -d"," -f 2`

                # 次の行は標準出力に返す処理を行います。cron処理する場合は#をつけて作動しないようにします。
                # echo "${line},${country_name}"
                # IPアドレス,国名の形式にして同ディレクトリのip_list.YYYYMMDD.csvに保存します
                echo "${line},${country_name}" >> $result_file
        sleep 1
done

chown www-data:www-data ip.`date +%Y%m%d`.csv
chown www-data:www-data ip_list.`date +%Y%m%d`.csv

exit

アクセス元の国を付与したリストを集計するスクリプト

ip_count.sh

スクリプト内容

#!/bin/bash

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

cd /var/lib/redmine/log

# ip_list.csv.YYYYMMDDを読み込んで、IPアドレスをキー、国を値とする連想配列を作成します
declare -A countries
while read -r line; do
  # CSVの1列目をIPアドレス、2列目を国とします
  ip=$(echo $line | cut -d',' -f1)
  country=$(echo $line | cut -d',' -f2)
  # 連想配列に格納します
  countries[$ip]=$country
done < ip_list.`date +%Y%m%d`.csv

# 結果を格納するための変数を定義します
result=""

# 連想配列を反復処理します
for ip in "${!countries[@]}"; do
  # 各IPアドレスの件数を数えます
  count=$(grep -c $ip ip_list.`date +%Y%m%d`.csv)
  # 件数、IPアドレス、国をカンマ区切りで結合します
  line="$count,$ip,${countries[$ip]}"
  # 結果に追加します
  result="$result\n$line"
done

# カウントした結果をcounted_ip.YYYYMMDD.csvに出力します
echo -e $result > counted_ip.`date +%Y%m%d`.csv

### ↑ここまでchatGPTが作成したスクリプト↑ ###

# 最終結果をsorted_ip.`date +%Y%m%d`.csvに出力します
# LC_ALL=Cを設定しないと日本語で書かれた国名の並べ替えがうまくいきませんでした
cat counted_ip.`date +%Y%m%d`.csv |LC_ALL=C sort -n -r > sorted_ip.`date +%Y%m%d`.csv

# 最終結果以外のログファイルを削除します
# 必要に応じて無効化してください
rm ip.`date +%Y%m%d`.csv
rm ip_list.`date +%Y%m%d`.csv
rm counted_ip.`date +%Y%m%d`.csv

chown www-data:www-data sorted_ip.`date +%Y%m%d`.csv

exit

Cron配置

sudo crontab -e -u root

定期実行

0 7 * * * /スクリプト配置パス/ip_check.sh && /スクリプト配置パス/ip_count.sh

これで、指定した時刻に

  1. IPアドレスの抜き出し
  2. IPアドレスの集計

を行うようにしてくれます。