例えば
deny 192.168.1.0; deny 192.168.1.1; (snip) deny 192.168.1.255; deny 192.168.1.256;
と
deny 192.168.1.0/24;
だと後者の方がレスポンスタイムが短くなるようです。(メモリ使用量も抑えられる)
実験してみる
二つのconfigでそれぞれ計測してみます
# configにdenyの192.168.1.1/20を設定 $ for i in $(seq 100); do curl localhost -s -o /dev/null -w "%{time_starttransfer}\n"; done | awk '{sum+=$1} END {print sum}' 0.34399 # configにdenyの192.168.1.1 ~ 192.168.15.255を設定 for i in $(seq 100); do curl localhost -s -o /dev/null -w "%{time_starttransfer}\n"; done | awk '{sum+=$1} END {print sum}' 39.6531
すごい差が出ました!!こんな例はあんまないと思いますがわかりやすくするためにやってます。。。w
なぜ
実際にdenyをした際に動くソースは以下。リクエストごとにアクセス元IPとdenyで設定し内容が一致するかを見るわけだが探索自体は線形探索を用いて実装されておりconfigで設定したdenyの行数分ループしていく実装となっている。denyが10000行あったら毎回この探索が実施されるという。CIDR形式の指定だとアクセス元IPとconfigに記載したCIDRのビット演算とルールのIPの一回の比較で済むので早いという仕組み。
static ngx_int_t ngx_http_access_inet(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf, in_addr_t addr) { ngx_uint_t i; ngx_http_access_rule_t *rule; rule = alcf->rules->elts; // allow, denyのルールの行数ごとに探索を実施する(netltsは配列内に格納された要素の数) for (i = 0; i < alcf->rules->nelts; i++) { ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "access: %08XD %08XD %08XD", addr, rule[i].mask, rule[i].addr); if ((addr & rule[i].mask) == rule[i].addr) { // 「アクセス元IPとconfigに設定されたCIDRとのビットAND」とconfigに設定されたIPの比較 return ngx_http_access_found(r, rule[i].deny); // 一致していたらdenyフラグを立てて検出関数を呼び出す } } return NGX_DECLINED; // 一致するものがなければ処理継続を呼び出し元に戻す } // アクセスルールの構造体 typedef struct { in_addr_t mask; // configのCIDR in_addr_t addr; // configのアドレス ngx_uint_t deny; /* unsigned deny:1; */ } ngx_http_access_rule_t; // ルール格納用の配列 typedef struct { ngx_array_t *rules; /* array of ngx_http_access_rule_t */ ngx_array_t *rules6; /* array of ngx_http_access_rule6_t */ ngx_array_t *rules_un; /* array of ngx_http_access_rule_un_t */ } ngx_http_access_loc_conf_t;
実際にNGX_HTTP_FORBIDDENを返すのはこっちの関数
static ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny) { ngx_http_core_loc_conf_t *clcf; // 今回のケースだとdenyフラグは有効化されて呼び出される if (deny) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "access forbidden by rule"); } // クライアントへは403が返る return NGX_HTTP_FORBIDDEN; } return NGX_OK; }
まとめ
CIDR 形式の方が計算量も少ないので良さそうというのとあとはメモリ効率も違うのでまとめれるならCIDR形式でガットかけると良さそうでした。ちなみに線形探索なのでよくアクセスしてくるIPを上に書くことでループ回数を抑えるテクニックがあることにも気づきました(そこまでやる必要がある機会はよくわからないけど...)