Nginx 集成 GeoIP2 限制网站可访问的国家或区域

2.0k 技术 发表评论

这里以 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 动态模块

  1. 在安装 geoip2 模块之前,可以先看看 nginx 现在有哪些模块:
    nginx -V
  2. 下载并解压扩展包:
    wget https://github.com/leev/ngx_http_geoip2_module/archive/3.3.tar.gz
    tar zxvf 3.3.tar.gz
  3. 查看当前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
  4. 安装 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 问题,解决办法请看文末的【遇到的问题】那一节。

  5. 把编译生成的 so 文件复制到 nginx 用的 modules 目录下:
    cp objs/ngx_http_geoip2_module.so /usr/lib64/nginx/modules/
  6. 打开 Nginx 配置文件 /etc/nginx/nginx.conf,在最外层载入 geoip2 动态库:
    load_module "/usr/lib64/nginx/modules/ngx_http_geoip2_module.so";

3 下载最新 IP 地址库

  1. 访问网站maxmind官网,免费注册,然后下载 mmdb 格式的 IP 库,其中,
  • GeoLite2-Country.mmdb 精确到国家范围
  • GeoLite2-City.mmdb 可以精确到城市范围。
  1. 然后把 mmdb 库放到 /usr/share/GeoIP2 目录下(没有则创建)。

4 配置 Nginx

  1. 打开 /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;
    }
  2. 同样,在 http 区域添加一个 map,说明哪些国家是允许的,哪些是不允许的。比如默认都是允许的,除了美国以外,就可以这样配置。
    map $geoip2_data_country_code $allowed_country {
     default yes;
     US no;
    }
  3. 当一个不允许访问的IP访问时,可以在 server 区域设置返回提示:
    if ($allowed_country = no) {
     return 403;
    }

5 重启和测试

  1. 重启 Nginx
    nginx -t
    nginx -s reload
  2. 通过代理或者其他方式访问,或者用 curl 命令:
    $ curl -I http://garymeng.com
    HTTP/2 403 

6 遇到的问题

  1. '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];
  1. -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
  1. 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 了。

7 参考资料

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

昵称 *