|
~ To be, or not to be, or to let it be. ~ null-i.net |
| Linux/静的IPフィルタの作成 | |
|
アクセス失敗履歴をもとにIPアドレスフィルター(2017-03-05、追記 2019-06-07) 出入り禁止のIPフィルター、出禁(できん)リストを作るのが今回の趣旨です。*1
掲示板cgi(既に閉鎖済み) へのアクセスが、少し煩わしいというか気持ち悪いのです。 IPで止めていいのか?†本来なら、同一IPアドレスに複数の端末が紐づいているので そういう製品買おうよ?†さらに、これをマメに確認し更新し続けなければならないので、
フィルタする対象の確認†まずサーバの「入り口」を確認しておきます。 # netstat -a Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State (結果は抜粋しています、もっと多いはずです) tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN tcp6 0 0 [::]:http [::]:* LISTEN tcp6 0 0 [::]:ssh [::]:* LISTEN netstat でポートを探します、 上の例だと ssh(=22)とhttp(=80)の2つがこのサーバへの入り口です。 今回は、Webサイト、sshログイン、メールの3つをターゲットにします。 Webへの不正アクセスの集計と、IPフィルタの作成†特定のキーワードに対して、件数と日付を調べます。 # ./black_url.sh
0201 -----------------
4xx xxx.254.236.32 : 6
4xx xx.82.64.21 : 2
4xx xxx.25.166.199 : 1
4xx xx.82.65.21 : 1
4xx xxx.96.249.42 : 1
spm xxx.189.235.253 : 1
0202 -----------------
4xx xxx.56.47.199 : 1
4xx xxx.22.126.147 : 1
(中略)
xxx.14.194.226 :06days
xxx.96.249.42 :09days
xxx.189.235.253 :12days
セキュリティホール狙いの404 NotFound や # cat black_url.sh
#!/bin/sh
# アクセスログの場所を指定し、
# 今月の今日までのログを読み込む
ACLOG=/var/log/httpd/access_log*
mm=`export LANG=C && date +%b` # 日本語になっちゃったからLANG=Cにする
# mm=`date +%m` # うちの場合は、月を文字(Feb)でなく数値(02)で表示している。
min=1
max=`date +%-d`
# 月日指定する場合は以下をいじる
#mm=Feb
#min=`date +%d --date "2 day ago"`
#max=$min
while [ $min -le $max ]; do
ii=`printf '%02d' $min`
echo "$mm$ii -----------------"
# 一行目の mm ii の部分はアクセスログの書式(日付表示)に合わせて整形する
# -> "Feb 01" のような表示か、 "02/01" のような表示か、など
# 二行目で、アクセス禁止のトリガにするキーワードを選ぶ
# -> 以下の例は2017/02 で比較的多かったもの。もっと沢山あるかも。
egrep "$mm $ii" $ACLOG | \
egrep -i "/mysql/admin|/phpmyadmin|/setup.php/|/wp-admin/|/upbbs.cgi" |\
awk '
{
# アクセスログに合わせて$数値は変える。以下の場合は、
# $2 番目がIPアドレスで、
# $4 番目がレスポンスコード の場合
if($0 ~ /upbbs.cgi/ && $4 ~ /[45][0-9][0-9]/){
# ここだけ別カウント。
# 今は存在しない掲示板。これは別カウントして確実に止める。
host_list_spam[$2]++
}
else if($4 ~ /4[0-9][0-9]/){
host_list_400[$2]++
}
else if($4 ~ /5[0-9][0-9]/){
host_list_500[$2]++
}
}
END{
# 配列 host_list の
# 添え字i は IPアドレス、中身はアクセス数 になる
for (i in host_list_400) {
printf(" 4xx %16s : %d\n", i, host_list_400[i]);
}
for (i in host_list_500) {
printf(" 5xx %16s : %d\n", i, host_list_500[i]);
}
for (i in host_list_spam) {
printf(" spm %16s : %d\n", i, host_list_spam[i]);
}
}' | tee $0.tmp
awk '{print $2;}' $0.tmp | sort -u > ng_list$mm$ii.txt
\rm $0.tmp
min=`expr $min + 1 `
done
# 過去何回アクセスしてきているかを調べる
awk '{
a[$1]++;
} END{
for (i in a){
printf("%16s :%02ddays\n", i, a[i]);
}
}' ng_list$mm* |\
sort -k2 |\
tee ng_list_days.txt
作成された ng_list*.txt で、再犯率に応じてリストをピックアップしましょう。 当時は、 参考まで、 まず、以前入れたものと総入れ替えすることにしたので、 # for ff in `firewall-cmd --list-sources --zone=drop`; do echo $ff------ firewall-cmd --zone=drop --permanent --remove-source=$ff done # firewall-cmd --reload # firewall-cmd --list-sources --zone=drop 今回のリストをすべて入れます。 # for ff in `cat black_hosts_1702.txt`; do echo $ff------- firewall-cmd --zone=drop --permanent --add-source=$ff done # firewall-cmd --reload # firewall-cmd --list-sources --zone=drop 効果のほどは・・・ ログイン失敗履歴(btmp/lastb)の集計†以下、ログインIDと、IPアドレスごとに失敗数を数えています。 # cat black_login.sh
#!/bin/sh
# btmpの実行結果から
# 今月の今日までのログを読み込む
mm=`export LANG=C && date +%b`
min=1
max=`date +%-d`
# 月日指定する場合は以下をいじる。
#mm="Feb"
#min=1
#max=28
while [ $min -le $max ]; do
ii=`printf '%2d' $min`
echo "$mm$ii -----------------"
# ローテ済みのログを見る場合は、 -f ファイル名で指定する。
#lastb -i -f /var/log/btmp-* |\
lastb -i |\
egrep "$mm $ii" | \
awk '
{
if($0 != "" && $1 !~ /btmp/){ # 空行とログの最後の記述は除外。
host_list[$3]++
name_list[$1]++
}
}
END{
# 配列 host_list, name_list の
# 添え字i は IPアドレス/ログイン名、中身はアクセス数 になる
for (i in host_list) {
printf(" %16s : %d\n", i, host_list[i]);
}
for (i in name_list) {
printf(" name %10s : %d\n", i, name_list[i]);
}
}' | tee btmp_list$mm$min.txt
min=`expr $min + 1 `
done
# 過去何回アクセスしてきているかを調べる
echo "----- total count [login]"
awk '{
if($1 ~ /name/){
a[$2] += $4;
}
} END{
for (i in a){
printf("%16s %4d\n", i, a[i]);
}
}' btmp_list$mm* |\
sort -k2
echo "----- total count [host]"
# 統計の出力
awk '{
if($1 ~ /name/){
# nothing
} else {
a[$1]++;
}
} END{
for (i in a){
printf("%16s :%02ddays\n", i, a[i]);
}
}' btmp_list$mm* |\
sort -k2 |\
tee btmp_list_days.txt
メール履歴(postfixなど)の集計†ログがフリーフォーマットなので、手探りで拾っています。 # cat black_mail.sh
#!/bin/sh
# アクセスログの場所を指定し、
# 今月の今日までのログを読み込む
MLLOG=/var/log/maillog*
mm=`export LANG=C && date +%b` # 日本語になっちゃったからLANG=Cにする
min=1
max=`date +%-d`
# 月日指定する場合は以下をいじる
#mm=Feb
#min=`date +%-d --date "2 day ago"`
min=1
#max=28
while [ $min -le $max ]; do
ii=`printf '%2d' $min`
echo "$mm$ii -----------------"
# 一行目の mm ii の部分はアクセスログの書式に合わせて整形する
# 二行目で、アクセス禁止のトリガにするキーワードを選ぶ
egrep "^$mm $ii" $MLLOG | \
egrep -i "fail|err" |\
awk '
{
if($0 ~ /auth.+fail/ && match($0, /\[[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\]/) ){
# 認証失敗のログから(auth.+fail)
# matchでIPアドレスを抜き出す ([IPアドレス])
host_list_auth[substr($0, RSTART+1, RLENGTH-2)]++
}
else if($0 ~ /fail/){
# IPアドレスが拾えない場合は行番号NRで文面を拾う。
msg_list_fail[NR]=$0
}
else {
# まだ想定書式がないので、とりあえず行番号NRで文面を拾う。
msg_list_err[NR]=$0
}
}
END{
for (i in host_list_auth) {
printf(" badIP %16s : %d\n", i, host_list_auth[i]);
}
for (i in msg_list_fail) {
printf(" fail %s\n", msg_list_fail[i]);
}
for (i in msg_list_err) {
printf(" err %s\n", msg_list_err[i]);
}
}' | tee $0.tmp
grep badIP $0.tmp | awk '{print $2;}' | sort -u > bad_mail$mm$ii.txt
\rm $0.tmp
min=`expr $min + 1 `
done
# 過去何回アクセスしてきているかを調べる
awk '{
a[$1]++;
} END{
for (i in a){
printf("%16s :%02ddays\n", i, a[i]);
}
}' bad_mail$mm* |\
sort -k2 |\
tee bad_mail_days.txt
やってみた感想†以上のような履歴をもとにしたIPアドレスフィルターですが、 攻撃自体は公開しているサーバなら必ず来るものなので、
もう、そうなると何を信じればいいのやら。 IoTだの、ネットワーク化が更に進めば、 |
|
![[worried]](image/face/worried.png)