使用搬瓦工api操作nftables实现更新白名单ip:修订间差异
更多语言
更多操作
EliToviyah(留言 | 贡献) 小 修改一些顺序 |
无编辑摘要 |
||
| 第919行: | 第919行: | ||
如果成功更新ip了 就可以把允许ssh的端口删掉了就真正变成白名单模式了 | 如果成功更新ip了 就可以把允许ssh的端口删掉了就真正变成白名单模式了 | ||
[[Category:500 常见应用指南 — Application Guides]] | |||
2026年1月26日 (一) 09:38的最新版本
请注意不要泄漏API_Key 任何时候都不要泄露 防止服务器和API被盗用 应当在安全的设备上使用API 防止API_Key被盗取
根据可实时知晓ip变化的办法不同 您需要的工具也不相同..最多需要多一台本地的Linux服务器(不是防火墙所在的那个服务器哦)
使用前准备
切换到root或有高权限的用户
输入whoami(蓝色输出)或者查看 @之前的名字(绿色框)就是使用的用户
如果是root就是root就可以了
如果不是
你输入 nft list ruleset 或者 sudo nft list ruleset 输入你的密码 和上图一样 说明没权限….
尝试切换到root账户
输入 su - 然后输入 root 用户的密码。输入的密码不会显示 输入好后按回车就行 变成root就说明成功了
防火墙部分
输入 sudo nft list ruleset
你的防火墙中应该有一个空白的链 专用户白名单 然后在含有粉色框框(主要是含有hook input就行)代码的链中 jump这个链 另外先不要改成policy drop;
如果没有链的话 如何创建链
sudo nft add chain inet my_firewall allowed_ip
请根据自己的表的类型inet 表的名字 my_firewall 自行修改哦
allowed_ip可以改成自己想要的名字 比如sudo nft add chain inet my_firewall whiteip
要先有链 才能jump 链的名字
如何添加一条jump allowed_ip 规则呢
sudo nft add rule inet my_firewall my_input jump allowed_ip
还是一样要修改成自己的表的类型 表的名字 刚刚创建的链的名字
要把这个jump allowed_ip 添加到 含有粉色代码的chain里面也就是(my_input)
服务器部分
创建一个脚本 用 API 调用这个脚本就好了 免去了 API中过多的指令
如果你是从 服务器安全-防火墙 nftables 那文章一步一步 没有修改防火墙的表 链 的话 直接复制代码框的全部内容 粘贴到ssh里面 按回车 文件就创建好了
如果你修改了防火墙的表和链的名字 或不是根据 那篇文章来的
请修改下面代码中的内容 根据自身情况修改 只需要修改=后面 “” 双引号里面的内容
- TABLE_NAME="my_firewall"
- 这个是表的名字 蓝色框框
- CHAIN_NAME="allowed_ip"
- 这个是链的名字 绿色框框 这个链的名字一定不是你 包含 hook input 粉色这一行的那个链..因为会被清空..然后就会可能导致防火墙规则混乱…一定要是另外一个专门的链
- FAMILY="inet"
- 这个是表类型 红色框框
示例
如示例中的图就要改成这样的
CHAIN_NAME="allowed_mobile" TABLE_NAME="filter"
FAMILY="inet" 不变
如果是table ip filter就把FAMILY="inet" 改成 FAMILY="ip"
这里还应该输入 ls /root/update_fw.sh
检查/root目录是否有一个叫update_fw.sh的文件 如果有的话你也需要修改 避免被覆盖
上面 蓝色框框的 说明 有存在的同名文件 那你就需要修改 成 > /root/别的文件名.sh那么下文中的所有/root/update_fw.sh都要改成你自己的文件名
下面红色框框说明没有找到这个文件就 不用改名了 直接使用就行
在下面的代码框里的对应位置改 可以先复制到记事本上 改完再全选重新复制 粘贴到ssh里面 回车
代码框的脚本如下:
cat << 'EOF' > /root/update_fw.sh
#!/bin/bash
# --- 用户配置区 ---
TABLE_NAME="my_firewall"
CHAIN_NAME="allowed_ip"
FAMILY="inet"
# --- 获取参数并剔除所有空格 ---
IP1=$(echo "$1" | tr -d '[:space:]')
IP2=$(echo "$2" | tr -d '[:space:]')
VALID_IPS=()
NFT_TYPES=()
SUCCESS_LIST=""
FAILED_LIST=""
# --- IP 校验函数 ---
check_ip() {
local input=$1
[[ -z "$input" ]] && return 1
local raw_ip="${input%/*}"
local mask="${input#*/}"
# 自动识别默认掩码
if [[ "$input" != */* ]]; then
if [[ "$raw_ip" =~ : ]]; then mask=128; else mask=32; fi
fi
# 1. IPv4 校验
# 原来的 2[0-4][0-5] 改为了 2[0-4][0-9]
if [[ "$raw_ip" =~ ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]; then
[[ "$mask" -lt 1 || "$mask" -gt 32 ]] && return 1
echo "ip|$raw_ip/$mask"
return 0
fi
# 2. IPv6 校验 (简化版正则,兼容性更好)
if [[ "$raw_ip" =~ ^(([0-9a-fA-F]{1,4}:){1,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|:(:[0-9a-fA-F]{1,4}){1,7}|::|([0-9a-fA-F]{1,4}:){1,7}:[0-9a-fA-F]{1,4})$ ]]; then
[[ "$mask" -lt 1 || "$mask" -gt 128 ]] && return 1
echo "ip6|$raw_ip/$mask"
return 0
fi
return 1
}
process_arg() {
local arg=$1
[ -z "$arg" ] && return
local res
res=$(check_ip "$arg")
if [ $? -eq 0 ]; then
local full_ip="${res#*|}"
VALID_IPS+=("$full_ip")
NFT_TYPES+=("${res%|*}")
SUCCESS_LIST+="$full_ip "
else
FAILED_LIST+="$arg "
fi
}
process_arg "$IP1"
process_arg "$IP2"
# --- 极简反馈逻辑 ---
if [ ${#VALID_IPS[@]} -eq 0 ]; then
echo "Error: No valid IP provided ($FAILED_LIST)"
exit 1
fi
# 执行 Nftables 操作
nft flush chain $FAMILY $TABLE_NAME $CHAIN_NAME || { echo "Error: NFT chain not found"; exit 1; }
for i in "${!VALID_IPS[@]}"; do
nft add rule $FAMILY $TABLE_NAME $CHAIN_NAME ${NFT_TYPES[$i]} saddr ${VALID_IPS[$i]} accept
done
# 成功返回
echo "OK: Allowed $SUCCESS_LIST"
EOF
解释一下代码框里面指令的意思
cat(Concatenate):原本是用来“查看”或“拼接”内容的,但在这种组合中,它负责接收你输入的一大段文字。<<(输入重定向):这叫“在此处开始读取”。它告诉系统:“别去翻别的文件了,接下来的内容就是我要给你的,直到我遇到结束标记为止。”'EOF'(End Of File):- 开始标志:它是你自定义的一个“暗号”。
- 为什么要加单引号
' '?: 加了单引号,系统就会“原样搬运”中间的内容,不会去解析里面的$变量。这对于写入包含变量的脚本至关重要,否则这些变量在写入文件前就会被当前系统搞乱。
>(覆盖写入):它的意思是“清空目标文件并把内容倒进去”。/root/update_fw.sh:这是目的地。系统会在指定路径创建或覆盖这个脚本文件。- 中间的内容 就是要写入文件的内容 是我们的核心代码
- 最后的
EOF:这是“结束暗号”。系统看到它,就知道“打包”结束了,正式保存文件。
我们可以看到最后蓝色框的时候 有点混乱 没事的 直接按回车就行了
然后我们可以输入 cat /root/update_fw.sh 查看一下文件内容 确认有写入内容就好了
创建好后
你可以先输入 ls -l /root/update_fw.sh
这个意思是 以长格式 查看/root/update_fw.sh 文件的信息
- 第一位 - 代表是文件的意思 d是目录
- 第二位到第四位 rw- 就是 拥有这个文件的人的权限 第一个代表的是读取 如果有这个权限显示r如果没有就是-第二个是写入(编辑)的权限有这个权限就显示w没有就是-第三位是执行(运行)这个文件的权限有就显示x没有就显示- 这里显示-证明在chmod之前是没有执行权限的
r--这个文件所属组 的权限 同上 第一位是读取r说明有 第二位是-说明没有写入权限 第三位是-说明没有执行权限r--这个是其他人 也是一样的 有读取权限 没有写入和执行权限- 1 代表这个文件在磁盘上只有这一个‘
- root root 第一个root 代表拥有这个文件的人 第二个 root 代表这个文件属于root组
- 2675 文件大小Byte
- Jan 22 01:07 修改时间
- /root/update_fw.sh 文件信息的文件
为了安全取消掉其他的权限我们输入
chmod 700 /root/update_fw.sh
这个指令的意思是 设置这个文件的权限是700 700的意思就是只有这个文件的拥有人(root ls -l看到的)才可以读取写入和执行 其他的两个0代表 所属组没有任何权限和 其他所有人没有任何权限。
这样,只有 root 用户能看到这个脚本的内容,也只有 root 能运行它。
调用API部分
获取 搬瓦工服务器的API_Key和veid
请参考 搬瓦工api使用
使用搬瓦工API触发服务器上的脚本变更防火墙nftables的chain 从而实现动态更新白名单ip
ipv6的网段 V6_MASK
这个..如果你想同时更新ipv6的白名单 有一个问题
就是ipv6 每个设备都有一个ipv6地址(各不相同) ipv4的时候你用电脑手机连接到同一个路由器的话一般都是走的同一个ipv4地址 而ipv6不同…所以如果你只是更新了你这个设备的ipv6地址的话…别的设备仍然不在白名单中…这就需要网段
请登录路由器
找到这个前缀长度…把V6_MASK改成60就行了这样就是说 连接路由器的 都可以进白名单了
所有的代码中都有一个可以修改的变量.. V6_MASK="/64" 单个设备请改为 /128 一些地区可能是48 但48有点危险范围太大 你的邻居可能也在这之中 那为什么是64呢 因为我的是64……哦 我记错了 我是60….
可实时更新ip变化的办法
Linux系
OpenWrt类
检查是否安装了curl
输入 curl 如果显示 -ash: curl not found
安装curl和证书 为了访问https网站
opkg update && opkg install curl ca-bundle
curl 8.12.1有这样的版本号 有IPv6 有ssl 确保有他们就可以啦
检查 获取ip 的url是否有效
在使用前先在ssh里面输入
curl -s --max-time 5 https://ifconfig.me 进行测试
看看能不能获取ip地址 红色方块里面的就是ip地址 如果不能….
在后续的代码块中修改这两个=后面的内容
CHECK_URL_V4="https://ifconfig.me"
改成
CHECK_URL_V4=”https://ident.me”
CHECK_URL_V6="https://ifconfig.me"
改成
CHECK_URL_V6=” http://v6.ipip.net”
开始
在 OpenWrt 中,每当接口(WAN)状态发生变化(比如重连、获取到新 IP、掉线)时,系统会自动触发对应的脚本。 OpenWrt 的网络热插拔脚本存放在: /etc/hotplug.d/iface/
进入目录:cd /etc/hotplug.d/iface/
创建脚本:
修改下面代码 你可以先复制到记事本或者别的文本编辑工具里面 最前面的
export VEID="你的VEID" export API_KEY="你的API_KEY"
改成类似这样的格式
export VEID="10202" export API_KEY="private_xxxxx"
请根据你实际的veid和API_Key修改
这两个如果你知道干嘛的也可以改
保存上传的ip防止重复调用
CACHE_FILE="/etc/config/vps_last_ip"
把执行过程和结果保存到这个文件里
API LOG_FILE="/tmp/vps_sync_error.log"
最后要检查的 输入 ls /etc/hotplug.d/iface/99-sync-vps-firewall 检查一下 有没有同名的其他脚本
下图就是没有 就不需要修改
下图就是有 那么你就要换一个名字
/etc/hotplug.d/iface/99-这里改成你想要的名字
比如 /etc/hotplug.d/iface/99-sync-vps-firewall1
修改后…复制粘贴到ssh里面 回车就创建好脚本了
代码块如下:
cat << 'EOF' > /etc/hotplug.d/iface/99-sync-vps-firewall
#!/bin/sh
# 只要是接口启动(ifup)就触发
[ "$ACTION" = "ifup" ] || exit 0
# --- 用户配置区 ---
export VEID="你的VEID"
export API_KEY="你的API_KEY"
# 用户可以分别设置获取地址(可以相同,也可以不同)
CHECK_URL_V4="https://ifconfig.me"
CHECK_URL_V6="https://ifconfig.me"
V6_MASK="/64" # IPv6 掩码
CACHE_FILE="/etc/config/vps_last_ip"
LOG_FILE="/tmp/vps_sync.log"
MAX_LINES=100
# --- 文件初始化与清理函数 ---
init_files() {
sleep 10
# 1. 处理日志文件
if [ ! -f "$LOG_FILE" ]; then
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
echo "$(date): [系统] 初始日志文件已创建" >> "$LOG_FILE"
else
# 日志滚动清理逻辑
local current_lines=$(wc -l < "$LOG_FILE")
if [ "$current_lines" -gt "$MAX_LINES" ]; then
echo "$(tail -n 50 "$LOG_FILE")" > "$LOG_FILE"
echo "$(date): [系统] 日志滚动清理完成" >> "$LOG_FILE"
fi
fi
# 2. 处理缓存文件及其目录
local cache_dir=$(dirname "$CACHE_FILE")
if [ ! -d "$cache_dir" ]; then
mkdir -p "$cache_dir"
echo "$(date): [系统] 创建缓存目录: $cache_dir" >> "$LOG_FILE"
fi
if [ ! -f "$CACHE_FILE" ]; then
touch "$CACHE_FILE"
# 初始化空变量,防止脚本第一次读取时报错
echo "LAST_IP4=\"\"" > "$CACHE_FILE"
echo "LAST_IP6=\"\"" >> "$CACHE_FILE"
echo "$(date): [系统] 初始缓存文件已创建" >> "$LOG_FILE"
fi
}
# --- IPv4 暴力获取函数 ---
get_v4() {
local count=0
while [ $count -lt 10 ]; do
local res=$(curl -s --max-time 5 "$CHECK_URL_V4" 2>/dev/null)
if echo "$res" | grep -Eq "^([0-9]{1,3}\.){3}[0-9]{1,3}$"; then
echo "$res" && return 0
fi
count=$((count + 1))
sleep 2
done
return 1
}
# --- IPv6 暴力获取函数 ---
get_v6() {
local count=0
while [ $count -lt 10 ]; do
local res=$(curl -s --max-time 5 "$CHECK_URL_V6" 2>/dev/null)
if echo "$res" | grep -q ":"; then
echo "${res}${V6_MASK}"
return 0
fi
count=$((count + 1))
sleep 1
done
ip6=$(ip -6 addr show scope global | grep inet6 | awk '{print $2}' | cut -d/ -f1 | grep -E '^(2|3)' | head -n 1)
if [ -n "$ip6" ]; then
echo "${ip6}${V6_MASK}"
return 0
fi
return 1
}
# 1. 初始化
init_files
# 2. 执行双栈并行/独立获取
CURRENT_IP4=$(get_v4)
CURRENT_IP6=$(get_v6)
# 3. 基础校验:至少得有一个 IP 吧
if [ -z "$CURRENT_IP4" ] && [ -z "$CURRENT_IP6" ]; then
echo "$(date): [错误] 尝试10次后仍无法获取到任何有效IP" >> "$LOG_FILE"
exit 1
fi
# 4. 缓存对比 (防重复刷 API)
[ -f "$CACHE_FILE" ] && . "$CACHE_FILE"
if [ "$CURRENT_IP4" = "$LAST_IP4" ] && [ "$CURRENT_IP6" = "$LAST_IP6" ]; then
exit 0
fi
# 5. 调用 API
CMD="bash /root/update_fw.sh $CURRENT_IP4 $CURRENT_IP6"
RESPONSE_FULL=$(curl -s -w "%{http_code}" -X POST \
--data "veid=${VEID}&api_key=${API_KEY}" \
--data-urlencode "command=${CMD}" \
"https://api.64clouds.com/v1/basicShell/exec")
# 6. 解析结果
HTTP_CODE="${RESPONSE_FULL:${#RESPONSE_FULL}-3}"
RESPONSE_JSON="${RESPONSE_FULL:0:${#RESPONSE_FULL}-3}"
RESPONSE_CLEAN=$(echo "$RESPONSE_JSON" | tr -d '\n\r ')
ERROR_CODE=$(echo "$RESPONSE_CLEAN" | grep -o '"error":[0-9]*' | cut -d: -f2)
MESSAGE=$(echo "$RESPONSE_CLEAN" | grep -o '"message":"[^"]*"' | sed 's/"message":"//;s/"$//')
# 7. 写入中文日志
if [ "$HTTP_CODE" = "200" ] && [ "$ERROR_CODE" = "0" ]; then
echo "LAST_IP4=\"$CURRENT_IP4\"" > "$CACHE_FILE"
echo "LAST_IP6=\"$CURRENT_IP6\"" >> "$CACHE_FILE"
echo "$(date): [成功] IP变更同步成功。IPv4: $CURRENT_IP4, IPv6: $CURRENT_IP6. VPS反馈: $MESSAGE" >> "$LOG_FILE"
echo "同步成功: $MESSAGE"
else
[ -z "$MESSAGE" ] && MESSAGE="请求异常: $RESPONSE_JSON"
echo "$(date): [失败] 同步失败。详细原因: $MESSAGE" >> "$LOG_FILE"
echo "同步失败: $MESSAGE"
fi
EOF
输入chmod 755 /etc/hotplug.d/iface/99-sync-vps-firewall赋予执行权限
输入 ls -l /etc/hotplug.d/iface/99-sync-vps-firewall 查看赋予权限是否成功
cat /tmp/vps_sync_error.log
查看是否成功
前面的蓝色框框说成功指的是API成功了 里面的ip是本地上传给服务器更新的ip 后面红色框框 OK才是防火墙成功的意思 后面的ip就是更新的ip 如果有一个ip无效或者…就不会显示了
常见错误 
身份验证失败 意思就是说 veid 或者 api_key 的内容错误
没有找到链
你可以看到 蓝色框的是防火墙指令..红色框是表
可能是 表(inet my_firewall) 或者 链(allowed_ip) 的名字和服务器防火墙的对不上 你可以去服务器nft list ruleset看一下
华硕路由器 官方固件
因为官方固件没有提供 重新连接网络 后执行脚本的功能 所以我们需要一台本地的Linux服务器了(不是防火墙所在的那个服务器哦)…
查看Linux服务器的ip (本地的局域网内的)
ssh连接的那个ip就是了…
如果你忘记了 输入 ip addr 找个eth0或者 ens18 这样 一般都是默认网络接口 里面的ip地址 蓝色框框
借助监听远程日志触发API更新防火墙白名单IP
用浏览器登陆你的路由器后台 输入用户名和密码 一般的地址是 192.168.1.1 如果不是可以打开 http://www.asusrouter.com/ 试试
远程记录服务器 输入 你的 本地局域网linux服务器的ip
远程记录服务器端口 默认514 如果你的linux服务器占用514的话 就改一下
如何查看是否占用
ssh连接那台服务器 切换root账户
- 输入命令:
su -(注意:su 后面有个空格和减号,这很重要,代表同时切换环境变量) - 输入 Root 密码(输入时看不见)。
- 此时你的提示符会变成
#,代表你又是 Root 了。
如果你没有安装sudo就会提示
那就不需要输入sudo 了 直接
ss -tunlp | grep :514
这样有输出 就是被占用了 占用的话换一个就行了
这样没有输出 就是没占用
然后点击 应用本页面设置
另外一台Linux服务器部分 不是防火墙所在的那个服务器哦
ssh连接后
切换到root账户
- 输入命令:
su -(注意:su 后面有个空格和减号,这很重要,代表同时切换环境变量) - 输入 Root 密码(输入时看不见)。
- 此时你的提示符会变成
#,代表你又是 Root 了。
安装socat
socat的功能很强大 是一个多功能的网络工具 我们这里用于接收路由器发过来的日志…
Debian / Ubuntu / PVE / Armbian 系:
apt update && apt install socat -y
Red Hat / CentOS / Rocky Linux 系:
yum install socat -y
新一些Red Hat/CentOS/Rocky Linux希的系统可以使用dnf install socat -y
安装后 输入 socat -V 确认安装成功
检查 获取ip 的url是否有效
在使用前先在ssh里面输入
curl -s --max-time 5 https://ifconfig.me 进行测试
看看能不能获取ip地址 红色方块里面的就是ip地址 如果不能….
在代码块中修改这两个=后面的内容
CHECK_URL_V4="https://ifconfig.me"
改成
CHECK_URL_V4=”https://ident.me”
CHECK_URL_V6="https://ifconfig.me"
改成
CHECK_URL_V6=” http://v6.ipip.net”
创建脚本
修改下面代码块 的代码 你可以复制到记事本或者其他文本编辑器里面 修改
最前面的
export VEID="你的VEID" export API_KEY="你的API_KEY"
改成类似这样的格式
export VEID="10202" export API_KEY="private_xxxxx"
请根据你实际的veid和API_Key修改
这两个如果你知道干嘛的也可以改 CACHE_FILE="/etc/config/vps_last_ip" 保存上传的ip防止重复调用API LOG_FILE="/tmp/vps_sync_error.log" 把执行过程和结果保存到这个文件里
根据刚才测试的结果来决定要不要替换
CHECK_URL_V4="https://ifconfig.me" CHECK_URL_V6="https://ifconfig.me"
V6_MASK="/64" 也可以修改 详情看上方的 ipv6的网段 V6_MASK
最后要检查的 输入 ls /root/checkip.sh 检查一下 有没有同名的其他脚本
下图就是没有 就不需要修改
下图这样就是有 就需要修改/root/checkip.sh 比如修改成 /root/checkip1.sh
修改后
全部选择 复制粘贴 到ssh里面 按回车
代码块:
cat << 'EOF' > /root/checkip.sh
#!/bin/bash
# --- 用户配置区 ---
export VEID="你的VEID"
export API_KEY="你的API_KEY"
# 获取 IP 的地址
CHECK_URL_V4="https://ifconfig.me"
CHECK_URL_V6="https://ifconfig.me"
V6_MASK="/64" # IPv6 掩码
CACHE_FILE="/etc/config/vps_last_ip"
LOG_FILE="/var/log/vps_sync.log"
MAX_LINES=100 # 日志保留行数
# --- 1. 初始化文件与目录 ---
init_files() {
# 处理日志文件
if [ ! -f "$LOG_FILE" ]; then
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
else
# 日志滚动清理
local current_lines=$(wc -l < "$LOG_FILE")
if [ "$current_lines" -gt "$MAX_LINES" ]; then
echo "$(tail -n 100 "$LOG_FILE")" > "$LOG_FILE"
echo "$(date): [系统] 日志滚动清理完成 (保留末尾100行)" >> "$LOG_FILE"
fi
fi
# 处理缓存目录及文件
local cache_dir=$(dirname "$CACHE_FILE")
[ ! -d "$cache_dir" ] && mkdir -p "$cache_dir"
if [ ! -f "$CACHE_FILE" ]; then
echo "LAST_IP4=\"\"" > "$CACHE_FILE"
echo "LAST_IP6=\"\"" >> "$CACHE_FILE"
fi
}
# --- 2. IPv4 暴力获取函数 ---
get_v4() {
local count=0
while [ $count -lt 10 ]; do
local res=$(curl -s4 --max-time 5 "$CHECK_URL_V4" 2>/dev/null)
if echo "$res" | grep -Eq "^([0-9]{1,3}\.){3}[0-9]{1,3}$"; then
echo "$res" && return 0
fi
count=$((count + 1))
sleep 2
done
return 1
}
# --- 3. IPv6 暴力获取函数 ---
get_v6() {
local count=0
while [ $count -lt 10 ]; do
local res=$(curl -s6 --max-time 5 "$CHECK_URL_V6" 2>/dev/null)
if echo "$res" | grep -q ":"; then
echo "${res}${V6_MASK}"
return 0
fi
count=$((count + 1))
sleep 4
done
ip6=$(ip -6 addr show scope global | grep inet6 | awk '{print $2}' | cut -d/ -f1 | grep -E '^(2|3)' | head -n 1)
if [ -n "$ip6" ]; then
echo "${ip6}${V6_MASK}"
return 0
fi
return 1
}
# --- 4. 核心同步逻辑 ---
sync_ip() {
# 刚收到日志时,给网络层 5-10 秒的初始化时间
sleep 10
# 获取当前 IP
CURRENT_IP4=$(get_v4)
CURRENT_IP6=$(get_v6)
# 基础校验
if [ -z "$CURRENT_IP4" ] && [ -z "$CURRENT_IP6" ]; then
echo "$(date): [错误] 尝试10次后仍无法获取到任何有效IP" >> "$LOG_FILE"
return 1
fi
# 读取缓存并对比
. "$CACHE_FILE"
if [ "$CURRENT_IP4" = "$LAST_IP4" ] && [ "$CURRENT_IP6" = "$LAST_IP6" ]; then
echo "$(date): [跳过] IP 未发生变化,取消同步" >> "$LOG_FILE"
return 0
fi
# 调用 API
CMD="bash /root/update_fw.sh $CURRENT_IP4 $CURRENT_IP6"
local RESPONSE_FULL=$(curl -s -w "%{http_code}" -X POST \
--data "veid=${VEID}&api_key=${API_KEY}" \
--data-urlencode "command=${CMD}" \
"https://api.64clouds.com/v1/basicShell/exec")
# 解析结果
local HTTP_CODE="${RESPONSE_FULL:${#RESPONSE_FULL}-3}"
local RESPONSE_JSON="${RESPONSE_FULL:0:${#RESPONSE_FULL}-3}"
# 核心解析:压缩 JSON 并提取字段
local CLEAN_JSON=$(echo "$RESPONSE_JSON" | tr -d '\n\r' | tr -s ' ')
local ERROR_CODE=$(echo "$CLEAN_JSON" | sed -n 's/.*"error":[ ]*\([0-9]*\).*/\1/p')
local MESSAGE=$(echo "$CLEAN_JSON" | sed -n 's/.*"message":[ ]*"\(.*\)"[ ]*}.*/\1/p' | sed 's/\\//g')
# 写入日志
if [ "$HTTP_CODE" = "200" ] && [ "$ERROR_CODE" = "0" ]; then
echo "LAST_IP4=\"$CURRENT_IP4\"" > "$CACHE_FILE"
echo "LAST_IP6=\"$CURRENT_IP6\"" >> "$CACHE_FILE"
echo "$(date): [成功] 同步成功。IPv4: $CURRENT_IP4, IPv6: $CURRENT_IP6. VPS反馈: $MESSAGE" >> "$LOG_FILE"
else
[ -z "$MESSAGE" ] && MESSAGE="$CLEAN_JSON"
echo "$(date): [失败] 同步失败。HTTP: $HTTP_CODE, ERROR: $ERROR_CODE, 原因: $MESSAGE" >> "$LOG_FILE"
fi
}
# --- 5. 监听入口 ---
init_files
# 如果直接运行脚本(无管道输入),执行一次同步
if [ -t 0 ]; then
sync_ip
else
# 如果有管道输入(来自 socat),则监听日志触发
while IFS= read -r line; do
if echo "$line" | grep -qi 'local *IP address'; then
echo "$(date): [触发] 检测到路由器拨号日志" >> "$LOG_FILE"
sync_ip
fi
done
fi
EOF
粘贴之后有点混乱 不用管 直接回车就行了
然后
检查一下 输入 cat /root/checkip.sh 有内容就好了
授予运行权限 chmod +x /root/checkip.sh
然后输入 ls -l /root/checkip.sh 查看多了三个x 就对了 如果像红色框框没有x 就不对 请重试 chmod +x /root/checkip.sh
然后你就可以输入
/root/checkip.sh
如果第一次测试成功了 以后想测试的话运行这个 rm /etc/config/vps_last_ip && /root/checkip.sh
需要rm /etc/config/vps_last_ip删除本地保留的ip记录 就是说如果这个ip记录和获取到的一样是不会运行api执行脚本的
测试一下 相当于是手动更新..不是自动更新
不会有内容输出
需要查看日志 cat /var/log/vps_sync.log
如果是下图那样就是成功了 要看红色的 红色框是vps服务器返回的OK 就是真的成功了 Allowed:后面是成功添加的ip 一个是ipv4 一个是ipv6的
蓝色框的成功说的是API使用成功 提交的IP是哪些
在此再次提醒您 使用时 保管好Veid和API_Key 你 看我打码服务器名字 然而还是在这里漏了...以前的打码就没意义了
常见错误
身份验证错误 一般代表 api_key 或 veid 的内容填写错误 就是= 后面的 "" 里面的内容 比如VEID="就这里错了" API_KEY="这里"
配置开机启动 和 自动更新
使用systemctl配置开机启动和 自动根据日志更新
修改以下代码中的
ExecStart=/bin/sh -c "/usr/bin/socat -u UDP-RECV:514 STDOUT | /root/checkip.sh"
UDP-RECV:514 把514换成你的路由器那里设置的 远程记录服务器端口 比如 515 就改成 UDP-RECV:515
ExecStart=/bin/sh -c "/usr/bin/socat -u UDP-RECV:514 STDOUT | /root/checkip.sh"
这里面的 /root/checkip.sh
如果你的文件名改了的话 就需要修改… 没改就不用改..
同理 /etc/systemd/system/router-log-watcher.service
这个地方你也应该输入 ls /etc/systemd/system/router-log-watcher.service
查看文件是否存在.如果存在就改名字 如果不存在就不用改了 和上面服务器部分的 ls /root/update_fw.sh 一样
修改好 全部选择 复制粘贴到ssh里面 然后按回车
cat << 'EOF' > /etc/systemd/system/router-log-watcher.service
[Unit]
Description=Router Log Watcher
After=network.target
[Service]
Type=simple
# 管道命令:socat 接收 UDP 514,然后传给脚本
ExecStart=/bin/bash -c "/usr/bin/socat -u UDP-RECV:514 STDOUT | /root/checkip.sh"
Restart=always
RestartSec=5
# 如果脚本需要 root 权限,确保 User=root
User=root
[Install]
WantedBy=multi-user.target
EOF
这个不怎么混乱 直接回车就行
然后 cat /etc/systemd/system/router-log-watcher.service 如果你的文件名字 如果不是这个的话请修改
输入这个 意思是识别新服务
systemctl daemon-reload
在输入这个 意思是 启动并设置开机自启
systemctl enable --now router-log-watcher.service
最后输入
systemctl status router-log-watcher.service
蓝色的框 是 已经运行了 绿色的是enabled 是 设置了 开机自动启动
现在可以重启路由器获取新的ip试试了
输入 cat /var/log/vps_sync.log 这样就是成功了
如果ipv6获取的速度慢 可能出现防火墙没有允许ipv6的情况
遇到极端情况下 可以手动更新
rm -f /etc/config/vps_last_ip && /root/checkip.sh
删除存储的ip然后执行脚本 ip一直相同的话是不会调用api的
另外如果你修改了/root/checkip.sh 那么还要输入 systemctl restart router-log-watcher.service 重启这个服务才生效
如果成功更新ip了 就可以把允许ssh的端口删掉了就真正变成白名单模式了








































