使用Nginx Geoip2模块 解析客户端IP地理信息 可以对指定国家区域进行限制访问
ngx_http_geoip2_module
libmaxminddb
ngx_http_geoip2_module 依赖
libmaxminddb
https://github.com/maxmind/libmaxminddb
Ubuntu 有 apt源 可以直接安装,也可以编译安装
sudo add-apt-repository ppa:maxmind/ppa
sudo apt update
sudo apt install libmaxminddb0 libmaxminddb-dev mmdb-bin
ngx_http_geoip2_module
编译时不能同时
with-http_geoip_module
使用过程会冲突
https://github.com/leev/ngx_http_geoip2_module/issues/92
git clone https://github.com/leev/ngx_http_geoip2_module.git
./configure \
--prefix=/usr/local/openresty \
--user=www \
--group=www \
--with-file-aio \
--with-poll_module \
--with-http_realip_module \
--with-http_image_filter_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_slice_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_degradation_module \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_stub_status_module \
--with-http_slice_module \
--with-http_auth_request_module \
--with-stream \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-stream_realip_module \
--with-threads \
--with-pcre \
--with-compat \
--with-stream \
--with-stream_ssl_module \
--add-module=/root/ngx_http_geoip2_module
# with-http_geoip_module 不能同时使用
编译成功后,手动复制nginx二进制执行文件
make -j 8
生产业务不能停机,这里采用平滑升级
mv /usr/local/openresty/nginx/sbin/nginx /usr/local/webserver/nginx/sbin/nginx_old
cp ./build/nginx-1.25.3/objs/nginx /usr/local/openresty/nginx/sbin/
# 测试
nginx -t
# 查看模块
nginx -V
若刷新配置文件nginx -s reload
不生效,这里再执行多一次,测试环境则可以简单粗暴systemctl restart nginx
kill -USR2 `cat /usr/local/openresty/nginx/logs/nginx.pid`
sleep 1
kill -QUIT `cat /usr/local/openresty/nginx/logs/nginx.pid.oldbin`
GeoIP2地址库
maxmind官方注册有点麻烦,这里使用
https://github.com/P3TERX/GeoLite.mmdb
mkdir /opt/geoip
cd /opt/geoip
wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb
wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb
$ mmdblookup --file GeoLite2-City.mmdb --ip 119.29.29.29
{
"city":
{
"geoname_id":
1809858 <uint32>
"names":
{
"de":
"Guangzhou" <utf8_string>
"en":
"Guangzhou" <utf8_string>
"es":
"Cantón" <utf8_string>
"fr":
"Canton" <utf8_string>
"ja":
"広州" <utf8_string>
"pt-BR":
"Cantão" <utf8_string>
"ru":
"Гуанчжоу" <utf8_string>
"zh-CN":
"广州市" <utf8_string>
}
}
...
mmdblookup 会返回 JSON 格式的数据,字段路径就是 JSON 的路径,数组字段直接用下标索引即可。
http {
geoip2 <db_path> {
<$variable_name> default=<default_value> source=<$variable> <path>;
// ...
}
}
db_path
为GeoIP2 地址库路径,建议使用绝对路径,相对路径为Nginx 安装目录;variable_name
为定义变量名,用于存储获取到的地理位置信息,比如 geoip2_city_name和geoip2_country_name 等;default_value
为当获取不到地理位置信息时的默认值;source
为要解析的IP 地址,默认为$remote_addr
变量,若 Nginx 前面还有一层负载网关,可以按实际调整IP变量, 如$http_x_forwarded_for
或$http_x_real_ip
等
解析国家使用 GeoLite2-Country.mmdb
解析国家城市等使用 GeoLite2-City.mmdb
http {
map $http_x_forwarded_for $real_ip {
default $remote_addr;
"~^(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$" $ip;
"~^(?P<first_ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}),.*$" $first_ip;
}
# geoip2 /opt/geoip/GeoLite2-Country.mmdb {
# auto_reload 5m;
# $geoip2_metadata_country_build metadata build_epoch;
# $geoip_country_code default=Unknown source=$real_ip country iso_code;
# $geoip_country_name default=Unknown source=$real_ip country names en;
# }
geoip2 /opt/geoip/GeoLite2-City.mmdb {
$geoip_country_code default=Unknown source=$real_ip country iso_code;
$geoip_province_name default=Unknown source=$real_ip subdivisions 0 names en;
$geoip_city_name default=Unknown source=$real_ip city names en;
}
log_format sundayhk_json '{'
'"@timestamp": "$time_iso8601",'
'"remote_addr": "$remote_addr",'
'"real_ip": "$real_ip,'
'"host": "$host",'
'"request_method": "$request_method",'
'"request_uri": "$request_uri", '
'"uri": "$uri", '
'"status": $status,'
'"request_time": $request_time,'
'"request_length": $request_length,'
#'"request_body":"$request_body",'
'"body_bytes_sent": $body_bytes_sent,'
'"bytes_sent": $bytes_sent,'
'"http_referer": "$http_referer",'
'"http_user_agent": "$http_user_agent",'
#'"http_x_request_id":"$http_x_request_id",'
'"http_x_forwarded_for": "$http_x_forwarded_for",'
'"upstream_addr": "$upstream_addr",'
'"upstream_status": "$upstream_status",'
'"upstream_response_time": "$upstream_response_time",'
'"upstream_cache_status": "$upstream_cache_status",'
'"server_addr":"$server_addr",'
'"server_port":$server_port,'
'"server_protocol":"$server_protocol",'
'"remote_user":"$remote_user",'
'"ssl_protocol":"$ssl_protocol",'
'"ssl_cipher":"$ssl_cipher"'
'"geoip_country_code": "$geoip_country_code",'
'"geoip_province_name": "$geoip_province_name",'
'"geoip_city_name": "$geoip_city_name"'
'}';
access_log /data/logs/nginx/access_json.log sundayhk_json;
}
$ tail /data/logs/nginx/access_json.log
{"@timestamp": "2024-07-28T11:07:42+08:00","remote_addr": "116.76.39.99","real_ip": "116.76.39.99,"host": "www.sundayhk.com","request_method": "GET","request_uri": "/albums/page/101", "uri": "/albums/page/101", "status": 301,"request_time": 0.000,"request_length": 413,"body_bytes_sent": 182,"bytes_sent": 383,"http_referer": "-","http_user_agent": "Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.4511.139 Mobile Safari/537.36 OppoBrowser/10.5.1.2","http_x_forwarded_for": "-","upstream_addr": "-","upstream_status": "-","upstream_response_time": "-","upstream_cache_status": "-","server_addr":"64.181.239.33","server_port":80,"server_protocol":"HTTP/1.1","remote_user":"-","ssl_protocol":"-","ssl_cipher":"-""geoip_country_code": "CN","geoip_province_name": "Guangdong","geoip_city_name": "Shenzhen"}
传递IP地理变量给上游服务
http {
server {
listen 80;
location / {
proxy_pass http://upstream;
proxy_set_header X-Real-IP $real_ip;
proxy_set_header X-Country-Code $geoip_country_code;
proxy_set_header X-Province-Name $geoip_province_name;
proxy_set_header X-City-Name $geoip_city_name;
}
location /php {
fastcgi_pass php:9000;
fastcgi_param REMOTE_ADDR $real_ip;
fastcgi_param GEOIP_COUNTRY_Code $geoip_country_code;
fastcgi_param GEOIP_PROVINCE_NAME $geoip_province_name;
fastcgi_param GEOIP_CITY_NAME $geoip_city_name;
}
}
}
限制指定国家访问
http {
map $geoip_country_code $is_country_code_allowed {
CN 1;
default 0;
}
server {
listen 80;
location / {
if ($is_country_code_allowed = 0) {
return 403 "Forbidden";
}
return 200 "Welcome!";
}
}
}
不同国家IP显示不同语言
http {
map $geoip_country_code $lang_path {
default /; # 默认路径(英文)
CN /cn/; # 中国用户访问 /cn/
JP /jp/; # 日本用户访问 /jp/
US /en/;
GB /en/;
FR /fr/;
}
server {
listen 80;
server_name your_domain.com; # 替换为你的域名
# 重定向逻辑
location / {
# 检查当前请求的 URI 是否已经包含了语言路径
# 避免重复重定向,例如用户手动访问 your_domain.com/cn/ 时不再重定向
if ($request_uri !~ ^/(cn|jp|en|fr)/) {
# 显式URL改变(例如从 example.com 到 example.com/cn/)
rewrite ^ $scheme://$host$lang_path$request_uri? permanent;
# 保持原URL不变,避免302/301 重定向,
# rewrite ^/(.*)$ $lang_path$1 break;
}
}
# 可以为不同的语言路径配置不同的根目录或处理逻辑
# location /cn/ {
# root /path/to/your/website/root/cn;
# index index.html;
# try_files $uri $uri/ =404;
# }
#
# location /jp/ {
# root /path/to/your/website/root/jp;
# index index.html;
# try_files $uri $uri/ =404;
# }
}
}