2008年08月25日

ケータイやクローラの判別などに使える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の判定ロジックについては以下のエントリが参考になると思います。 より適したロジックがあるという方はご連絡お待ちしております!


(ひ)とKラボの仲間たち

klab_gijutsu2 at 07:00│Comments(7)TrackBack(0)codecompe 

トラックバックURL

この記事へのコメント

1. Posted by es   2008年09月05日 19:18
お世話になります。
こちらに書くべきか迷ったのですが、適切な場所が見つかりませんでしたので、質問させてください。

早速使わせて頂こうかと思いまして、
例に載っています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です。
2. Posted by (ひ)   2008年09月12日 19:44
使っていただいてありがとうございます!

お返事が遅くなってしまいましたが、報告いただいた問題を修正したバージョン1.1を先ほどリリースいたしましたので、お試しいただけるとうれしいです。

今回はご報告ありがとうございました!!

http://lab.klab.org/wiki/Mod_cidr_lookup
3. Posted by masutaro   2008年10月23日 08:51
4 初めましてmasutaroと申します。

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を返していますが、他のモジュールと一緒に使うときに問題はないでしょうか?

# こちらは検証していません。

どうぞよろしくお願いします。
4. Posted by ひろせ   2008年10月31日 02:58
masutaroさん、コメントありがとうございました!

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
が参考になると思いますのでよかったら見てみてください。


また何かありましたらお気軽にコメントくださいませ!!

5. Posted by masutaro   2008年11月06日 10:01
ひろせさん、ご丁寧な回答ありがとうございました。私の勉強不足でした。

勉強していて参照できる日本語の情報がそれほど多くないので、大変参考になりました!
※そんなことないのでしょうか(汗)

ありがとうございました。
6. Posted by yatabe@falcon   2010年11月02日 18:02
お世話になります。
矢田部@ファルコンと申します。

当方、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値が設定される為。
7. Posted by yatabe@falcon   2010年11月02日 18:55
(つづき)
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;
}

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント