文章目录
- 前言
- 安装环境
- 软件版本说明
- libmaxminddb 环境安装
- ngx_http_geoip2_module 安装
- GeoLite2 数据库下载
- 定时更新
- 测试定位
- 安装模块
- 应用场景
- 重点
- 仅限中国访问,国外禁止
- 仅限中国访问,但放开国外部分IP
- 不同国家展示不同页面
- IP地址解析
- 总结
前言
GeoIP2是一种被广泛使用的IP地址定位技术,它可以根据用户的IP地址确定其所在的地理位置信息。无论是网络安全还是市场营销等领域,GeoIP2都是一个非常实用的工具。因为它易于使用,所以它经常被外贸等行业广泛应用。
由于之前,我也有了相关的需求,也是开始测试各种定位相关的工具。这个工具的测试,是在我写通过API限制城市之前。为了避免忘记,我将记录下测试Geoip2的过程整理后,与大家分享。希望这篇文章可以对需要定位IP地址的读者有所帮助。
安装环境
如果不是环境特别差,没有要求不能动nginx的版本的话,直接更换openresty吧。
这个环境基本上是上一篇的环境一致,对于这个模块的测试实际上是在上一个环境之前,修改后才将文档放出来的
软件版本说明
个人实际的软件版本
nginx:1.20.1
https://nginx.org/download/nginx-1.20.1.tar.gz
我使用源码安装
libmaxminddb:1.6.0
https://github.com/maxmind/libmaxminddb
https://github.com/maxmind/libmaxminddb/releases/download/1.6.0/libmaxminddb-
1.6.0.tar.gz
我使用yum安装,因为这些组件很少会出现版本不适配的问题
ngx_http_geoip2_module:3.4
https://github.com/leev/ngx_http_geoip2_module/
https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/3.4.tar.gz
GeoLite2 数据库下载
https://dev.maxmind.com/geoip/geoip2/geolite2/
libmaxminddb 环境安装
安装 libmaxminddb 库(开源的C库,主要用于解析MaxMind GeoIP2数据库文件)
MaxMind数据库更新工具GeoIP Update,
以及与 MaxMind 数据库相关的一些实用工具mmdblookup
目的是用于解析和更新之后添加的地图数据库
<1>源码安装
wget https://github.com/maxmind/libmaxminddb/releases/download/1.6.0/libmaxminddb-1.6.0.tar.gz
tar -xf libmaxminddb-1.6.0.tar.gz
cd libmaxminddb-1.6.0
./configure
make && make install
echo "/usr/local/lib" >> /etc/ld.so.conf.d/libc.conf
sudo ldconfig
<2>Ubuntu安装
apt install libmaxminddb0 libmaxminddb-dev mmdb-bin geoipupdate
<3>Centos安装
yum install libmaxminddb libmaxminddb-devel mmdb-utils geoipupdate
ngx_http_geoip2_module 安装
用于将MaxMind GeoIP2数据库中的地理位置信息与HTTP请求相关联。它可以在Nginx配置文件中使用,并且提供了一些指令和变量,可以使用这些指令和变量来处理基于地理位置的HTTP请求
$geoip2_city: 解析出的城市名称
$geoip2_country_code: 解析出的国家代码,例如US表示美国
$geoip2_country_name: 解析出的国家名称,例如United States表示美国
$geoip2_latitude: 解析出的纬度,以十进制度数表示
$geoip2_longitude: 解析出的经度,以十进制度数表示
$geoip2_region: 解析出的行政区域名称,例如California表示加利福尼亚州
$geoip2_region_code: 解析出的行政区域代码,例如CA表示加利福尼亚州
wget https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/3.4.tar.gz -O /usr/local/src/ngx_http_geoip2_module-3.4.tar.gz
cd /usr/local/src/
tar -xzf ngx_http_geoip2_module-3.4.tar.gz
GeoLite2 数据库下载
https://dev.maxmind.com/geoip/geoip2/geolite2/
MaxMind 公司要求用户注册并创建帐户才能下载 Geo 数据库。注册过程是免费的,并且没有任何付款要求。但是建议注册的时候使用谷歌等邮箱,并且不要连接VPN或代理,IP所在国家和注册时候填入的国家保持一致。如果被检测为不一致会被认为使用代理,并对该ip封禁一段时间,可以更换其他的IP(网络连接方式),再次进行注册。
(手机号不是必填的,真实信息不泄露,用途随意选择)
GeoIP2数据库包含了有关世界上各个地理位置的信息,如国家、城市、邮政编码、经纬度、时区等等,开发者可以根据IP地址(支持IPv4 和 IPv6), 定位该IP所在的洲、经纬度、国家、省市、ASN 等信息,并将这些地理位置数据与他们的应用程序集成在一起,从而实现更好的用户定位和服务。GeoLite2是GeoIP2的免费版本,与GeoIP2数据库相比准确性较差,但是实际上就像前一篇说的,精度始终不要有太高的期望。
<1>注册
成功后,邮箱会收到激活邮件,点击进入并修改密码
<2>创建密钥(用于后面设置自动更新)
https://www.maxmind.com/en/accounts/847212/license-key
下载配置文件,里面包含秘钥
GeoIP.conf
上传到/etc/下
<3>下载数据库
or
提供各种数据库格式和下载方式。最后要上传到服务器,统一存放,解压即可,无需编译
基本下载:
GeoLite2 City
GeoLite2 Country
mkdir /usr/local/Geoip2
ls /usr/local/Geoip2/
GeoLite2-Country.mmdb GeoLite2-City.mmdb
GeoLite2-Country.mmdb仅提供有关IP地址所属国家的信息,包含一个IP地址范围和一个对应的国家代码和英文名称
GeoLite2-City.mmdb则提供了更详细的信息,包括IP地址的精确位置、城市、邮政编码、地理坐标以及其他更详细的地理位置信息
可以根据您的应用程序需要来选择使用哪个数据库。如果只需要知道用户的来源国家,则可以仅使用GeoLite2-Country,如果需要更详细的位置信息(如城市、地区等),则应使用GeoLite2-City
定时更新
<1>修改上传配置文件
添加要更新数据库的位置
cat /etc/GeoIP.conf
AccountID 841951
LicenseKey 3qvF5fxxxxxxxxxU8_mmk
EditionIDs GeoLite2-City GeoLite2-Country
DatabaseDirectory /usr/local/Geoip2
<2>创建计划任务
crontab -l
0 * * * 6 /usr/bin/geoipupdate > /var/log/geoipupdate.log 2>&1
测试定位
mmdblookup 命令可指定数据库文件,并通过命令和参数, 查找IP所在位置的相关数据,并以JSON格式输出
<1>mmdblookup --file ./GeoLite2-Country.mmdb --ip 123.123.123.123
{
"continent":
{
"code":
"AS" <utf8_string>
"geoname_id":
6255147 <uint32>
"names":
{
"de":
"Asien" <utf8_string>
"en":
"Asia" <utf8_string>
"es":
"Asia" <utf8_string>
"fr":
"Asie" <utf8_string>
"ja":
"アジア" <utf8_string>
"pt-BR":
"Ásia" <utf8_string>
"ru":
"Азия" <utf8_string>
"zh-CN":
"亚洲" <utf8_string>
}
}
"country":
{
"geoname_id":
1814991 <uint32>
"iso_code":
"CN" <utf8_string>
"names":
{
"de":
"China" <utf8_string>
"en":
"China" <utf8_string>
"es":
"China" <utf8_string>
"fr":
"Chine" <utf8_string>
"ja":
"中国" <utf8_string>
"pt-BR":
"China" <utf8_string>
"ru":
"Китай" <utf8_string>
"zh-CN":
"中国" <utf8_string>
}
}
"registered_country":
{
"geoname_id":
1814991 <uint32>
"iso_code":
"CN" <utf8_string>
"names":
{
"de":
"China" <utf8_string>
"en":
"China" <utf8_string>
"es":
"China" <utf8_string>
"fr":
"Chine" <utf8_string>
"ja":
"中国" <utf8_string>
"pt-BR":
"China" <utf8_string>
"ru":
"Китай" <utf8_string>
"zh-CN":
"中国" <utf8_string>
}
}
}
<2>mmdblookup --file ./GeoLite2-City.mmdb --ip 123.123.123.123
{
"city":
{
"geoname_id":
1816670 <uint32>
"names":
{
"de":
"Beijing" <utf8_string>
"en":
"Beijing" <utf8_string>
"es":
"Pekín" <utf8_string>
"fr":
"Pékin" <utf8_string>
"ja":
"北京市" <utf8_string>
"pt-BR":
"Pequim" <utf8_string>
"ru":
"Пекин" <utf8_string>
"zh-CN":
"北京" <utf8_string>
}
}
"continent":
{
"code":
"AS" <utf8_string>
"geoname_id":
6255147 <uint32>
"names":
{
"de":
"Asien" <utf8_string>
"en":
"Asia" <utf8_string>
"es":
"Asia" <utf8_string>
"fr":
"Asie" <utf8_string>
"ja":
"アジア" <utf8_string>
"pt-BR":
"Ásia" <utf8_string>
"ru":
"Азия" <utf8_string>
"zh-CN":
"亚洲" <utf8_string>
}
}
"country":
{
"geoname_id":
1814991 <uint32>
"iso_code":
"CN" <utf8_string>
"names":
{
"de":
"China" <utf8_string>
"en":
"China" <utf8_string>
"es":
"China" <utf8_string>
"fr":
"Chine" <utf8_string>
"ja":
"中国" <utf8_string>
"pt-BR":
"China" <utf8_string>
"ru":
"Китай" <utf8_string>
"zh-CN":
"中国" <utf8_string>
}
}
"location":
{
"accuracy_radius":
50 <uint16>
"latitude":
39.914300 <double>
"longitude":
116.386100 <double>
"time_zone":
"Asia/Shanghai" <utf8_string>
}
"registered_country":
{
"geoname_id":
1814991 <uint32>
"iso_code":
"CN" <utf8_string>
"names":
{
"de":
"China" <utf8_string>
"en":
"China" <utf8_string>
"es":
"China" <utf8_string>
"fr":
"Chine" <utf8_string>
"ja":
"中国" <utf8_string>
"pt-BR":
"China" <utf8_string>
"ru":
"Китай" <utf8_string>
"zh-CN":
"中国" <utf8_string>
}
}
"subdivisions":
[
{
"geoname_id":
2038349 <uint32>
"iso_code":
"BJ" <utf8_string>
"names":
{
"en":
"Beijing" <utf8_string>
"fr":
"Municipalité de Pékin" <utf8_string>
"zh-CN":
"北京市" <utf8_string>
}
}
]
}
上面获取的参数可用于对nginx的Geoip2模块的设置
多找一些各地的IP进行测试,并和百度,高德以及其他定位api得出的结果进行对比
如果根据geoip2给的经纬度去做地图显示功能是很离谱的
据了解即使是搞外贸的也只是常用国家或地区级别的定位功能和推广功能
安装模块
编译安装可查看前文
<1>编译参数
此次在编译参数末尾加上
--add-dynamic-module=/usr/local/src/ngx_http_geoip2_module-3.4
也可写相对路径这一类
../ngx_http_geoip2_module-3.4
由于每个人环境不同,如编译时遇到报错,根据自己报错解决问题或安装依赖
可以直接安装前文中的依赖包
<2>make编译后拷贝生成的文件到自己配置文件指定的modules目录
cd objs/
cp -r objs/ngx_http_geoip2_module.so /usr/share/nginx/modules/
cp -r objs/ngx_stream_geoip2_module.so /usr/share/nginx/modules/
根据直接情况决定,是要平滑升级还能直接更换
cp -r objs/nginx /usr/sbin/nginx
应用场景
有些人的服务器放到了国外,客户仅仅只是某些国家,而要禁止其他国家访问,可以减少流量和攻击
有些搞人外贸的,给不同国家的用户,展示不同的页面和报价
重点
加载Geoip2 模块动态链接库
load_module modules/ngx_http_geoip2_module.so;
load_module modules/ngx_stream_geoip2_module.so;
events {
worker_connections 102400;
}
必须在events之前
仅限中国访问,国外禁止
<1>http块设置
http {
....
geoip2 /usr/local/Geoip2/GeoLite2-Country.mmdb {
$geoip2_data_country_code country iso_code;
}
map $geoip2_data_country_code $allowed_country {
CN yes; #中国
TW yes; #中国台湾
HK yes; #中国香港
MO yes; #中国澳门
default no;
}
...
}
GeoIP2模块存在着很多变量,会根据设置的匹配规则,将该变量设置为匹配的值。并且只有引入的变量才可被使用,例如要做IP地址解析服务,需要配置城市,经纬度等变量
由于这里只演示国家级别配置,只配置了Country数据库,引入geoip2_data_country_code这一变量
$geoip2_data_country_code 变量包含客户端IP地址所属的国家的ISO代码。它与geoip2_data_country不同,因为它返回的是国家的ISO代码而不是国家名称,后面可根据IP解析出的ISO代码判断其国家
map 用于定义映射表,它需要两个参数:源变量和目标变量
$geoip2_data_country_code 作为源变量,它表示客户端IP地址所属的国家的ISO代码
$allowed_country 作为目标变量,它将在映射表中查找匹配项,并返回相应的值
default no; 这是默认值,意思是如果没有匹配到任何条目,则将$allowed_country设置为“no”
CN yes; 是告知Nginx如果客户端IP地址所属的国家代码是CN(中国),则将$allowed_country设置为“yes”
<2>server或location块配置
根据需求放到server或location
location /sheying/ {
if ($allowed_country != yes ) {
#or
#if ($allowed_country = no) {
return 403
}
root /usr/share/nginx/sheying/;
index index.html;
}
如果变量$allowed_country不等于yes(或等于no),将返回HTTP状态码403,禁止访问
仅限中国访问,但放开国外部分IP
<1>http块设置
http {
....
geoip2 /usr/local/Geoip2/GeoLite2-Country.mmdb {
$geoip2_data_country_code country iso_code;
}
#map也可放到server里
map $geoip2_data_country_code $allowed_country {
default no;
CN yes;
}
# 指定放开的外国IP地址
map $remote_addr $is_foreign {
default 1;
192.168.0.0/16 0; # 指定局域网内的 IP 地址不被拒绝
10.0.0.0/8 0; # 指定局域网内的 IP 地址不被拒绝
172.16.0.0/12 0; # 指定局域网内的 IP 地址不被拒绝
12.12.12.12 0; # 允许特定外国IP地址或网段或ipv6或ipv6网段访问
104.16.0.0/13; # 允许特定外国IP地址或网段或ipv6或ipv6网段访问
}
....
}
<2>server或location块配置
根据需求放到server或location
location /sheying/ {
if ($allowed_country = no) {
if ($is_foreign) {
root /usr/share/nginx/sheying/;
index index.html;
} else {
return 403;
}
}
or
lua方法
location /sheying/ {
access_by_lua_block {
if ngx.var.allowed_country == "yes" or ngx.var.is_foreign == "yes" then
ngx.exec('/usr/share/nginx/sheying/index.html')
end
return ngx.exit(ngx.HTTP_FORBIDDEN)
}
}
不同国家展示不同页面
静态页面
<1>http配置
geoip2 /usr/local/Geoip2/GeoLite2-Country.mmdb {
$geoip2_data_country_code country iso_code;
}
map $geoip2_data_country_code $country_page {
default /pay/defa;
CN /pay/cn;
US /pay/us;
JP /pay/jp;
}
<2>server或location配置
location /pay/ {
root /usr/share/nginx/html/;
index index.html;
}
一个日本ip访问https://xx.xx.xx.xxx/pay/
会跳转到https://xx.xx.xx.xxx/pay/jp/
重定向的本地路径应该是/usr/share/nginx/html/pay/jp/index.html
因为map配置路径为/pay/jp,root目录为/usr/share/nginx/html/,所以只有当日本用户请求的路径为https://xxx.xxx.x.xx/pay时,才会跳转到/usr/share/nginx/html/pay/jp/。请求为https://xx.xx.xx.xxx/下的其他路径不受影响
也可以按照前几个场景中的map配置,不在map中匹配路径。只在server或location中用if判断不同国家或城市走不同的路径
location /pay/ {
if ($allowed_country = CN) {
root /usr/share/nginx/html/pay/cn/;
index index.html;
}
if ($allowed_country = JP) {
root /usr/share/nginx/html/pay/jp/;
index index.html;
}
}
反向代理
<1>http配置
geoip2 /usr/local/Geoip2/GeoLite2-Country.mmdb {
$geoip2_data_country_code country iso_code;
}
#upstream可以另写配置文件,然后include
upstream tomcat_cn {
server 192.168.1.1:8080;
server 192.168.1.2:8080;
}
upstream tomcat_us {
server 192.168.1.1:8081;
server 192.168.1.2:8081;
}
map $geoip2_data_country_code $country_page {
default tomcat_cn;
CN tomcat_cn;
US tomcat_us;
}
<2>server或location配置
location /pay {
proxy_pass http://$country_page/pay/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
IP地址解析
做一个类似ip-api.com的ip地址解析网站
http {
.....
geoip2 /usr/local/Geoip2/GeoLite2-Country.mmdb {
auto_reload 7d;
$geoip2_country_code country names en;
}
geoip2 /usr/local/Geoip2/GeoLite2-City.mmdb {
$geoip2_data_country "default=中国" source=$remote_addr country names zh-CN;
$geoip2_data_country_code country iso_code;
$geoip2_data_country_continent continent names zh-CN;
$geoip2_data_country_continent_code continent code;
$geoip2_data_province_name subdivisions 0 names zh-CN;
$geoip2_data_province_isocode subdivisions 0 names iso_code;
$geoip2_data_city city names zh-CN;
$geoip2_data_city_longitude location longitude;
$geoip2_data_city_latitude location latitude;
$geoip2_data_city_time_zone location time_zone;
$geoip2_data_country_en "default=United States" source=$remote_addr country names en;
$geoip2_data_country_code country iso_code;
$geoip2_data_country_continent_en continent names en;
$geoip2_data_country_continent_code continent code;
$geoip2_data_province_name_en subdivisions 0 names en;
$geoip2_data_province_isocode subdivisions 0 names iso_code;
$geoip2_data_city city names en;
$geoip2_data_city_longitude location longitude;
$geoip2_data_city_latitude location latitude;
$geoip2_data_city_time_zone location time_zone;
}
....
map $geoip2_data_country_code $allowed_country {
CN yes;
TW yes;
HK yes;
MO yes;
default no;
}
}
上面的配置是:中国的IP访问,返回的值是中文格式;中国以外的IP访问,返回的值是英文格式
$geoip2_data_country变量 指包含客户端IP地址所属的国家名称 (names en是英文格式;names zh-CN是中文格式)
defaults 指如果无法从GeoIP2数据库中获取到客户端IP地址所在国家信息,则将此变量的值设置为"中国"
source=$http_x_forwarded_for 指将变量$geoip2_data_country的源设置为请求头中的X-Forwarded-For字段.这个字段通常用于代理服务器之间传递客户端IP地址信息,最常使用的是remote_addr,但是如果有特殊需要或者要获取真实IP,可写入自己配置的真实IP变量或者第三方提供nginx变量
country names 指定义输出格式,表示返回国家的名称的格式
location = /ip-api/ {
if ( $geoip2_data_country_code ~* (CN|TW|HK|MO) ){
rewrite (.*) /ip-api/cn last;
}
rewrite (.*) /ip-api/en last;
}
location /ip-api/cn {
default_type application/json;
return 200 '{"current_ip":"$http_cf_connecting_ip","country":{"name": "$geoip2_data_country", "iso_code": "$geoip2_data_country_code", "continent": "$geoip2_data_country_continent","continent_code": "$geoip2_data_country_continent_code"},"province":{"name":"$geoip2_data_province_name","iso_code":"$geoip2_data_province_isocode"},"city":{"name":"$geoip2_data_city","timezone":"$geoip2_data_city_time_zone"},"location":{"longitude":"$geoip2_data_city_longitude","latitude":"$geoip2_data_city_latitude"}}';
}
location /ip-api/en {
default_type application/json;
return 200 '{"current_ip":"$http_cf_connecting_ip","country":{"name": "$geoip2_data_country_en", "iso_code": "$geoip2_data_country_code", "continent": "$geoip2_data_country_continent_en","continent_code": "$geoip2_data_country_continent_code"},"province":{"name":"$geoip2_data_province_name_en","iso_code":"$geoip2_data_province_isocode"},"city":{"name":"$geoip2_data_city","timezone":"$geoip2_data_city_time_zone"},"location":{"longitude":"$geoip2_data_city_longitude","latitude":"$geoip2_data_city_latitude"}}';
}
curl http://xx.xx.xx.xx/ip-api 即可显示当前ip的解析
总结
从上面的演示场景中,应该可以看到Geoip2的基本用法。如果要限制城市或有其他需求,可以根据自己的实际需求,进行修改和配置。
通过测试Geoip2,我发现虽然它提供了定位的各种参数,但对于在中国的城市级别的定位精度存在问题。因此,如果您需要做城市级别的访问限制,要仔细考虑是否适用。不过,对于根据需求给不同国家或地区,展示不同页面或功能或推广来说,GeoIP2仍然是个好选择。