大量のIPが特定のCIDRに含まれているか調べるツールを作った
アクセスログとかに書かれている大量のIPアドレスが、特定のCIDRに含まれるかどうかを高速にチェックしたくなったので、専用CLI checkcidr を作った たぶん誰かすでに作ってると思うんだけれど、最近コード書いてなかったし、気晴らしも兼ねて自分でガリガリ書いた
背景
アクセスログにはアクセス元IPアドレスが記録されており、それである程度アクセス元が特定できる
サービスを運用してるとほぼ毎日なんらかの不正アクセスが試行されている
で、怪しいアクセスをまとめてブロックできたらいいんだけれど、誤ってブロックしてはいけないものもブロックするとまずい
Googleの検索エンジン上にサイトを乗せたり、サイトに対してなんらかの評価をして、検索結果の上位に表示するために定期的にクローリングをしているBot
このクローラーを誤ってブロックしてしまうと、下手したら検索エンジン上にサイトが出てこなくなってSEOが悪化する可能性がある
Google のクローラーからのアクセスの場合は UserAgent もそれとわかる名前になっているので、UserAgent でブロックしないようにしても良いんだけれど、攻撃者が UserAgent を Googlebot に偽装する可能性も 0 ではない
という感じで、なんらかの CIDR に特定の IP アドレスが含まれるか調べたくなる機会は多々ある
が、アクセスログに記録されるIPアドレスは膨大で、且つ検査したい CIDR も1つではなく、何十個も存在したりする
必然的に CIDR 数 x IP アドレス数 の総当たりでチェックして、見つかったら break 、って感じにしないととんでもなく時間がかかる
こういった処理をそれなりに効率よく処理できて、進捗も確認できて、いろんな書式(JSONとか)で出力できるツールが欲しかったので、今回作った 使い方
CIDRだけ書いたファイル、IPだけ書いたファイルを引数として渡す
CIDRファイル
code:cidr.list
1.2.0.0/16
1.3.0.0/16
IPファイル
code:ip.list
1.2.1.2
1.2.1.3
1.3.1.2
1.3.1.3
実行するとこう
code:bash
$ ./checkcidr cidr.list ip.list
ip_file=ip.list cidr=1.2.0.0/16 ip=1.2.1.2 contains=true
ip_file=ip.list cidr=1.3.0.0/16 ip=1.2.1.2 contains=false
ip_file=ip.list cidr=1.2.0.0/16 ip=1.2.1.3 contains=true
ip_file=ip.list cidr=1.3.0.0/16 ip=1.2.1.3 contains=false
ip_file=ip.list cidr=1.2.0.0/16 ip=1.3.1.2 contains=false
ip_file=ip.list cidr=1.3.0.0/16 ip=1.3.1.2 contains=true
ip_file=ip.list cidr=1.2.0.0/16 ip=1.3.1.2 contains=false
ip_file=ip.list cidr=1.3.0.0/16 ip=1.3.1.2 contains=true
JSON書式で出力
code:bash
$ ./checkcidr -style json cidr.list ip.list | head -n 13
[
{
"ip_file": "ip.list",
"cidr": "1.2.0.0/16",
"ip": "1.2.1.2",
"contains": true
},
{
"ip_file": "ip.list",
"cidr": "1.3.0.0/16",
"ip": "1.2.1.2",
"contains": false
},
JSON配列の出力の場合、すべての行の処理が完了するまで出力できなくて不便なので、1行JSON(JSON Stream)として出力するオプションもつけた。この形式は jq でも処理できるし、割とよくある方式
code:bash
$ ./checkcidr -style json_stream cidr.list ip.list
{"ip_file":"ip.list","cidr":"1.2.0.0/16","ip":"1.2.1.2","contains":true}
{"ip_file":"ip.list","cidr":"1.3.0.0/16","ip":"1.2.1.2","contains":false}
{"ip_file":"ip.list","cidr":"1.2.0.0/16","ip":"1.2.1.3","contains":true}
{"ip_file":"ip.list","cidr":"1.3.0.0/16","ip":"1.2.1.3","contains":false}
{"ip_file":"ip.list","cidr":"1.2.0.0/16","ip":"1.3.1.2","contains":false}
{"ip_file":"ip.list","cidr":"1.3.0.0/16","ip":"1.3.1.2","contains":true}
{"ip_file":"ip.list","cidr":"1.2.0.0/16","ip":"1.3.1.2","contains":false}
{"ip_file":"ip.list","cidr":"1.3.0.0/16","ip":"1.3.1.2","contains":true}
進捗表示
検査処理のカウントが2500回を超えるたびに . を出力する処理を入れている
100,000回に到達すると改行する
総当たりチェックをすると試行回数がとんでもない数になる場合があるので、1回ずつ進捗を出すのではなく、数千回に1回だけ進捗を出すようにしている
画面がドットだらけ改行だらけとかになるとノイズになってしまう
こういう進捗表示は、ノイズにならないように、でも動作していることが視覚的に分かる良いバランスのとこを見つける必要がある
進捗表示のためだけに依存ライブラリを増やすのも嫌なので、基本的に簡単な進捗表示で良いときはいつもこのスタイルで実装している
今回は一般公開のCLIで採用したが、社内専用ツールを実装するときにも使える手法なのでおすすめ
これらは標準エラー出力に書き込むようにしてるので、検査結果出力の邪魔にはならないし、オプションで進捗表示を消せるようにしている
code:bash
$ ./checkcidr testdata/cidr_1.txt testdata/ip_1.txt > /dev/null
........................................ 100000
........
実装時間
3時間とちょっとでだいたい実装できた
テストコードを全然整備できてないので、そこは追々やる