mod_mrubyとmruby-geoipを使って地理情報でアクセス制御

[@matsumotory](http://hb.matsumoto-r.jp/) さん謹製の mod_mruby と mruby-geoip で地理情報を使ってアクセス制御してみた。

※ 本記事は [mod_mrubyとmruby-geoipを使って地理情報でアクセス制御](http://atani.github.io/2015/07/mod-mrubyとmruby-geoipを使ってアクセス制御/) にも記載してます。

## スペック

```
# more /etc/redhat-release
CentOS Linux release 7.1.1503 (Core)

# uname -r
3.10.0-229.7.2.el7.x86_64
```

<!-- more -->

### apacheをインストール

```
yum -y install httpd httpd-devel
```

### 必要パッケージをインストール

```
yum -y install git vim-enhanced ruby gcc GeoIP-devel GeoIP-update
```

パッケージの説明をかるく書いておく。

|Package|Description|
|:---------|:---------|
|git|mod_mrubyをgitリポジトリから取得するため|
|vim-enhanced|なくてもよい|
|ruby|mod_mruby入れるのに必要|
|gcc|mod_mrubyコンパイルするために必要|
|GeoIP-devel|mruby-geoipコンパイルするために必要|
|GeoIP-update|GeoIPデータベースのアップデートするツール|

### mod_mrubyをcloneする。

```
git clone git://github.com/matsumoto-r/mod_mruby.git
```

### build_config.rbを修正

今回は、[mruby-geoip](https://github.com/matsumoto-r/mruby-geoip) を導入したので、入れた。さらにredis,vedisは使わないのでコメントに。

```diff
# diff -ur build_config.rb_backup build_config.rb
--- build_config.rb_backup      2015-07-03 13:32:27.017667686 +0900
+++ build_config.rb     2015-07-03 13:37:33.756710824 +0900
@@ -15,13 +15,14 @@
   conf.gem :github => 'iij/mruby-pack'
   conf.gem :github => 'mattn/mruby-json'
   conf.gem :github => 'mattn/mruby-onig-regexp'
-  conf.gem :github => 'matsumoto-r/mruby-redis'
-  conf.gem :github => 'matsumoto-r/mruby-vedis'
+  #conf.gem :github => 'matsumoto-r/mruby-redis'
+  #conf.gem :github => 'matsumoto-r/mruby-vedis'
   conf.gem :github => 'matsumoto-r/mruby-sleep'
   conf.gem :github => 'matsumoto-r/mruby-userdata'
   conf.gem :github => 'matsumoto-r/mruby-uname'
   conf.gem :github => 'matsumoto-r/mruby-cache'
   conf.gem :github => 'matsumoto-r/mruby-mutex'
+  conf.gem :github => 'matsumoto-r/mruby-geoip'

   # mod_mruby extended class
   conf.gem '../mrbgems/mod_mruby_mrblib'
```

### ビルドしよう！

```
sh build.sh
```

正常にビルドが終わると、以下のように `successful` と表示される。

```txt
*** Warning: Linking the shared library src/mod_mruby.la against the
*** static library ./mruby/build/host/lib/libmruby.a is not portable!
mod_mruby building ... Done
build.sh ... successful
```

### apacheに組み込む。

```
make install
```

実行結果はこちら

```
# make install
/usr/bin/apxs -i -a -n 'mruby' /home/atani/mod_mruby/src/.libs/mod_mruby.so
/usr/lib64/httpd/build/instdso.sh SH_LIBTOOL='/usr/lib64/apr-1/build/libtool' /home/atani/mod_mruby/src/.libs/mod_mruby.so /usr/lib64/httpd/modules
/usr/lib64/apr-1/build/libtool --mode=install install /home/atani/mod_mruby/src/.libs/mod_mruby.so /usr/lib64/httpd/modules/
libtool: install: install /home/atani/mod_mruby/src/.libs/mod_mruby.so /usr/lib64/httpd/modules/mod_mruby.so
Warning!  dlname not found in /usr/lib64/httpd/modules/mod_mruby.so.
Assuming installing a .so rather than a libtool archive.
chmod 755 /usr/lib64/httpd/modules/mod_mruby.so
[activating module `mruby' in /etc/httpd/conf/httpd.conf]
```

### GeoIPのデータベースを取得する。

初期のGeoIPデータは古いので、`geoipupdate` で最新のデータを取得する。

```
 ll /usr/share/GeoIP/
total 17696
lrwxrwxrwx. 1 root root       17 Jul  3 13:56 GeoIP.dat -> GeoIP-initial.dat
-rw-r--r--. 1 root root  1362244 Jun 10  2014 GeoIP-initial.dat

# geoipupdate
/usr/share/GeoIP/GeoLiteCountry.dat can't be opened, proceeding to download database
Updating /usr/share/GeoIP/GeoLiteCountry.dat
Updated database
/usr/share/GeoIP/GeoLiteCity.dat can't be opened, proceeding to download database
Updating /usr/share/GeoIP/GeoLiteCity.dat
Updated database
```

## mruby-geoipの設定

ここからはmruby-geoipを利用するための設定をしよう。

```
mkdir /etc/httpd/hook
```

### httpd.conf

```
mrubyChildInitMiddle /etc/httpd/conf.d/geo_init.rb
<Location />
  mrubyAccessCheckerLast /etc/httpd/conf.d/geo_check.rb
  mrubyHandlerMiddle /etc/httpd/conf.d/geo_handler.rb
</Location>
```

※ `geo_check.rb` を `mrubyAccessCheckerMiddle` で処理させようとすると地理情報の値がうまく値が取れなかった。

### /etc/httpd/conf.d/geo_init.rb

```
Userdata.new("geoip_#{Process.pid}").geoip = GeoIP.new("/usr/share/GeoIP/GeoLiteCity.dat")
```

### /etc/httpd/conf.d/geo_check.rb

```
Server = get_server_class

geoip = Userdata.new("geoip_#{Process.pid}").geoip
geoip.record_by_addr Server::Connection.new.remote_ip

if geoip.country_code != "JP"
  Server.return Server::HTTP_FORBIDDEN
end
```

### /etc/httpd/conf.d/geo_handler.rb

```
Server = get_server_class

r = Server::Request.new
c = Server::Connection.new
geoip = Userdata.new("geoip_#{Process.pid}").geoip

r.content_type = "text/html"
Server.echo "<HEAD><TITLE>your information</TITLE></HEAD><BODY>"
Server.echo "Your IP Address is #{c.remote_ip}<br>"
Server.echo "Your Country Code is #{geoip.country_code}<br>"
Server.echo "Your city is #{geoip.city}<br>"
Server.echo "Your region is #{geoip.region}<br>"
Server.echo "your are at <a href='http://maps.google.com/maps?q=#{geoip.latitude},#{geoip.longitude}'>this pin</a><br>"
Server.echo "</BODY>"
```

### apacheを起動

- configtestを行い、問題なければ起動。

   ```
# /usr/sbin/apachectl configtest
Syntax OK
# /usr/sbin/apachectl start
```

- systemdで起動する場合は、httpdにUNIT登録したあと、起動する。

   ```
# systemctl enable httpd.service
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'
# systemctl start httpd.service
```

実際にアクセスしてみると国コードなど取得できることを確認！

## 備考

`gcc` が入ってないと以下のようなエラーとなる。

```
Command Failed: [gcc -g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings -fPIC -g3 -Wall -Werror-implicit-function-declaration -I"/home/atani/mod_mruby/mruby/include" -MMD -o "/home/atani/mod_mruby/mruby/build/host/src/array.o" -c "/home/atani/mod_mruby/mruby/src/array.c"]
```

`bison` が入ってないと以下のようなエラーとなる。

```
sh: bison: command not found
sh: bison: command not found
rake aborted!
Command Failed: [bison -o "/home/atani/mod_mruby/mruby/build/host/mrbgems/mruby-compiler/core/y.tab.c" "/home/atani/mod_mruby/mruby/mrbgems/mruby-compiler/core/parse.y"]
```

`openssl-devel` が入ってないと以下のようなエラーとなる。

```
/home/atani/mod_mruby/mruby/build/mrbgems/mruby-digest/src/digest.c:16:25: fatal error: openssl/evp.h: No such file or directory
 #include <openssl/evp.h>
                         ^
compilation terminated.
/home/atani/mod_mruby/mruby/build/mrbgems/mruby-digest/src/digest.c:16:25: fatal error: openssl/evp.h: No such file or directory
 #include <openssl/evp.h>
                         ^
compilation terminated.
rake aborted!
Command Failed: [gcc -g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings -fPIC -g3 -Wall -Werror-implicit-function-declaration -DMRBGEM_MRUBY_DIGEST_VERSION=0.0.0 -I"/home/atani/mod_mruby/mruby/include" -MMD -o "/home/atani/mod_mruby/mruby/build/host/mrbgems/mruby-digest/src/digest.o" -c "/home/atani/mod_mruby/mruby/build/mrbgems/mruby-digest/src/digest.c"]
```

`GeoIP-devel` が入ってないと以下のようなエラーとなる。

```
/home/atani/mod_mruby/mruby/build/mrbgems/mruby-geoip/src/mrb_geoip.c:12:19: fatal error: GeoIP.h: No such file or directory
 #include <GeoIP.h>
                   ^
compilation terminated.
/home/atani/mod_mruby/mruby/build/mrbgems/mruby-geoip/src/mrb_geoip.c:12:19: fatal error: GeoIP.h: No such file or directory
 #include <GeoIP.h>
                   ^
compilation terminated.
rake aborted!
Command Failed: [gcc -g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings -fPIC -g3 -Wall -Werror-implicit-function-declaration -DMRBGEM_MRUBY_GEOIP_VERSION=0.0.1 -I"/home/atani/mod_mruby/mruby/include" -MMD -o "/home/atani/mod_mruby/mruby/build/host/mrbgems/mruby-geoip/src/mrb_geoip.o" -c "/home/atani/mod_mruby/mruby/build/mrbgems/mruby-geoip/src/mrb_geoip.c"]
```

`httpd-devel` が入ってないと以下のようなエラーとなる。

```
--with-mruby: https://github.com/mruby/mruby.git will be used.
checking for rake1.9.1... no
checking for rake... no
checking for ruby1.9.1... no
checking for ruby... /usr/bin/ruby
checking for apxs2... no
checking for apxs... no
configure: error: neither apxs2 or apxs not found.
```

---

## 参照URL
- [mod_mruby を Amazon EC2、Apache2.4 へ導入する](http://qiita.com/hkusu/items/ca8622c83b0bfc2cac72)
- [mod_mrubyインストール後入門](http://qiita.com/matsumotory/items/7121d6cce728125e5ce2)
- [地理情報を使ってmod_mrubyとngx_mrubyでプログラマブルにアクセス制御](http://hb.matsumoto-r.jp/entry/2014/12/13/004226)