IPアドレスブロックを集中管理する方法と、その活用法
やりたいこと
実現方法
スクリプトを2つ書きました。いずれもgithubにあります。
図も用意しました。
update-mobilejp-cidr
キャリアのサイトをスクレイピングして、指定されたディレクトリにその情報を書き出します。
その形式は、1行1アドレスブロック、コメントは "#" です。
$ cat plain/docomo 124.146.174.0/24 124.146.175.0/24 ... $ cat plain/softbank 123.108.236.0/24 123.108.237.0/27 ...
generate-cidr-files
指定されたディレクトリからIPアドレスブロックを読み込み、いくつかの形式に変換して書き出します。
読み込める形式は、1行1アドレスブロック、コメントは "#"、つまり update-mobilejp-cidr が書き出す形式です。
手動で変更を反映するIPアドレスブロックファイル(図中だと「office」「home」)も、同じ形式で update-mobilejp-cidr が書き出すのとディレクトリに置いておきます。
つまり、generate-cidr-filesは、いろいろな方法(自動、手動)で変更されるがその形式は統一されたファイル群が収められている、とあるディレクトリ (/etc/ip.d/plain/) からデータを読み込みます。
読み込んだデータは、情報はそのままに形式を違えてまた別なディレクトリにファイルとして書き出します。
今のところ、サポートしているのは YAML (単一ファイル) と Pair (CIDR ブロック名;の形式で単一ファイル) の2種類です。
$ cat yaml/cidr --- docomo: - 124.146.174.0/24 - 124.146.175.0/24 ... softbank: - 123.108.236.0/24 - 123.108.237.0/27 ... office: - XXX.XXX.XXX.XXX home: - YYY.YYY.YYY.YYY $ cat pair/cidr 124.146.174.0/24 docomo; 124.146.175.0/24 docomo; ... 123.108.236.0/24 softbank; 123.108.237.0/27 softbank; ... XXX.XXX.XXX.XXX office; YYY.YYY.YYY.YYY home;
運用
意識して変更するのは /etc/ip.d/plain/ の下のファイルのみです。
「office」「home」のように手動更新のものは、更新したのち、手動で generate-cidr-files を実行して他の形式に反映します。
携帯3キャリアのは、cronで update-mobilejp-cidr を定期的に実行するようにすれば、plain/* の更新は自動化できますし、update-mobilejp-cidr は更新があった場合は終了コード 0 で終了するので、このようにすれば必要に応じて generate-cidr-files を実行することができます。
update-mobilejp-cidr -o /etc/ip.d/plain/ && generate-cidr-files -i /etc/ip.d/plain/ -o /etc/ip.d/
活用法
plain
mod_cidr_lookup の入力データとして使えます。
mod_cidr_lookupは、当該IPアドレスがどのIPアドレスブロックに属するかを非常に高速に検索できる点、IPアドレスブロックの数が多くなっても検索スピードが劣化しない点が特長です。(実測していませんが) 原理的に、ApacheのAllowディレクティブを使って大量の Allow from XXX を列挙するのより、かなり高速なはずです。
mod_cidr_lookup の使い方は、そのプロジェクトページに詳しいです。
pair
Nginxのgeoモジュール の入力データとして使えます。
追いきれてませんが、ざっくり実装を見た感じでは、geoモジュールはひとつの木構造にIPアドレスブロック群の情報を格納し、検索しているようなので、遅くはないと思います。Nginxで、Apacheのmod_cidr_lookup相当のことを行うには、geoモジュールを使うのはリーズナブルな選択だと思います。
使い方はこんな感じです。
http { # HTTPクライアントのIPアドレスについて、/etc/ip.d/pair/cidr の # データにマッチすればそのIPアドレスブロック名を、マッチしなけ # れば (defaultで指定している) 「no」を、$clienttype に格納します。 ... geo $clienttype { default no; include /etc/ip.d/pair/cidr; } # 変数 $clienttype は、 # ログに出力したり、 log_format full '$msec [$time_local] $remote_addr $remote_user ' '"$request" $status ' '$request_length $body_bytes_sent $request_time ' '"$http_referer" "$http_user_agent" $clienttype'; # アクセス制御に使えます。 server { ... location / { # モバイルとオフィス以外からは拒否 if ($clienttype !~* '^(docomo|softbank|ezweb|office)$') { return 403; } ...