結構力技だが、IPアドレスの割当られた国を調べる方法。
怪しげなアクセスをしてきたIPを割り当てたブロックごとにIPTABLESでDROP指定するという記事を書いたが、
大量に怪しげなアクセスがあるし、割当ブロックを指定するのは難しい。
IP逆引きなどをすれば国もわかるそうだけれども、大量に対象のIPがあると相当時間がかかりそうだ。
それにDNSに対して多くの仕事を頼むのは行儀が良くないと思うし…
というわけでネットワークを使わず、IPが指定した国であることの判別と
そのIPを含む割り当てられたブロックを表示するプログラムをPythonで作った。
まず用意するのは怪しげなアクセスをしてきたIPを重複の内容にテキストに書き出す。
grepでステータスコード404のアクセスを cutコマンドで切り取って、sortとuniqで整形する感じにすればいいと思う。
僕は直近のアクセスを検索しやすいようにSQLiteで管理しているので、distinctで簡単検索!
次にこちらの
国地域別IPアドレス割当リスト
から特定の国(スパムは中国がもっとも多いので僕は中国を指定)の割当リストの
IPの部分をテキストに書き出す(x.x.x.x/24みたいな部分)
こんなプログラムを書いた
import ipaddress def main(): #割当IPのリストを読み込み f = open('./cnip.list') cniplist = [s.replace('\n','') for s in f] f.close() #自分のサイトに来た怪しげIPを読み込み f = open('./ip.txt') iplist = [s.replace('\n','') for s in f] f.close() #引数に渡したIPが割当IPリストに該当するならば引数に渡したIPとそれを含むブロックをタプルで返す def matchCnIP(ip): for ips in cniplist: if ipaddress.IPv4Address(ip) in ipaddress.IPv4Network(ips): return (ip,ips,) #怪しげIPリストを上の関数を使って怪しげIPタプルリストにする reslist = [matchCnIP(i) for i in iplist] #Noneを除去(指定した国じゃないとタプルじゃなくNoneを返すから) reslist = [rs for rs in reslist if rs != None ] #怪しげIPリストの割当ブロック部分のみを重複のしないリストに変換 seglist = list(set( [rs[1] for rs in reslist])) #それを標準出力に表示 for seg in seglist: print(seg) if __name__ == '__main__': main()
これはipaddressモジュールのIPアドレスのプレフィックス表示している文字列からIPアドレスのリストを返す関数があって
更にそこに含まれているかどうかというのも簡単にin演算子で行えるそうだ。
上記のプログラムを実行すると、指定した割当ブロックのリストに対象があれば
そこから割当ブロックを表示するので、それをIPTABLESにDROP指定すればいいでしょう。
ただし、善良な一般市民のアクセスも遮断することになるので注意。
ブログが日本人を対象にしているし、
インターネットの中心地アメリカのアクセスは禁止にしたら検索エンジンのクローラとか追い出すことになるけれど
そういうところ以外の外国だったら余り問題無いと思う。
ちなみにまあまあ実行時間かかった。
2分くらいかな。
中国の割当ブロックのリストだけでも3600行を超えていて、IPにしたら数億になるので検索に時間がかかるんだと思う。
言ってみれば線型探索だからね。
ipaddressモジュールには比較演算子を使える関数もあったので、
2分探索などを実装すればだいぶ早くなると思う。
またsocketモジュールにはIPからホスト名に逆引きする関数があるので、
怪しげなIPすべてじゃなくて、怪しげドメインを指定してそれが含まれるブロックを検索ということも簡単にできる。