2006年06月21日

キャリアのゲートウェイアドレス管理 -- apache module hacking!

はてなブックマークに登録


KLab は,元々が携帯向けのサービスを主とした会社で,現在も多くの携帯サイトを運用しています.それらのサイトでは,諸般の事情により携帯以外からのアクセスを制限しています.これは,アクセス元アドレスをキャリアのゲートウェイアドレスに限定することで実現しています.この制限は,Firewall でも行っているのですが,同時に Apachemod_access を使って,Web サーバのレベルでも制限しています.

KLab が運用しているサイトは複数あって,それぞれ対応するキャリアが異なります.ですので,それぞれのサイト毎にどのキャリアのアドレスを許可するか設定する必要があります.ところが,このキャリアのゲートウェイアドレスは,時々増減します.もちろん,その都度そのキャリア向けにサービスを行っている全サイトの設定を変更する必要があります.

当初は,サイト毎に Allow from の設定を記述していましたので,キャリアアドレスの追加がある度に,そのアドレスの Allow 設定を追加していました.しかしながら,当然サイトの数が増えるに従ってこの作業はひどく大変になる上,ミスの可能性がぐっと増えてきました.そこで,各キャリアのゲートウェアドレスは一つのファイルで管理して,必要に応じてそれを Include ディレクティブを使って各々のサイトの設定に取り込む形にできないかと考えました.その当時は Apache の 1.3 系列を使っていたのですが,しかしながら 1.3 系列の Apache ではどうにも上手くいきませんでいた.ということで,新たに Apache のモジュールを作ることにしました.


さて,最初に考えた方法は,キャリア毎にそのゲートウェイアドレスを Allow するファイルを用意して,<Directory> や <Location> ディレクティブの中で Include ディレクティブを使って取り込もうとしました.つまり,このような形です

  ServerName keitai.klab.org
DocumentRoot /var/www/htdocs/

<Directory "/">
Order Deny,Allow
Deny from all
Include allow.docomo
</Directory>
allow.docomo の中には
  Allow from 10.10.10.0/24
のように記述されています.

ところが 1.3 系列の Include ディレクティブだと,Allow from だけを記述した外部ファイルだと,上手く Include 処理してくれませんでした(2.0系列になって,これができるようになっています).これが,<Directory> ディレクティブも含んだ形になっていればちゃんと期待通りに Include できます.つまりこのような形です.
  ServerName keitai.klab.org
DocumentRoot /var/www/htdocs/

Include allow.docomo
allow.docomo の中には
  <Directory "/">
Order Deny,Allow
Deny from all
Allow from 10.10.10.0/24
</Directory>
のように記述します.


この設定は,少々制限があるとはいえ,一つだけのキャリア向けのサイトであればまずまず上手く動作します.けれども,同時に二つのキャリアをサポートしようとして

  ServerName keitai.klab.org
DocumentRoot /var/www/htdocs/

Include allow.docomo
Include allow.au

と設定すると,allow.docomo の設定は無視されて,allow.au の設定だけが生きます.つまり,これでは DoCoMo からのアクセスができません.

その根拠を求めて mod_access のソースを見てみたところ,あるディレクトリに対する設定が複数あった場合,先の設定は捨てて,最後の設定だけを使うようになっていました.これは,おそらく複数の設定がぶつかった場合にどうするか,というポリシーを決めるのが難しいためにそうなっているのだと思われます.例えば,1つめの設定で,Order allow,deny となっていて,2つめの設定が Order deny,allow になっているような状況です.

mod_access はアクセス制限のためのモジュールですから,このポリシーは迂闊には決められませんし,不特定多数の環境で使われることが前提であることから,利用上の手間が増えたとしても安全サイドに倒した設計にするのは理に叶っています.逆に言えば,限定された状況で,なにが起こるのかを理解した上で使用する分には,複数の設定を混合する,というのは十分有りです.というわけで,mod_access を少し改造してみました.量も多くないですので,以下に diff を掲載します.これは,1.3.29 の Apache に付属している mod_access.c をベースにしています.


--- mod_access.c 2003-02-04 02:13:27.000000000 +0900
+++ mod_access2.c 2004-04-21 17:09:56.000000000 +0900
@@ -100,7 +100,7 @@
array_header *denys;
} access_dir_conf;

-module MODULE_VAR_EXPORT access_module;
+module MODULE_VAR_EXPORT access2_module;

static void *create_access_dir_config(pool *p, char *dummy)
{
@@ -116,6 +116,24 @@
return (void *) conf;
}

+static void *merge_access_dir_config(pool *p, void *parent_conf, void *newloc_conf)
+{
+ access_dir_conf *pc = (access_dir_conf *)parent_conf;
+ access_dir_conf *nc = (access_dir_conf *)newloc_conf;
+
+ access_dir_conf *ret =
+ (access_dir_conf *) ap_pcalloc(p, sizeof(access_dir_conf));
+ int i;
+
+ for (i = 0; i < METHODS; ++i)
+ ret->order[i] = pc->order[i];
+
+ ret->allows = ap_append_arrays(p, pc->allows, nc->allows);
+ ret->denys = ap_append_arrays(p, pc->denys, nc->denys);
+
+ return (void *)ret;
+}
+
static const char *order(cmd_parms *cmd, void *dv, char *arg)
{
access_dir_conf *d = (access_dir_conf *) dv;
@@ -260,11 +278,11 @@

static const command_rec access_cmds[] =
{
- {"order", order, NULL, OR_LIMIT, TAKE1,
+ {"applyorder", order, NULL, OR_LIMIT, TAKE1,
"'allow,deny', 'deny,allow', or 'mutual-failure'"},
- {"allow", allow_cmd, &its_an_allow, OR_LIMIT, ITERATE2,
+ {"mergeallow", allow_cmd, &its_an_allow, OR_LIMIT, ITERATE2,
"'from' followed by hostnames or IP-address wildcards"},
- {"deny", allow_cmd, NULL, OR_LIMIT, ITERATE2,
+ {"mergedeny", allow_cmd, NULL, OR_LIMIT, ITERATE2,
"'from' followed by hostnames or IP-address wildcards"},
{NULL}
};
@@ -351,7 +369,7 @@
int method = r->method_number;
access_dir_conf *a =
(access_dir_conf *)
- ap_get_module_config(r->per_dir_config, &access_module);
+ ap_get_module_config(r->per_dir_config, &access2_module);
int ret = OK;

if (a->order[method] == ALLOW_THEN_DENY) {
@@ -387,12 +405,12 @@



-module MODULE_VAR_EXPORT access_module =
+module MODULE_VAR_EXPORT access2_module =
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
create_access_dir_config, /* dir config creater */
- NULL, /* dir merger --- default is to override */
+ merge_access_dir_config, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
access_cmds,



変更点は次の二つで,

  • 複数の設定を混合するための関数(merge_access_dir_config())を用意

  • mod_access と平行利用するために,ぶつかる名前の変更


です.

Apache には,一つのディレクトリに対して同一のモジュールが提供する設定が複数なされていた場合に,指定された関数を呼び出す機能が元から備わっています.オリジナルの mod_access ではこの関数が定義されていなかったため,複数の設定があった場合に,先に定義されたものは単に捨てられていました.我々の目標を実現するには,この call back(merge_access_dir_config()) 関数を定義してやって Apache に登録すれば済みます.

二つめの,「ぶつかる名前の変更」は具体的にはディレクティブ名と内部で用いられるモジュールの識別名です.ディレクティブ名は,Order, Allow, Deny をそれぞれ ApplyOrder, MergeAllow, MergeDeny にしました.モジュール名は,access_moduleaccess2_module に変更しました.


実は,Apache のモジュールをいじったのはこれが初めてだったのですが,モジュールを開発する上でApache というのは,とても洗練されたプラットフォームになっている,ということがよく分かりました.今回追加した merge_access_dir_config() はたったの20行足らずで,しかも本質的には元の mod_access.c のコードを変更することなく,追加だけで済んでいます.そのため,バグの混入する可能性も低く,気楽に改造できました.また,一からモジュールを開発することを考えた場合でも,Apache の処理の各ステージに簡単に新たな機能を組み込むことができるようになっていますので,メモリ管理などのユーティリティも整備されていることも相まって,思ったよりも楽に開発できそうです.

klab_gijutsu2 at 22:35│Comments(0)TrackBack(0)apache 

トラックバックURL

この記事にコメントする

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