ufwのログをPythonで集計してみる

Linux Programming Python

Webサーバをインターネット上に公開運用していると、避けられないのがサイバー攻撃です。

特に攻撃として厄介なのは単にサーバへ負荷を掛けるDOS攻撃ではなく、こっそり侵入して踏み台やら他への攻撃用のBotとして利用されるケースですね。

本ブログサーバはLinuxで稼働し、オープンソースのファイヤウオールアプリケーションのufwでパケットフィルタリングしています。
ubuntu系のLinuxであれば以下コマンドでufwをインストール可能です。

sudo apt install ufw

ufwを有効にするには以下

sudo ufw enable

ufwの設定内容を確認するには以下

sudo ufw status numbered

設定内容は以下のように表示されます。

[ 1] Apache Full                ALLOW IN    Anywhere                  
[ 2] OpenSSH                    ALLOW IN    Anywhere                  
[ 3] 8080                       ALLOW IN    Anywhere                  
[ 4] 22                         ALLOW IN    Anywhere                  
[ 5] 8000                       ALLOW IN    Anywhere                  
[ 6] 3128                       ALLOW IN    Anywhere 

またufwのログは/var/log/ufw.logに存在し、内容は以下のようなものになります。

Feb  5 00:46:55 EeePC-1005HA kernel: [3059566.005174] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=104.37.191.23 DST=192.168.1.111 LEN=40 TOS=0x00 PREC=0x00 TTL=238 ID=54321 PROTO=TCP SPT=36850 DPT=52869 WINDOW=65535 RES=0x00 SYN URGP=0 
Feb  5 00:47:05 EeePC-1005HA kernel: [3059575.662561] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=88.42.32.78 DST=192.168.1.111 LEN=40 TOS=0x00 PREC=0x00 TTL=50 ID=14545 PROTO=TCP SPT=3205 DPT=60001 WINDOW=1164 RES=0x00 SYN URGP=0 
Feb  5 00:47:47 EeePC-1005HA kernel: [3059618.184622] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=51.91.212.80 DST=192.168.1.111 LEN=40 TOS=0x00 PREC=0x00 TTL=238 ID=54321 PROTO=TCP SPT=59946 DPT=8082 WINDOW=65535 RES=0x00 SYN URGP=0 
Feb  5 00:48:01 EeePC-1005HA kernel: [3059632.087773] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=193.32.161.31 DST=192.168.1.111 LEN=40 TOS=0x00 PREC=0x00 TTL=236 ID=20117 PROTO=TCP SPT=50748 DPT=9010 WINDOW=1024 RES=0x00 SYN URGP=0 
Feb  5 00:48:32 EeePC-1005HA kernel: [3059663.301766] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=45.56.122.208 DST=192.168.1.111 LEN=40 TOS=0x00 PREC=0x00 TTL=243 ID=54321 PROTO=TCP SPT=53212 DPT=2404 WINDOW=65535 RES=0x00 SYN URGP=0 
Feb  5 00:49:01 EeePC-1005HA kernel: [3059692.347139] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=74.82.47.15 DST=192.168.1.111 LEN=40 TOS=0x00 PREC=0x00 TTL=246 ID=54321 PROTO=TCP SPT=40657 DPT=389 WINDOW=65535 RES=0x00 SYN URGP=0 
Feb  5 00:49:32 EeePC-1005HA kernel: [3059722.655854] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=140.238.163.130 DST=192.168.1.111 LEN=49 TOS=0x00 PREC=0x00 TTL=242 ID=54321 PROTO=UDP SPT=55888 DPT=11211 LEN=29 
Feb  5 00:49:50 EeePC-1005HA kernel: [3059741.390640] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=194.26.29.104 DST=192.168.1.111 LEN=40 TOS=0x00 PREC=0x00 TTL=244 ID=12949 PROTO=TCP SPT=59042 DPT=15843 WINDOW=1024 RES=0x00 SYN URGP=0 
Feb  5 00:50:04 EeePC-1005HA kernel: [3059754.525362] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=162.62.19.220 DST=192.168.1.111 LEN=40 TOS=0x08 PREC=0x00 TTL=231 ID=54321 PROTO=TCP SPT=58032 DPT=2080 WINDOW=65535 RES=0x00 SYN URGP=0 
Feb  5 00:50:09 EeePC-1005HA kernel: [3059759.991423] [UFW BLOCK] IN=enp1s0 OUT= MAC=90:e6:ba:60:b0:21:68:89:c1:c5:28:28:08:00 SRC=201.149.110.5 DST=192.168.1.111 LEN=52 TOS=0x00 PREC=0x00 TTL=109 ID=26292 DF PROTO=TCP SPT=52276 DPT=1433 WINDOW=8192 RES=0x00 SYN URGP=0 

ちょっと見辛い。。。
そんなわけで上記ログを見やすくするコードを久しぶりにPythonで書きました。集計ついでにGeoIPを利用してどこの国からアクセスが多いのかも判別可能にしました。

なお、筆者の本業はインフラエンジニアでプログラミングは趣味レベルなので至らない点があるかもしれませんが、ご容赦ください。

事前準備

・GeoIPデータベースファイル
GeoIP2を使います。以下のURLにサインアップして取得可能です。
https://www.maxmind.com/

GeoLite2-Country_20200128.tar.gzというファイルをダウンロードして解凍すると、GeoLite2-Country.mmdbが格納されています。このファイルをコードから読み込みます。

・python3-geoip2
以下コマンドでインストール

sudo apt install python3-geoip2

ソースコード

#!/usr/bin/python3
import re
import sys
import ipaddress
import geoip2.database
import csv
import collections
filename='/var/log/ufw.log'
ip_address = []
ip_country = []
ip_date = []
gip = geoip2.database.Reader("GeoLite2/GeoLite2-Country.mmdb")
#
def trans_geoip(ip_addr):
    if '192.168.1.' in ip_addr:
        return ('Private')
    try:
        response = gip.country(ip_addr)
        return (response.country.names['ja'])
    except:
        print('')
def trans_word(input_text):
    replacements = {
            'Jan':'01',
            'Feb':'02',
            'Mar':'03',
            'Apl':'04',
            'May':'05',
            'Jun':'06',
            'Jul':'07',
            'Aug':'08',
            'Sep':'09',
            'Oct':'10',
            'Nov':'11',
            'Dec':'12',
            }
#    print('({})'.format('|'.join(map(re.escape,replacements.keys()))))
    return re.sub('({})'.format('|'.join(map(re.escape,replacements.keys()))),lambda m: replacements[m.group()],input_text)
with open(filename) as f:
    reader = csv.reader(f,delimiter=" ",doublequote=False,lineterminator="\r\n")
    for row in reader:
        ip_list = ("2020"+"/"+trans_word(row[0])+"/"+row[2].zfill(2),row[3],row[12].strip("SRC="))
        ip_address.append(row[12].strip("SRC="))
        ip_address.append(row[12].strip("SRC="))
        ip_date.append("2020"+"/"+trans_word(row[0])+"/"+row[2].zfill(2))
        
        ip_country.append(trans_geoip(row[12].strip("SRC=")))
        print("2020"+"/"+trans_word(row[0])+"/"+row[2].zfill(2),",",row[3],",",row[12].strip("SRC="),",",trans_geoip(row[12].strip("SRC=")))
ip_addrgroup = collections.Counter(ip_address)
ip_dategroup = collections.Counter(ip_date)
ip_cntrgroup = collections.Counter(ip_country)
print(ip_cntrgroup.most_common(10))
print(ip_addrgroup.most_common(10))
print(ip_dategroup)
print(trans_geoip(ip_addrgroup.most_common(1)[0][0]),ip_addrgroup.most_common(1)[0][0],ip_addrgroup.most_common(1)[0][1])
print(trans_geoip(ip_addrgroup.most_common(2)[1][0]),ip_addrgroup.most_common(2)[1][0],ip_addrgroup.most_common(2)[1][1])
print(trans_geoip(ip_addrgroup.most_common(3)[2][0]),ip_addrgroup.most_common(3)[2][0],ip_addrgroup.most_common(3)[2][1])
print(trans_geoip(ip_addrgroup.most_common(4)[3][0]),ip_addrgroup.most_common(4)[3][0],ip_addrgroup.most_common(4)[3][1])
print(trans_geoip(ip_addrgroup.most_common(5)[4][0]),ip_addrgroup.most_common(5)[4][0],ip_addrgroup.most_common(5)[4][1])

実行結果

上記実行結果は以下になります。

<ログの整形>

2020/02/05 , 01:00:07 , 167.71.205.13 , シンガポール
2020/02/05 , 01:00:44 , 185.216.140.185 , オランダ王国
2020/02/05 , 01:00:57 , 194.26.29.103 , ロシア
2020/02/05 , 01:02:46 , 106.75.78.135 , 中国
2020/02/05 , 01:03:00 , 77.247.108.119 , オランダ王国
2020/02/05 , 01:03:23 , 92.119.160.143 , ロシア
2020/02/05 , 01:03:41 , 78.188.229.94 , トルコ共和国
2020/02/05 , 01:03:57 , 162.243.128.14 , アメリカ合衆国
2020/02/05 , 01:04:05 , 92.119.160.52 , ロシア
2020/02/05 , 01:04:32 , 195.2.93.18 , オランダ王国
2020/02/05 , 01:04:38 , 193.32.161.71 , ルーマニア
2020/02/05 , 01:05:08 , 195.2.93.18 , オランダ王国
2020/02/05 , 01:05:43 , 51.91.212.80 , フランス共和国
2020/02/05 , 01:07:06 , 198.108.67.38 , アメリカ合衆国
2020/02/05 , 01:07:14 , 194.26.29.102 , ロシア
2020/02/05 , 01:07:32 , 209.17.97.26 , アメリカ合衆国
2020/02/05 , 01:07:40 , 185.176.27.162 , ロシア
2020/02/05 , 01:07:49 , 152.67.6.250 , インド
2020/02/05 , 01:08:13 , 186.67.181.60 , チリ共和国
2020/02/05 , 01:08:28 , 222.138.163.23 , 中国
2020/02/05 , 01:08:42 , 194.26.29.102 , ロシア
2020/02/05 , 01:08:50 , 118.96.16.93 , インドネシア共和国

<国別集計>

[('オランダ王国', 4309), ('ロシア', 1456), ('アメリカ合衆国', 808), ('中国', 751), ('ルーマニア', 464), ('スイス連邦', 460), ('日本', 171), ('フランス共和国', 114), ('セイシェル', 114), ('イギリス', 101)]

<IPアドレス別集計>

[('185.176.27.2', 1134), ('80.82.64.167', 598), ('89.248.168.225', 594), ('80.82.65.40', 586), ('89.248.172.196', 580), ('93.174.93.68', 560), ('89.248.162.247', 522), ('80.82.64.171', 514), ('185.39.10.54', 508), ('80.82.64.105', 458)]

<日付別集計>

Counter({'2020/02/02': 4063, '2020/02/03': 3826, '2020/02/04': 1525, '2020/02/05': 196})

<TOP5のIPと国>

ロシア 185.176.27.2 1134
オランダ王国 80.82.64.167 598
オランダ王国 89.248.168.225 594
オランダ王国 80.82.65.40 586
オランダ王国 89.248.172.196 580

ufwのブロックリストの追加

これなら見やすいし、わかりやすいですね。
上位5位のIPをとりあえずufwのブロックリストに追加しておきました。

sudo ufw deny from xxx.xxx.xxx.xxx to any

追加されたかどうか、以下で確認します。

sudo ufw status numbered

<確認結果>

[19] Anywhere                   DENY IN     185.176.27.2              
[20] Anywhere                   DENY IN     80.82.64.167              
[21] Anywhere                   DENY IN     89.248.168.225            
[22] Anywhere                   DENY IN     80.82.65.40               
[23] Anywhere                   DENY IN     89.248.172.196        

これで少しはWEBサーバへの負荷が減るかと思います。
誰かの役に立てば幸いです。
ハッピーハックライフを!