nginx限制国外ip访问
从分析图上也可以看到,访问的来源哪都有,上次说把blog的访问限制一下,这下开始搞起来
下载ngx_http_geoip2_module模块
这里下载到/tmp目录下
cd /tmp/
[root@lishuai tmp]# git clone https://github.com/leev/ngx_http_geoip2_module.git
可以构建为静态模块,也可以构建为动态模块,静态模块需要编译完替换nginx文件,有点麻烦,这里就构建为动态模块
下载nginx源码
我的nginx版本是1.29.0 ,这里下载1.29.0的源码,保持一致
[root@lishuai tmp]# nginx -v
nginx version: nginx/1.29.0
[root@lishuai tmp]# wget https://nginx.org/download/nginx-1.29.0.tar.gz
构建为动态module,将生成objs/ngx_http_geoip2_module.so的
替换到nginx的module目录
./configure --with-compat --add-dynamic-module=/path/to/ngx_http_geoip2_module
make modules #只编译不安装
这是执行输出
[root@lishuai nginx-1.29.0]# ./configure \
> --with-compat \
> --add-dynamic-module=/tmp/ngx_http_geoip2_module
checking for OS+ Linux 4.18.0-193.el8.x86_64 x86_64
checking for C compiler ... found+ using GNU C compiler+ gcc version: 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC)
checking for gcc -pipe switch ... found
checking for -Wl,-E switch ... found
checking for gcc builtin atomic operations ... found
checking for C99 variadic macros ... found
checking for gcc variadic macros ... found
checking for gcc builtin 64 bit byteswap ... found
checking for unistd.h ... found
checking for inttypes.h ... found
checking for limits.h ... found
checking for sys/filio.h ... not found
checking for sys/param.h ... found
checking for sys/mount.h ... found
checking for sys/statvfs.h ... found
checking for crypt.h ... found
checking for Linux specific features
checking for epoll ... found
checking for EPOLLRDHUP ... found
checking for EPOLLEXCLUSIVE ... found
checking for eventfd() ... found
checking for O_PATH ... found
checking for sendfile() ... found
checking for sendfile64() ... found
checking for sys/prctl.h ... found
checking for prctl(PR_SET_DUMPABLE) ... found
checking for prctl(PR_SET_KEEPCAPS) ... found
checking for capabilities ... found
checking for crypt_r() ... found
checking for sys/vfs.h ... found
checking for BPF sockhash ... found
checking for SO_COOKIE ... found
checking for UDP_SEGMENT ... not found
checking for nobody group ... found
checking for poll() ... found
checking for /dev/poll ... not found
checking for kqueue ... not found
checking for crypt() ... not found
checking for crypt() in libcrypt ... found
checking for F_READAHEAD ... not found
checking for posix_fadvise() ... found
checking for O_DIRECT ... found
checking for F_NOCACHE ... not found
checking for directio() ... not found
checking for statfs() ... found
checking for statvfs() ... found
checking for dlopen() ... not found
checking for dlopen() in libdl ... found
checking for sched_yield() ... found
checking for sched_setaffinity() ... found
checking for SO_SETFIB ... not found
checking for SO_REUSEPORT ... found
checking for SO_ACCEPTFILTER ... not found
checking for SO_BINDANY ... not found
checking for IP_TRANSPARENT ... found
checking for IP_BINDANY ... not found
checking for IP_BIND_ADDRESS_NO_PORT ... found
checking for IP_RECVDSTADDR ... not found
checking for IP_SENDSRCADDR ... not found
checking for IP_PKTINFO ... found
checking for IPV6_RECVPKTINFO ... found
checking for IP_MTU_DISCOVER ... found
checking for IPV6_MTU_DISCOVER ... found
checking for IP_DONTFRAG ... not found
checking for IPV6_DONTFRAG ... found
checking for TCP_DEFER_ACCEPT ... found
checking for TCP_KEEPIDLE ... found
checking for TCP_FASTOPEN ... found
checking for TCP_INFO ... found
checking for accept4() ... found
checking for int size ... 4 bytes
checking for long size ... 8 bytes
checking for long long size ... 8 bytes
checking for void * size ... 8 bytes
checking for uint32_t ... found
checking for uint64_t ... found
checking for sig_atomic_t ... found
checking for sig_atomic_t size ... 4 bytes
checking for socklen_t ... found
checking for in_addr_t ... found
checking for in_port_t ... found
checking for rlim_t ... found
checking for uintptr_t ... uintptr_t found
checking for system byte ordering ... little endian
checking for size_t size ... 8 bytes
checking for off_t size ... 8 bytes
checking for time_t size ... 8 bytes
checking for AF_INET6 ... found
checking for setproctitle() ... not found
checking for pread() ... found
checking for pwrite() ... found
checking for pwritev() ... found
checking for strerrordesc_np() ... not found
checking for sys_nerr ... found
checking for localtime_r() ... found
checking for clock_gettime(CLOCK_MONOTONIC) ... found
checking for posix_memalign() ... found
checking for memalign() ... found
checking for mmap(MAP_ANON|MAP_SHARED) ... found
checking for mmap("/dev/zero", MAP_SHARED) ... found
checking for System V shared memory ... found
checking for POSIX semaphores ... not found
checking for POSIX semaphores in libpthread ... found
checking for struct msghdr.msg_control ... found
checking for ioctl(FIONBIO) ... found
checking for ioctl(FIONREAD) ... found
checking for struct tm.tm_gmtoff ... found
checking for struct dirent.d_namlen ... not found
checking for struct dirent.d_type ... found
checking for sysconf(_SC_NPROCESSORS_ONLN) ... found
checking for sysconf(_SC_LEVEL1_DCACHE_LINESIZE) ... found
checking for openat(), fstatat() ... found
checking for getaddrinfo() ... found
configuring additional dynamic modules
adding module in /tmp/ngx_http_geoip2_module
checking for MaxmindDB library ... found+ ngx_geoip2_module was configured
checking for PCRE2 library ... found
checking for zlib library ... found
creating objs/Makefile
Configuration summary+ using system PCRE2 library+ OpenSSL library is not used+ using system zlib librarynginx path prefix: "/usr/local/nginx"nginx binary file: "/usr/local/nginx/sbin/nginx"nginx modules path: "/usr/local/nginx/modules"nginx configuration prefix: "/usr/local/nginx/conf"nginx configuration file: "/usr/local/nginx/conf/nginx.conf"nginx pid file: "/usr/local/nginx/logs/nginx.pid"nginx error log file: "/usr/local/nginx/logs/error.log"nginx http access log file: "/usr/local/nginx/logs/access.log"nginx http client request body temporary files: "client_body_temp"nginx http proxy temporary files: "proxy_temp"nginx http fastcgi temporary files: "fastcgi_temp"nginx http uwsgi temporary files: "uwsgi_temp"nginx http scgi temporary files: "scgi_temp"
[root@lishuai nginx-1.29.0]# make modules
make -f objs/Makefile modules
make[1]: 进入目录“/tmp/nginx-1.29.0”
cc -c -fPIC -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I src/core -I src/event -I src/event/modules -I src/event/quic -I src/os/unix -I objs -I src/http -I src/http/modules \-o objs/addon/ngx_http_geoip2_module/ngx_http_geoip2_module.o \/tmp/ngx_http_geoip2_module/ngx_http_geoip2_module.c
cc -c -fPIC -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I src/core -I src/event -I src/event/modules -I src/event/quic -I src/os/unix -I objs -I src/http -I src/http/modules \-o objs/ngx_http_geoip2_module_modules.o \objs/ngx_http_geoip2_module_modules.c
cc -o objs/ngx_http_geoip2_module.so \
objs/addon/ngx_http_geoip2_module/ngx_http_geoip2_module.o \
objs/ngx_http_geoip2_module_modules.o \
-lmaxminddb \
-shared
make[1]: 离开目录“/tmp/nginx-1.29.0”
[root@lishuai nginx-1.29.0]# ls objs/
addon Makefile ngx_auto_headers.h ngx_http_geoip2_module_modules.o ngx_modules.c
autoconf.err ngx_auto_config.h ngx_http_geoip2_module_modules.c ngx_http_geoip2_module.so src
将模块拷贝到/usr/lib64/nginx/modules/
[root@lishuai nginx-1.29.0]# cp objs/ngx_http_geoip2_module.so /usr/lib64/nginx/modules/
修改nginx配置
修改 /etc/nginx/nginx.conf
加载模块,在文件顶部增加一行
load_module /usr/lib64/nginx/modules/ngx_http_geoip2_module.so;
测试配置并重新加载配置
nginx -t && nginx -s reload
准备 GeoIP2 数据库
从maxmind下载.mmdb
格式的ip数据库文件
创建/etc/nginx/geoip目录,将下载的GeoLite City数据库文件解压到/etc/nginx/geoip下
[root@lishuai geoip]# ls
GeoLite2-ASN.mmdb GeoLite2-City.mmdb GeoLite2-Country.mmdb
修改nginx配置在http段下增加
geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb {auto_reload 5m;$geoip2_city_name city names en;$geoip2_country_name country names en;}log_format main '$remote_addr $geoip2_country_name $geoip2_city_name - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for" "$scheme://$host$request_uri" ';
查看日志可以看到日志中已经打印了 访问者的国家和城市
[root@lishuai geoip]# tail -2 /var/log/nginx/access.log
74.7.35.63 United Kingdom - - - [31/Jul/2025:10:46:50 +0800] "GET / HTTP/1.1" 200 93514 "-" "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot" "74.7.35.63" "http://lishuai.fun/"
183.192.233.150 China Shanghai - - [31/Jul/2025:10:46:57 +0800] "GET /content.json?t=1753930017054 HTTP/1.1" 200 26268 "https://www.lishuai.fun/2023/05/05/kuboard-etcd-error/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "183.192.233.150" "http://www.lishuai.fun/content.json?t=1753930017054"
这里也可以设置中文显示城市,不过日志中打印的是UTF-8 编码的十六进制转义形式
$geoip2_city_name city names zh-CN;$geoip2_country_name country names zh-CN;
[root@lishuai geoip]# tail -2 /var/log/nginx/access.log
160.16.140.54 \xE6\x97\xA5\xE6\x9C\xAC \xE4\xB8\x9C\xE4\xBA\xAC - - [31/Jul/2025:11:00:05 +0800] "GET /intro/index-bg.jpg HTTP/1.1" 304 0 "https://lishuai.fun/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "160.16.140.54" "http://lishuai.fun/intro/index-bg.jpg"
160.16.140.54 \xE6\x97\xA5\xE6\x9C\xAC \xE4\xB8\x9C\xE4\xBA\xAC - - [31/Jul/2025:11:00:05 +0800] "GET /content.json?t=1753930805292 HTTP/1.1" 200 26268 "https://lishuai.fun/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "160.16.140.54" "http://lishuai.fun/content.json?t=1753930805292"
我们可以使用工具转换一下
配置限制访问
我想限制只允许国内ip访问,不允许国外ip访问,这里国外ip访问,我直接打印对方的ip和国家
修改/etc/nginx/nginx.conf ,在http段添加
geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb {auto_reload 5m;$geoip2_city_name city names en;$geoip2_country_name country names en;$geoip2_data_country_code country iso_code;}map $geoip2_data_country_code $allowed_country {default yes;CN no;}
在要限制的域名配置里添加,也即是在server段中添加,我这里是 /etc/nginx/conf.d/default.conf
error_page 403 = @deny403;location @deny403 {default_type text/html;return 200 "<html><body><h1>Access Denied</h1><p>Your IP: $remote_addr</p><p>Your Country: $geoip2_country_name</p></body></html>";}if ($allowed_country = yes) {return 403;#return 200 "<html><body><h1>Access Denied</h1><p>Your IP: $remote_addr</p><p>Your Country: $geoip2_country_name</p></body></html>";}
验证配置并重启
nginx -t && nginx -s reload
我机器开了代理,访问一下
不开代理,访问ok
最终
自己代理日常用的是日本节点,blog又部署在japan节点,代理配置为走规则访问的,浏览器访问blog都是走的代理,也懒得改规则,避免误伤自己,修改一下
map $geoip2_data_country_code $allowed_country {default yes;CN no;JP no;}
这样自己就可以正常访问了
总结
受限于ip数据库的准确性,肯定会误杀部分ip ,另外如果用cloudflare 代理了网站,那么访问会从cloudflare 走一圈,这时候又会拿不到真实的ip了,这个后续有时间再实践解决