使用搬瓦工api操作nftables實現更新白名單ip
更多語言
更多操作
請注意不要泄漏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
檢查這個目錄是否有一個叫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修改
這兩個如果你知道幹嘛的也可以改 CACHE_FILE="/etc/config/vps_last_ip" 保存上傳的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配置開機啟動和 自動根據日誌更新
修改以下代碼中的
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的端口刪掉了就真正變成白名單模式了








































