0%

通过第三方IP库构建自己的增强版的ifconfig.me,查询myip

ifconfig.me

访问ifconfig.me会返回客户端的IP地址(其返回结果本身就没有换行符)

1
2
wsl2-gentoo ~ # curl ifconfig.me
2407:8207:1930:1600:4df2:5cd1:c2b7:f8adwsl2-gentoo ~ #

原生版

如果只是单纯返回客户端 IP 地址,Nginx 简单配置即可,例如

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name myip.wafcloud.cn;
access_log /var/log/nginx/myip.wafcloud.cn.log;

location / {
default_type text/plain;
return 200 $remote_addr\r\n;
}
}

效果如下

1
2
3
wsl2-gentoo ~ # curl myip.wafcloud.cn
22.11.15.145
wsl2-gentoo ~ #

增强版

除返回客户端 IP 地址外,还包括 IP 地址的地域属性、ASN、所属机构等(取决于你所选择的 IP 地址库及封装效果)。

本实现是通过 Nginx 模块实现,不需要任何编码。

考虑到个人需求、 IP 地址库精确度及更新频率等,这里选择三个地址库,来源于两个站点:

前期准备

相关软件下载及依赖安装

依赖安装(database/C library)

1
2
3
4
5
6
7
8
9
apt install libmaxminddb0

wget https://github.com/chrislim2888/IP2Location-C-Library/archive/refs/tags/8.6.1.tar.gz
tar xvf 8.6.1.tar.gz
cd IP2Location-C-Library-8.6.1
autoreconf -i -v --force
./configure
make
make install

下载并解压

1
2
3
4
5
6
7
wget https://nginx.org/download/nginx-1.24.0.tar.gz
wget https://www.openssl.org/source/openssl-3.2.1.tar.gz
wget https://www.zlib.net/zlib-1.3.1.tar.gz
wget https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.gz

wget https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/3.4.tar.gz
wget https://github.com/ip2location/ip2location-nginx/archive/refs/tags/8.6.0.tar.gz

Nginx 及模块编译、安装(根据实际情况处理依赖、路径等问题)

1
2
3
4
5
6
7
8
9
10
./configure --sbin-path=/usr/local/nginx/nginx \
--conf-path=/usr/local/nginx/nginx.conf \
--pid-path=/usr/local/nginx/nginx.pid \
--with-pcre=../pcre2-10.42 \
--with-zlib=../zlib-1.3.1 \
--with-http_ssl_module \
--with-openssl=../openssl-3.2.1\
--with-stream \
--add-dynamic-module=../ngx_http_geoip2_module-3.4 \
--add-dynamic-module=../ip2location-nginx-8.6.0
1
make -j`nproc` && make install

nginx.conf 关键配置(IP 库的路径按需调整)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
load_module modules/ngx_http_geoip2_module.so;
load_module modules/ngx_http_ip2location_module.so;

http {

geoip2 /opt/data/GeoLite2-City.mmdb {
# $variable_name [default=<value] [source=$variable_with_ip] path ...

$geoip2_country default="none" country names zh-CN;
$geoip2_province default="none" city names zh-CN;
$geoip2_city default="none" subdivisions 0 names zh-CN;
}

geoip2 /opt/data/GeoLite2-ASN.mmdb {
$geoip2_asn default="none" autonomous_system_number;
$geoip2_organization default="none" autonomous_system_organization;
}

ip2location_proxy_recursive on;
ip2location_proxy 0.0.0.0/0;
ip2location_database /opt/data/IP2LOCATION-LITE-DB3.IPV6.BIN;

server {
listen 80;
server_name myip.wafcloud.cn;
charset utf-8;
access_log logs/myip.wafcloud.cn.log main;

location / {
default_type text/plain;
return 200 "\n数据源: GeoLite2-City.mmdb\nIP: $remote_addr\nCountry: $geoip2_country\nProvince: $geoip2_province\nCity: $geoip2_city\nASN: $geoip2_asn\nOrganization: $geoip2_organization\n\n-------\n\n数据源: IP2LOCATION-LITE-DB3.IPV6.BIN\nIP: $remote_addr\nCountry: $ip2location_country_long\nRegion: $ip2location_region\nCity: $ip2location_city\n\n";
}
}
}

实现的效果(IP 地址进行了脱敏,并不是 IP 库精度问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
wsl2-gentoo ~ # curl myip.wafcloud.cn

数据源: GeoLite2-City.mmdb
IP: 22.11.15.145
Country: 中国
Province: 北京
City: 北京市
ASN: 4808
Organization: China Unicom Beijing Province Network

-------

数据源: IP2LOCATION-LITE-DB3.IPV6.BIN
IP: 22.11.15.145
Country: China
Region: Beijing
City: Beijing
更进一步,可查询指定 IP 的信息

只实现了 geoip2 的查询;ip2location 应该需要改源码(???)暂未实现. update: 已实现,参考最后patch

nginx.conf 的关键配置如下:geoip2 支持 source 参数,然后通过 map 指令映射待查询的 IP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
http {

map $uri $query_addr {
default $remote_addr;
"~*/ipinfo/(.*)" $1;
}

geoip2 /opt/data/GeoLite2-City.mmdb {
# $variable_name [default=<value] [source=$variable_with_ip] path ...

$geoip2_country default="none" source=$query_addr country names zh-CN;
$geoip2_province default="none" source=$query_addr city names zh-CN;
$geoip2_city default="none" source=$query_addr subdivisions 0 names zh-CN;
}

geoip2 /opt/data/GeoLite2-ASN.mmdb {
$geoip2_asn default="none" source=$query_addr autonomous_system_number;
$geoip2_organization default="none" source=$query_addr autonomous_system_organization;
}

ip2location_proxy_recursive on;
ip2location_proxy 0.0.0.0/0;
ip2location_database /opt/data/IP2LOCATION-LITE-DB3.IPV6.BIN;

server {
listen 80;
server_name myip.wafcloud.cn;
location ~* /ipinfo/(.*) {
default_type text/plain;
return 200 "
数据源: GeoLite2-City.mmdb
IP: $query_addr
Country: $geoip2_country
Province: $geoip2_province
City: $geoip2_city
ASN: $geoip2_asn
Organization: $geoip2_organization

数据源: IP2LOCATION-LITE-DB3.IPV6.BIN
IP: $query_addr
Country: $ip2location_country_long
Region: $ip2location_region
City: $ip2location_city\n\n";
}
}
}

实现的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wsl2-gentoo ~ # curl myip.wafcloud.cn/ipinfo/221.198.70.47

数据源: GeoLite2-City.mmdb
IP: 87.114.196.191
Country: United Kingdom
Province: Whitechapel
City: "none"
ASN: 20473
Organization: AS-VULTR

数据源: IP2LOCATION-LITE-DB3.IPV6.BIN
IP: 87.114.196.191
Country: United Kingdom of Great Britain and Northern Ireland
Region: England
City: London
IP 地址库更新

官方有提供相关的自动更新工具,个人更倾向于自行封装脚本按需更新

其它

结合 IP 地址库可以做很多事情,比如限制特定国家的访问、基于访客地域的特定推广等,当然这种环境建议购买商业库而非使用免费版。

ip2location-nginx-8.6.0.patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
diff --git a/ngx_http_ip2location_module.c b/ngx_http_ip2location_module.c
index 3ccb710..02f0dba 100644
--- a/ngx_http_ip2location_module.c
+++ b/ngx_http_ip2location_module.c
@@ -370,7 +370,21 @@ ngx_http_ip2location_get_records(ngx_http_request_t *r)

ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "IP address detected by IP2Location: %s", p);

- return IP2Location_get_all(gcf->handler, (char *)p);
+ //return IP2Location_get_all(gcf->handler, (char *)p);
+ u_char iparam[NGX_INET6_ADDRSTRLEN + 1];
+ ngx_http_variable_value_t* val;
+ ngx_str_t name = ngx_string("query_addr");
+ iparam[0]='\0';
+ val = ngx_http_get_variable(r, &name, ngx_hash_key(name.data, name.len));
+ if( val && !val->not_found && val->valid){
+ memcpy(iparam, val->data, val->len);
+ iparam[val->len]='\0';
+ }
+ if(iparam[0]!='\0'){
+ return IP2Location_get_all(gcf->handler, (char *)iparam);
+ } else {
+ return IP2Location_get_all(gcf->handler, (char *)p);
+ }
}

return NULL;