AI 摘要

本文详解如何为Nginx编译GeoIP2模块实现精准地理定位,通过源码编译整合libmaxminddb依赖库,配置动态分流策略实现基于IP属地的内容分发。重点剖析模块加载机制与配置优化,展示从数据库部署到流量分发的全链路实践方案,助力服务器智能化升级。

GeoIP2

GeoIP2是MaxMind提供的最新一代地理定位服务,它提供了比旧版GeoIP更高的精度和更多的数据点。GeoIP 数据库有免费版(GeoLite2)和收费版(GeoIP2),这些数据库可以提供详细的地理位置信息,如国家、地区/省、城市、邮政编码、纬度/经度、ISP、自治系统编号(ASN)等。

将GeoIP数据库集成到Nginx服务器,常见的做法是编译时使用--with-http_geoip_module参数,然而这样编译实际上是提供了旧版GeoIP数据库格式的支持(官方称之为Legacy格式)。虽然Legacy格式曾经是行业标准,但随着技术的发展,它逐渐被功能更强大、精度更高的 GeoIP2 所替代。官方已宣布在2022年5月结束对Legacy数据库的支持,并建议用户迁移到更新的GeoIP2平台。

Nginx集成GeoIP2可以通过ngx_http_geoip2_module项目实现。

编译安装Nginx

安装依赖

为了编译一个支持stream和http/3等常用功能的Nginx,需要先行安装如下依赖:

sudo apt-get install openssl libssl-dev \
libpcre3 libpcre3-dev \
zlib1g-dev \
libxml2 libxml2-dev libxslt-dev \
libgd-dev \
libgeoip1 libgeoip-dev

进一步地,安装geoip相关的库:

sudo apt-get install libmaxminddb0 libmaxminddb-dev mmdb-bin

编译安装

首先下载ngx_http_geoip2_module源码到服务器,并解压。

然后下载Nginx-1.27.4源码到服务器并解压,切换到configure文件所在目录下(cd nginx-1.27.4/):

./configure --with-debug \
--with-pcre-jit \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_auth_request_module \
--with-http_v2_module \
--with-http_v3_module \
--with-http_dav_module \
--with-http_slice_module \
--with-threads \
--with-http_addition_module \
--with-http_geoip_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_image_filter_module \
--with-http_sub_module \
--with-http_xslt_module \
--with-stream \
--with-stream_ssl_module \
--with-mail \
--with-mail_ssl_module \
--with-stream_ssl_preread_module \
--add-module=/home/src/ngx_http_geoip2_module-3.4/

注意上面的最后一行指向ngx_http_geoip2_module所在的目录。最后编译安装。

make && make install

也可以同时调用更多的内核参与这个过程,这也会消耗更多的内存,如:

make -j4 && make install

Nginx会被默认安装在/usr/local/nginx/目录下

创建如下软连接

ln -s /usr/local/nginx/sbin/nginx /usr/sbin/nginx

之后即可正常使用nginx -Vnginx -s reload等命令。

应用实战

场景——对于站点s.221.ltd上的静态资源,来自中国大陆的访问直接提供资源,中国大陆以外的访问重定向到static.colorfulstage.cn站点下的同一路径资源(实际应用中,后者可能是CDN域名或专门用于提供地区特供资源)。基于此,同样可以实现屏蔽特定地区访问的功能(简单粗暴地return 403;)。

配置文件

为方便管理,在/usr/local/nginx/conf/目录下创建conf.dmodules-enabled文件夹,并把/usr/local/nginx/conf/nginx.conf中的内容替换为如下内容,重要修改有以下几点:

  • http中的include conf.d/*.conf;和最外层的include modules-enabled/*.conf;
  • http{}中加入server_tokens off;,以此隐藏错误页的服务器信息
  • log_formataccess_log,quic需要用到
  • [可选]禁用了远古TLS协议,仅启用TLSv1.2和1.3
  • 基于GeoIP2提供了访客IP属地的判定
user root root;
worker_processes auto;

include modules-enabled/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format quic '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" "$http3"';
    access_log logs/access.log quic;

    include mime.types;
    charset utf-8;
    default_type application/octet-stream;
    sendfile on;
    #tcp_nopush     on;
    #keepalive_timeout  0;

    gzip on;
    # Sets a gzip compression level of a response. Acceptable values are in the range from 1 to 9.
    gzip_comp_level 6;
    gzip_types text/css text/xml application/javascript;
    # Disable gzip for legacy IEs.
    gzip_disable "MSIE [1-6]\.";
    # 选择支持vary header;改选项可以让前端的缓存服务器缓存经过gzip压缩的页面; 这个可以不写,表示在传送数据时,给客户端说明我使用了gzip压缩
    gzip_vary on;

    # with geoip-2
    geoip2 /home/geoip-2/GeoLite2-Country.mmdb {
        $geoip2_data_country_code country iso_code;
    }

    map $geoip2_data_country_code $is_china {
        default 0;
        CN 1;
    }

    set_real_ip_from 0.0.0.0/0;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    include conf.d/*.conf;

    server_tokens off;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
}

conf.d/目录下创建static-file.conf,并写入以下内容:

server {
    listen 443 ssl;
    server_name s.221.ltd;
    ssl_certificate crts/221.ltd/fullchain.pem;
    ssl_certificate_key crts/221.ltd/key.pem;
    http2 on;
    add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
    proxy_hide_header X-Powered-By;
    add_header 'Access-Control-Allow-Origin' '*';

    if ($is_china = 0) {
        return 301 https://static.colorfulstage.cn$request_uri;
    }

    location / {
        alias /var/www/html/static/;
        client_max_body_size 512m;
        autoindex on;
        try_files $uri $uri/ =404;
        valid_referers none 221.ltd *.221.ltd;
        if ($invalid_referer) {
            return 403;
        }
    }
}

server {
    listen 443 ssl;
    server_name static.colorfulstage.cn;
    ssl_certificate crts/colorfulstage.cn/fullchain.pem;
    ssl_certificate_key crts/colorfulstage.cn/key.pem;
    http2 on;
    add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
    proxy_hide_header X-Powered-By;
    add_header 'Access-Control-Allow-Origin' '*';

    location / {
        alias /var/www/html/static/;
        client_max_body_size 512m;
        autoindex off;
        try_files $uri $uri/ =404;
    }
}

添加Systemd守护进程

控制台执行vim /usr/lib/systemd/system/nginx.service并写入以下内容

[Unit]
Description=nginx service
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=true
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target

输入:wq保存,之后配置开启自启动

systemctl enable nginx

启动Nginx服务

service nginx start

查看服务状态

service nginx status

编译安装Nginx到此结束。

使用XFF模拟测试

设置X-Forwarded-For头来模拟源站ip地址测试。

使用8.8.8.8来模拟海外ip的访问,可以看到发生了跳转并由跳转后的Cloudflare CDN提供了资源。

使用114.114.114.114来模拟中国大陆的访问,源站直接提供了资源。