2016年10月3日月曜日

nginx で ngx_http_geoip_module を利用して国別アクセス制御を行う

nginx Module ngx_http_geoip_module
http://nginx.org/en/docs/http/ngx_http_geoip_module.html

nginxのngx_http_geoip_moduleモジュールを利用して、国を指定してアクセス制御をします。
フィルタリング用のデータは、MaxMindのGeoIPオープンソースデータを利用します。
日本だけにコンテンツを配信したい、もしくは、特定の国からのアクセスを拒否したい、といったときに使えるとおもいます。
環境は、CentOS 6です。

以下、セットアップ方法です。

GeoIPデータベースを準備

GeoIPデータベースと更新コマンドをインストール。
# rpm -ivh http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
# yum install GeoIP geoipupdate
GeoIPデータベースファイルが設置されたことを確認。
# stat /usr/share/GeoIP/GeoIP.dat

nginxを準備

nginxをインストール。
# rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
# yum install nginx nginx-module-geoip
# nginx -v
nginx version: nginx/1.10.1
nginx設定パラメータにhttp_geoip_moduleが入っているか確認。GeoIPは、動的モジュールとして有効化できるようになっています。
# nginx -V
--with-http_geoip_module=dynamic
nginx動的モジュールの共有オブジェクトファイルが設置されているか確認。
# ls /etc/nginx/modules/
ngx_http_geoip_module-debug.so  ngx_http_geoip_module.so

nginx設定

カントリーコードはこちらのURLを参考にします。
http://dev.maxmind.com/geoip/legacy/codes/iso3166/

load_moduleを追記。modules/ngx_http_geoip_module.soを指定します。
events {} ブロックより上に記述するのがポイントです。
# emacs /etc/nginx/nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

load_module modules/ngx_http_geoip_module.so;

events {
    worker_connections  1024;
}

http {} ブロックを編集。
includeより上に記述します。
アメリカ(US)と日本(JP)からのアクセスを拒否する場合。
# emacs /etc/nginx/nginx.conf
    geoip_country /usr/share/GeoIP/GeoIP.dat;
    map $geoip_country_code $allowed_country {
        default yes;
        US no;
        JP no;
    }
日本(JP)と韓国(KR)からのアクセスを許可、他の国からのアクセスを拒否する場合。
# emacs /etc/nginx/nginx.conf
    geoip_country /usr/share/GeoIP/GeoIP.dat;
    map $geoip_country_code $allowed_country {
        default no;
        JP yes;
        KR yes;
    }

server {} ブロックを編集。
allowed_countryがnoだったらステータスコード403を返します。
# emacs /etc/nginx/conf.d/default.conf
    if ($allowed_country = no) {
        return 403;
    }


nginx再起動

nginxをリスタート、またはリロード。
# /etc/init.d/nginx restart
ブラウザなどでアクセスして確認しましょう。思い通りにアクセスコントロールできたでしょうか?

GeoIPデータベースを更新するには?

GeoIPデータベースをアップデートをするには、以下のコマンドを叩きます。定期実行するようにしましょう。
# geoipupdate -v

ELBの場合は?

AWSを使っている場合、ELBを前段に置く場合がほとんどだとおもいます。
ELBからのヘルスチェックでサーバに来るアクセスは、内部IPアドレスのため、国で判別することができません。そのため、VPC内のIPアドレスからアクセスが来た時には、制限をスルーするようにnginxを設定します。

以下の設定では、ELB経由のアクセスから、アクセス元IPアドレスを取得。ELBのヘルスチェックのアクセスを許可、日本からのアクセスを許可、ほかの国は拒否としています。
ついでに、アクセスログにGeo情報を出力するようにしています。
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
if ($allowed_country = yes) {
set $exclusions 1;
}
if ($exclusions = "0") {
return 403;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
view raw default.conf hosted with ❤ by GitHub
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
load_module modules/ngx_http_geoip_module.so;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
geoip_country /usr/share/GeoIP/GeoIP.dat;
geoip_city /usr/share/GeoIP/GeoLiteCity.dat;
log_format ltsv 'time:$time_iso8601\t'
'remote_addr:$remote_addr\t'
'request_method:$request_method\t'
'request_length:$request_length\t'
'request_uri:$request_uri\t'
'https:$https\t'
'uri:$uri\t'
'query_string:$query_string\t'
'status:$status\t'
'bytes_sent:$bytes_sent\t'
'body_bytes_sent:$body_bytes_sent\t'
'referer:$http_referer\t'
'useragent:$http_user_agent\t'
'forwardedfor:$http_x_forwarded_for\t'
'request_time:$request_time\t'
'upstream_response_time:$upstream_response_time\t'
'host:$host\t'
'geoip_country_name:$geoip_city_country_name\t'
'geoip_country_code3:$geoip_city_country_code3\t'
'geoip_city:$geoip_city\t'
'geoip_latitude:$geoip_latitude\t'
'geoip_longitude:$geoip_longitude';
access_log /var/log/nginx/access.log ltsv;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
real_ip_header X-Forwarded-For;
set_real_ip_from 172.31.0.0/16;
map $geoip_country_code $allowed_country {
default no;
JP yes;
}
geo $exclusions {
default 0;
172.31.0.0/16 1;
}
include /etc/nginx/conf.d/*.conf;
}
view raw nginx.conf hosted with ❤ by GitHub



Top image from いらすとや