ケータイやクローラの判別などに使えるmod_cidr_lookupを公開しました
mod_cidr_lookupというApacheモジュールを公開しました。
mod_cidr_lookupは、アクセスしてきたクライアントのIPアドレスが、指定したCIDRブロック群のいずれかにマッチするかどうかを判別するApacheモジュールです。
Apache 2.0と2.2系に対応しています。
マッチした結果は、環境変数 (X_CLIENT_TYPE) とHTTPリクエストヘッダ (X-Client-Type) にセットするので、Apache自身とバックエンドのWebアプリの両方で同じ情報を参照することができます。
このモジュールを使うメリット
- 簡単にクライアントの種類を知ることができる
- 判別処理はモジュールが行ってくれるので、のちほどお見せるように、Webアプリやhttpd.confでは環境変数やリクエストヘッダの値を参照するだけでクライアントの種類を知ることができます
- 処理時間の短縮化
- 同じようなCIDR判定の処理を、Apache (のAllow fromの羅列やRewriteCondの羅列) とWebアプリの両方でやるのは無駄です。このモジュールを使えば、判定処理は1つで済み、判定結果を使う場合は環境変数などを参照するだけで済むので効率的です
- CIDRデータの一元管理
- 異なるレイヤ(ApacheとWebアプリとか)のそれぞれで別々にいくつもCIDRデータを管理していると、更新漏れや食い違いの可能性があります
利用例
※IPアドレス帯域の正確性などについては、情報提供元にお問い合わせください。
例1: クローラからのアクセスは別のサーバにreverse proxyする
モバイル用のクローラには、送信元IPアドレスを公開しているものがあります。
これらの情報を使って、クローラからのアクセスを判別し、クローラはクローラ専用のサーバクラスタへreverse proxyで振り分けることができます。
mod_rewriteを使った例はこうなります。
RewriteCond %{ENV:X_CLIENT_TYPE} ^crawler-.* # (2) 環境変数X_CLIENT_TYPEがcrawler-で始まるときだけ。 RewriteRule ^/(.*)/$ http://4crawler/$1 [P,L] # (1) http://4crawlerにreverse proxyする
例2: 特定のクライアントからのアクセスを許可する
Allowディレクティブでは環境変数を参照することができるので、例えば自社のIPアドレス帯からのアクセスを許可したり、
Allow from env=my_company
モバイルキャリアからのアクセスを許可したり、
SetEnvIf X_CLIENT_TYPE "^(docomo|au|softbank)" is_mobile # 3キャリアのいずれかの場合は新たに環境変数is_mobileをセットする Allow from env=is_mobile
ということができます。
またWebアプリから、環境変数を参照したり、
$type = getenv("X_CLIENT_TYPE"); # docomo, au, softbank, ...
リクエストヘッダを参照することによって、
$r->header_in("X-Client-Type"); # docomo, au, softbank, ...
$headers = apache_request_headers(); $headers["X-Client-Type"]; # docomo, au, softbank, ...
判別した情報を参照することができます。
インストール、設定
インストールや設定方法など詳しい情報は、プロジェクトサイトをご覧ください。
参考
CIDRの判定ロジックについては以下のエントリが参考になると思います。 より適したロジックがあるという方はご連絡お待ちしております!
- #1 ひろせの場合 - IP::CountryとAPRを使ってみた
- #2 安井の場合: バイナリサーチのあれとこれ
- #3 hamanoの場合: あ ありのまま 今 起こった事を話すぜ!『コードコンペだと思ったらゴルフコンペだった』な(ry
- #4 稲田の場合: hamanoが倒せない
(ひ)とKラボの仲間たち
トラックバックURL
この記事へのコメント
こちらに書くべきか迷ったのですが、適切な場所が見つかりませんでしたので、質問させてください。
早速使わせて頂こうかと思いまして、
例に載っていますSetEnvIfディレクティブを使った判定を行ってみたのですが、どうしても環境変数がセットできませんでした。
User-AgentやRemote_Addrを利用してのセットはできていますので、
SetEnvIfの利用方法の間違いではなさそうです。
また、アプリケーション(PHP)では、Apache EnvironmentでX_CLIENT_TYPE、
HTTP Headers InformationでX-Client-Type
に値が入っているのを確認しましたので、
正常に動いているのは間違いなさそうです。
apacheのモジュールのロードされる順番やconfファイルの記述順番を変えたりしてみたのですが、
セットできませんでした。
環境はCentOS4.6にソースから入れたapache2.2.9です。
お返事が遅くなってしまいましたが、報告いただいた問題を修正したバージョン1.1を先ほどリリースいたしましたので、お試しいただけるとうれしいです。
今回はご報告ありがとうございました!!
http://lab.klab.org/wiki/Mod_cidr_lookup
Apacheモジュールの勉強を始めたばかりなので、間違った指摘かもしれませんが、気になった点を相談させていただきます。よろしくお願いします。
1. fook登録のところで、post_read_requestとheader_parserそれぞれにfixup_cidr_infoを登録していますが、どちらかに1回で良いのでは?と思いました。両方で呼び出す必要があるのでしょうか?
>ap_hook_post_read_request(fixup_cidr_info, NULL, suc, APR_HOOK_MIDDLE);
>ap_hook_header_parser(fixup_cidr_info, NULL, suc, APR_HOOK_MIDDLE);
# 私の環境で、試しにどちらか片方をコメントアウトしても、仕様通りに動きました。
2. post_read_requestと、header_parserはDECLINED以外が返ると、そのフェーズを終了すると認識しています。fiup_cidr_infoはOKを返していますが、他のモジュールと一緒に使うときに問題はないでしょうか?
# こちらは検証していません。
どうぞよろしくお願いします。
1. post_read_requestにもregisterするようにしたのは最近(mod_cidr_lookup-1.2)でして、このようにした理由は、サーバ設定とバーチャルホストのコンテキストでもmod_cidr_lookupでセットした環境変数をSetEnvIfで参照できるようにするためです。
modules/metadata/mod_setenvif.cではpost_read_requestとheader_parserにregisterしているので、mod_cidr_lookupもそれにあわせるようにregisterするhookポイントを追加した次第です。
2. post_read_requestとheader_parserはRUN_ALL型なので、OKかDECLINEDを返せば処理は継続します。
http://dsas.blog.klab.org/archives/50626863.html
http://d.hatena.ne.jp/dayflower/20081029/1225266220
が参考になると思いますのでよかったら見てみてください。
また何かありましたらお気軽にコメントくださいませ!!
勉強していて参照できる日本語の情報がそれほど多くないので、大変参考になりました!
※そんなことないのでしょうか(汗)
ありがとうございました。
矢田部@ファルコンと申します。
当方、RHL-AS2.0上でApache2.0.63×Tomcat(mod_jk連携)
という環境です。
最新バージョンの「mod_cidr_lookup-1.2」を導入したところ、
以下に記載の事象が発生致しましたのでご報告致します。
ご確認のほど宜しくお願い致します。
【発生事象】
CIDRファイルに記述されていない接続元IPでのリクエスト時に、
動的ページ(Servlet /JSP)が表示できない。
静的コンテンツは、表示・画面遷移ともに可能。
エラーログ上に下記のセグメンテーションフォルトエラーが発生している。
[Mon Nov 01 11:53:48 2010] [notice] child pid 8331 exit signal Segmentation fault (11)
【原因】
ヘッダフィールドにValue(=マッチしたCIDRファイル名)を設定する際に、
本来であれば設定されないNULL値が設定される為。
427行目の判定処理が正しく評価されない為に、不正値が設定されるようです。
if (trie_root->name == NULL)--- >> ☆☆☆☆☆
return DECLINED;
静的コンテンツリクエスト時には、このフェーズでセグフォルトが発生しても
特に問題無く画面表示・遷移ができます。
※この為PHPやCGI環境等では実害が無いと思われます。
mod_jk連携がある場合、この時点で子プロセスが亡くなると、
連携ができなくなるので、画面が表示できません。
【対応】
下記の通り、判定処理を変更したところ、事象が発生しなくなりました。
判定の際、ap_get_module_configで取得した構造体を直に参照するのではなく、
一度変数に取得してから値チェックを実行する事で想定通りの動作となります。
type = lookup_cidr(r, trie_root);
if ( type == NULL || strlen(type) < 1 ){ --> 変数に取得の上チェック
return DECLINED;
}