boxmoe_header_banner_img

希望是美好的,也许是人间至善,而美好的事物永不消逝

文章导读

屏蔽中国大陆IPv6地址段和自动获取Cloudflare IP段


avatar
hydrangea 10 9 月, 2026 38

服务器使用了HE Tunnel来获得IPv6地址,但是HE的隧道连接中国大陆IPv6质量属实是有点差,所以有必要搞一个脚本批量屏蔽中国大陆IPv6地址段,防止Headscale、Derper和Minecraft连接自动选择更慢的IPv6网络。

#!/bin/bash
# 自动拉取并封禁中国大陆所有 IPv6 网段 (改进版)
set -euo pipefail

SET_NAME="cn_ipv6_block"
URL="https://www.ipdeny.com/ipv6/ipaddresses/blocks/cn.zone"
TMP_FILE="/tmp/cn_ipv6_zone.txt"

# 1. 检查并安装必要工具 (补全 iptables 依赖)
if ! command -v ipset &> /dev/null || ! command -v curl &> /dev/null || ! command -v ip6tables &> /dev/null; then
    echo "[+] 正在安装 ipset, curl 和 iptables..."
    if command -v apt-get &> /dev/null; then
        apt-get update && apt-get install -y ipset curl iptables
    elif command -v yum &> /dev/null; then
        yum install -y ipset curl iptables
    fi
fi

# 2. 下载最新的中国 IPv6 地址库 (-L 跟随重定向)
echo "[+] 正在下载最新的中国大陆 IPv6 地址段列表..."
if ! curl -sLf -o "$TMP_FILE" "$URL"; then
    echo "[!] 错误: 下载 IPv6 地址库失败,请检查网络。" >&2
    exit 1
fi

if [ ! -s "$TMP_FILE" ]; then
    echo "[!] 错误: 下载的地址库文件为空。" >&2
    exit 1
fi

# 3. 创建临时 ipset 集合以实现无缝热更新
echo "[+] 更新 ipset 集合..."
if ipset list "${SET_NAME}_tmp" &>/dev/null; then
    ipset flush "${SET_NAME}_tmp"
else
    ipset create "${SET_NAME}_tmp" hash:net family inet6 hashsize 4096 maxelem 65536
fi

# 4. 填充临时集合(允许单条格式校验失败,避免脚本异常中断)
while read -r cidr; do
    # 忽略空行和注释
    [[ -z "$cidr" || "$cidr" =~ ^# ]] && continue
    ipset add "${SET_NAME}_tmp" "$cidr" 2>/dev/null || true
done < "$TMP_FILE"

# 5. 原子替换 (Swap) 主集合并销毁临时集合
if ! ipset list "$SET_NAME" &>/dev/null; then
    ipset create "$SET_NAME" hash:net family inet6 hashsize 4096 maxelem 65536
fi
ipset swap "${SET_NAME}_tmp" "$SET_NAME"
ipset destroy "${SET_NAME}_tmp"

# 清理临时文件
rm -f "$TMP_FILE"

# 6. 挂载 ip6tables 拦截规则(防止重复添加)
if ! ip6tables -C INPUT -m set --match-set "$SET_NAME" src -j DROP 2>/dev/null; then
    echo "[+] 添加 ip6tables 封禁规则到 INPUT 链首部..."
    ip6tables -I INPUT -m set --match-set "$SET_NAME" src -j DROP
fi

echo "[✔] 成功!中国大陆 IPv6 地址库(共 $(ipset list $SET_NAME | grep -c '/') 条)已成功加载并拦截。"

另外,博客域名使用了Cloudflare proxy,所以源站应当拒绝Cloudflare之外的IP连接。这里也使用脚本获得Cloudflare的IP段,并在Nginx中配置仅允许该IP段的连接:

vim /usr/local/bin/update_cloudflare_ips.sh
#!/bin/bash

# 定义生成规则文件的路径
CONF_FILE="/etc/nginx/cloudflare_allow.conf"
TEMP_FILE="/tmp/cloudflare_allow.tmp"

# 清空临时文件
> "$TEMP_FILE"

echo "# Cloudflare IPv4" >> "$TEMP_FILE"
curl -s https://www.cloudflare.com/ips-v4 | awk '{print "allow " $0 ";"}' >> "$TEMP_FILE"

echo -e "\n# Cloudflare IPv6" >> "$TEMP_FILE"
curl -s https://www.cloudflare.com/ips-v6 | awk '{print "allow " $0 ";"}' >> "$TEMP_FILE"

# 检查拉取到的 IP 列表是否合法(防止网路异常拉到空文件导致 deny all 把 Cloudflare 也关了)
if grep -q "allow 173.245.48.0/20;" "$TEMP_FILE"; then
    mv "$TEMP_FILE" "$CONF_FILE"

    # 验证 Nginx 语法并重载 Nginx
    nginx -t && systemctl reload nginx
    echo "[$(date)] Cloudflare IP 列表已更新并已重载 Nginx"
else
    echo "[$(date)] 错误:未能获取有效的 Cloudflare IP,本次未更新。"
    rm -f "$TEMP_FILE"
fi

以上是更新脚本;在Nginx的sites-available中,在需要的站点加入:

server {
...
#主要是这两行
include /etc/nginx/cloudflare_allow.conf;
deny all;

#以下是location段
location / {
...

Cloudflare更新IP频率极低,所以用Crontab设置一个每月执行脚本的计划任务就可以了。



评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码