Nginx 集成 GeoIP2 限制网站可访问的国家或区域
这里以 CentOS 系统为例。
1 安装 libmaxminddb
下载最新的 aeris-release rpm,三个命令依次对应CentOS6/7/8
yum -y install https://repo.aerisnetwork.com/pub/aeris-release-6.rpm
yum -y install https://repo.aerisnetwork.com/pub/aeris-release-7.rpm
dnf -y install https://repo.aerisnetwork.com/pub/aeris-release-8.rpm
再安装 libmaxminddb,CentOS6/7请把dnf
换成yum
。
dnf install -y libmaxminddb libmaxminddb-devel
2 安装 geoip2 动态模块
- 在安装 geoip2 模块之前,可以先看看 nginx 现在有哪些模块:
nginx -V
- 下载并解压扩展包:
wget https://github.com/leev/ngx_http_geoip2_module/archive/3.3.tar.gz tar zxvf 3.3.tar.gz
- 查看当前nginx版本,并下载对应版本的 nginx 源码,解压:
nginx -v wget https://nginx.org/download/nginx-1.14.2.tar.gz tar zxvf nginx-1.14.2.tar.gz cd nginx-1.14.2
- 安装
gcc
编译器和一些必要的工具,然后编译 geoip2 成动态库:dnf install -y gcc make pcre-devel zlib-devel openssl-devel ./configure --with-compat --add-dynamic-module=/path/to/ngx_http_geoip2_module make make modules
说明,在
make
的时候你可能会遇到'struct crypt_data' has no member named 'current_salt'
问题和-Werror=cast-function-type
问题,解决办法请看文末的【遇到的问题】那一节。 - 把编译生成的 so 文件复制到 nginx 用的
modules
目录下:cp objs/ngx_http_geoip2_module.so /usr/lib64/nginx/modules/
- 打开 Nginx 配置文件
/etc/nginx/nginx.conf
,在最外层载入 geoip2 动态库:load_module "/usr/lib64/nginx/modules/ngx_http_geoip2_module.so";
3 下载最新 IP 地址库
- 访问网站maxmind官网,免费注册,然后下载 mmdb 格式的 IP 库,其中,
GeoLite2-Country.mmdb
精确到国家范围GeoLite2-City.mmdb
可以精确到城市范围。
- 然后把 mmdb 库放到
/usr/share/GeoIP2
目录下(没有则创建)。
4 配置 Nginx
- 打开
/etc/nginx/nginx.conf
,在http
区域加入如下一行,动态加载编译出来的 so 库:geoip2 /usr/share/GeoIP2/GeoLite2-Country.mmdb { auto_reload 60m; $geoip2_metadata_country_build metadata build_epoch; $geoip2_data_country_code country iso_code; $geoip2_data_country_name country names en; }
- 同样,在
http
区域添加一个map
,说明哪些国家是允许的,哪些是不允许的。比如默认都是允许的,除了美国以外,就可以这样配置。map $geoip2_data_country_code $allowed_country { default yes; US no; }
- 当一个不允许访问的IP访问时,可以在
server
区域设置返回提示:if ($allowed_country = no) { return 403; }
5 重启和测试
- 重启 Nginx
nginx -t nginx -s reload
- 通过代理或者其他方式访问,或者用
curl
命令:$ curl -I http://garymeng.com HTTP/2 403
6 遇到的问题
'struct crypt_data' has no member named 'current_salt'
问题
错误详情:src/os/unix/ngx_user.c:26:7: error: 'struct crypt_data' has no member named 'current_salt' cd.current_salt[0] = ~salt[0]; ^
错误原因:新的 CentOS 系统中的
glibc's crypt
库已经由libxcrypt
取代,但是Nginx代码没有同步更新导致。
解决办法:其实这个错误不影响整个编译过程,所以可以直接注释掉。
打开文件 src/os/unix/ngx_user.c
的 26 行,注释掉它:
//cd.current_salt[0] = ~salt[0];
-Werror=cast-function-type
问题src/http/ngx_http_script.c:698:18: error: cast between incompatible function types from 'size_t (*)(ngx_http_script_engine_t *)' {aka 'long unsigned int (*)(struct <anonymous> *)'} to 'void (*)(ngx_http_script_engine_t *)' {aka 'void (*)(struct <anonymous> *)'} [-Werror=cast-function-type] code->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
错误原因:gcc 将警告当成了错误。
解决办法:打开 objs/Makefile 文件,找到有 -Werror
的这一行(大概在第 3 行),把它删掉
CFLAGS = -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g
变成
CFLAGS = -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -g
is not binary compatible
问题
这个错误是在执行nginx -t
的时候报的。nginx: [emerg] module "/usr/lib64/nginx/modules/ngx_http_geoip2_module.so" is not binary compatible in /usr/share/nginx/modules/ngx_http_geoip2_module.conf:1
错误原因:编译动态模块的时候,编译选项和编译 Nginx 不一致。
解决办法:在./configure
的时候,加上所有nginx -V
的选项,如果还是不行,就需要 手动编译安装nginx 了。