跳到内容
6️⃣nginx 通过配置文件阻止海外 ip 访问

6️⃣nginx 通过配置文件阻止海外 ip 访问

这里是半山灯的博客,这篇文章介绍了如何利用 nginx 配置文件阻止海外 IP 访问,以应对刷评论脚本的困扰。作者发现评论垃圾大多来自海外 IP,考虑到博客主要面向国内中文用户,决定屏蔽海外访问 artalk 评论功能。 文章先是介绍了通过 APNIC 获取海外 IP 列表的脚本方法,生成黑名单配置,并在 nginx 中引用。但测试发现部分海外 IP 未被覆盖,于是调整策略改为只允许国内 IP 访问。作者提供了新的脚本,从 APNIC 提取中国 IP 段生成白名单,再默认拒绝所有其他 IP,从而更精准地实现屏蔽。 配置完成后,通过 curl 测试确认海外 IP 返回 403,国内访问正常。作者建议可以设置定时任务更新 IP 列表,但选择手动维护。文末还分享了他实际使用的屏蔽配置片段,供读者参考。

nginx 通过配置文件阻止海外 ip 访问

master,这是我的小站,欢迎访问哦~~

说明

因为最近发现有不少刷评论的脚本,在 nginx 请求日志里面看了眼,都是海外的 ip,反正我的博客也是全中文。所以干脆把海外 ip 禁止 artalk 评论。

/etc/nginx/nginx.conf 中可以看到默认的日志路径,在里面能找到每一个转发的请求和其源 IP。其中 artak 新增评论的请求是 /api/add 路径

access_log  /var/log/nginx/access.log  main;

考虑到添加海外 ip 屏蔽可能会阻止一些真的在国外的朋友,如果你在阅读本站博客时,遇到相关问题无法直接评论与我交流,可以移步 github 随便找个我的仓库开个 issue 提问!

解决

APNIC 介绍

后文出现的网站是来自 APNIC (Asia Pacific Network Information Center),其是 IP 地址管理机构之一,负责亚洲、太平洋地区。

APNIC提供了每日更新的亚太地区IPv4,IPv6,AS号分配的信息表:
http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest
该文件的格式与具体内容参见:
http://ftp.apnic.net/pub/apnic/stats/apnic/README.TXT

脚本获取 ip

初步解决方法参考:https://www.cnblogs.com/guoyabin/p/14263732.html

原博主提供的脚本如下,可以下载所有海外 ip 列表并生成一个 nginx 配置,写入 /etc/nginx/blackip.conf

shell
#!/bin/bash

# 删除旧文件
rm -f legacy-apnic-latest black_`date +%F`.conf

# 下载 APNIC 旧版数据
wget http://ftp.apnic.net/apnic/stats/apnic/legacy-apnic-latest

# 处理数据并生成新的黑名单配置文件
awk -F '|' '{if(NR>2)printf("%s %s/%d%s\n","deny",$4,24,";")}' legacy-apnic-latest > black_`date +%F`.conf

# 删除旧的 Nginx 配置文件
rm -f /etc/nginx/blackip.conf

# 创建新的符号链接
ln -s $PWD/black_`date +%F`.conf /etc/nginx/blackip.conf

脚本执行后的效果如下

shell
[root@Alpine:/etc/nginx]# ll
total 88
lrwxrwxrwx 1 root root   34 Aug  11 16:03 blackip.conf -> /root/docker/black_2024-08-11.conf

文件内容如下

shell
[root@Alpine:/etc/nginx/conf.d]# cat ../blackip.conf
deny 128.134.0.0/24;
deny 128.184.0.0/24;
deny 128.250.0.0/24;
deny 129.60.0.0/24;
deny 129.78.0.0/24;
...

nginx 屏蔽海外 ip

参考原博主的做法,你可以将这个 blackip.conf/etc/nginx/nginx.conf 中的 http 模块里面 include,这样会阻止当前服务器所有反代的海外的请求。

include /etc/nginx/blackip.conf;

还可以在单个配置文件的 location 里面引用

conf
  location / {
      proxy_redirect off; # artalk 的 nginx 配置中必须有这个
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_set_header Upgrade-Insecure-Requests 1;
      proxy_set_header X-Forwarded-Proto https;

      include /etc/nginx/blackip.conf; # 引用配置

      proxy_pass http://127.0.0.1:14722;
}

修改后重启 nginx,没有报错就是 ok 了

shell
systemctl restart nginx

用海外的服务器试试能不能请求 artalk,用 artalk 后台登录地址 这个管理员登录页面来进行测试。

国内服务器请求结果如下,和浏览器打开的结果基本是一样(管理员登录界面)

shell
[root@Alpine:/etc/nginx/conf.d]# curl http://127.0.0.1:14722/sidebar/#/login
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Artalk Sidebar</title>
    <script type="module" crossorigin src="./assets/index-dba50be6ce6f.js"></script>
    <link rel="stylesheet" href="./assets/index-18398e6b.css">
  </head>
  <body>
    <div id="app"></div>

  </body>
</html>

海外服务器请求结果也是上面这样…… 然后发现是因为我的海外服务器 ip 压根不在那个 black 的 deny 列表里面

尝试把 ip 的网段给加进去,重启 nginx 再试试。完美处理!添加前能正常请求到,添加后就变成 403 了

shell
[root@Alpine:~]# curl http://127.0.0.1:14722/sidebar/#/login
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Artalk Sidebar</title>
    <script type="module" crossorigin src="./assets/index-dba50be6ce6f.js"></script>
    <link rel="stylesheet" href="./assets/index-18398e6b.css">
  </head>
  <body>
    <div id="app"></div>

  </body>
</html>
[root@Alpine:~]#
[root@Alpine:~]# curl http://127.0.0.1:14722/sidebar/#/login
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>
[root@Alpine:~]#

nginx 屏蔽非国内 ip

我前文提到了我的海外服务器的 ip 不在这个 deny 的 ip 列表里面,没有被屏蔽。

考虑到网上搜不到 legacy-apnic-latest 文件存放的是什么 ip 的信息,我决定换一个思路:allow 国内的 ip,拒绝所有非国内的 ip

获取国内 ip 列表 https://www.cnblogs.com/sentangle/p/13201770.html

下面这个 url 里面的 ip 地址标明了地区,我们只需要将其提取出来即可

http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest

这个文件里面的内容结构如下

等级机构|获得该IP段的国家/组织|资源类型|起始IP|IP段长度|分配日期|分配状态

我们只需要提取 CN 的所有 IP,然后允许他们,再 deny all 阻止其他 ip 就可以了

shell
#!/bin/bash

# 删除旧文件
rm -f delegated-apnic-latest blackcn_`date +%F`.conf

# 下载 APNIC 数据
wget http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest

# 处理数据并生成新的黑名单配置文件
awk -F'|' '/CN|ipv4/ { printf("%s %s/%d%s\n", "allow", $4, 32-log($5)/log(2), ";") }' delegated-apnic-latest > blackcn_$(date +%F).conf

# 删除旧的 Nginx 配置文件
rm -f /etc/nginx/blackcn.conf

# 创建新的符号链接
ln -s $PWD/blackcn_`date +%F`.conf /etc/nginx/blackcn.conf

执行这个脚本后,会生成 /etc/nginx/blackcn.conf 文件

shell
[root@Alpine:/etc/nginx]# ll
total 88
lrwxrwxrwx 1 root root   42 Aug  11 16:54 blackcn.conf -> /root/docker/nginx/blackcn_2024-08-11.conf
lrwxrwxrwx 1 root root   40 Aug  11 16:56 blackip.conf -> /root/docker/nginx/black_2024-08-11.conf

内容如下

conf
allow 223.248.0.0/14;
allow 223.252.128.0/17;
allow 223.254.0.0/16;
allow 223.255.0.0/17;
allow 223.255.236.0/22;
allow 223.255.252.0/23;
....

还是修改 nginx 单个站点配置文件的 location 中的内容

conf
  location / {
      proxy_redirect off;
  	proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_set_header Upgrade-Insecure-Requests 1;
      proxy_set_header X-Forwarded-Proto https;
# 允许所有国内 ip
  	include /etc/nginx/blackcn.conf;
  	deny all; # 阻止其他 ip

  	proxy_pass http://127.0.0.1:14722;
      }

先来试试不修改配置文件(不做任何 deny 和 allow 操作的情况下)海外 ip 请求结果

shell
[root@Alpine:~]# curl http://127.0.0.1:14722/#/login
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Artalk Sidebar</title>
    <script type="module" crossorigin src="./assets/index-dba50be6ce6f.js"></script>
    <link rel="stylesheet" href="./assets/index-18398e6b.css">
  </head>
  <body>
    <div id="app"></div>

  </body>
</html>

符合预期,正常请求出了登录页面的 html 文件。

添加如上修改后,重启 nginx,再次进行测试。这一次已经 403 阻止了,完美!

shell
[root@Alpine:~]# curl http://127.0.0.1:14722/#/login
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>

这里可以看到每一个路径的配置文件,参考上文所述,修改配置文件就可以了。

The end

你可以写个 crontab 让其定时执行脚本并重启 nginx,我个人还是选择人工处理了(什么时候想起来就去更新一下 ip 列表)

感谢本文中出现的博客的博主。没有他们的帮助,我无法编写出 shell 脚本。

分享我的拉黑 conf