<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh">
	<id>https://md5.pw/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=EliToviyah</id>
	<title>md5.pw - 用户贡献 [zh]</title>
	<link rel="self" type="application/atom+xml" href="https://md5.pw/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=EliToviyah"/>
	<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=Special:%E7%94%A8%E6%88%B7%E8%B4%A1%E7%8C%AE/EliToviyah"/>
	<updated>2026-05-06T15:17:20Z</updated>
	<subtitle>用户贡献</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1373</id>
		<title>使用搬瓦工api自动周期性创建快照并telegrambot通知</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1373"/>
		<updated>2026-02-09T08:20:07Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​修复一个分类误修改&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== &#039;&#039;&#039;&#039;&#039;注意：&#039;&#039;&#039; 创建快照的过程中，虚拟机会自动重启!&#039;&#039; ==&lt;br /&gt;
请先阅读关于 快照、备份与恢复 了解 此项功能 &lt;br /&gt;
&lt;br /&gt;
[[快照、备份与恢复]]&lt;br /&gt;
&lt;br /&gt;
== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBot bot token和chat_id ===&lt;br /&gt;
请参考 [[使用搬瓦工api监控网络流量的使用并telegrambot通知]]&lt;br /&gt;
&lt;br /&gt;
再次提醒：&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
=== 获取搬瓦工api_key和veid(服务器id) ===&lt;br /&gt;
请参考: [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
再次提醒:&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
== 开始配置 ==&lt;br /&gt;
&lt;br /&gt;
=== 切换root账户 ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 先检查是否安装了jq ===&lt;br /&gt;
输入 &amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果输出&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-bash: jq: command not found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
说明没有安装&lt;br /&gt;
&lt;br /&gt;
==== 安装jq ====&lt;br /&gt;
用于解析api返回的复杂内容&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. Debian / Ubuntu&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt update sudo apt install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. CentOS / RHEL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果是 CentOS 7，可能需要先安装 epel-release &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install epel-release -y&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果是 RHEL 8/9 或 AlmaLinux&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo dnf install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 验证是否安装成功 ===&lt;br /&gt;
安装完成后，你可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Jqcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 脚本 ===&lt;br /&gt;
需要修改的地方&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot; #快照描述的前缀 作为判断快照是否创建、删除、锁定的重要依据&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot; # 日志文件路径&lt;br /&gt;
----检查 /root/autosnapshot.sh 是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfilehave.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面 蓝色框框就是文件不存在 就可以不修改文件名&lt;br /&gt;
&lt;br /&gt;
下面的红色框框 就是文件存在 需要修改文件名字 /root/这里是文件名字.sh 比如修改成/root/autosnapshot11.sh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
把下面内容修改后 就可以复制粘贴到ssh里面 然后按回车 就可以创建脚本文件了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/autosnapshot.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区域 =================&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot;&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot;&lt;br /&gt;
# ===========================================&lt;br /&gt;
&lt;br /&gt;
API_URL=&amp;quot;https://api.64clouds.com/v1&amp;quot;&lt;br /&gt;
DATE_TODAY=$(date +&amp;quot;%Y_%m_%d&amp;quot;)&lt;br /&gt;
CURRENT_SNAPSHOT_NAME=&amp;quot;${TAG_PREFIX}_${DATE_TODAY}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
log() {&lt;br /&gt;
    # 检查日志文件是否存在&lt;br /&gt;
    if [ -f &amp;quot;$LOG_FILE&amp;quot; ]; then&lt;br /&gt;
        # 获取当前行数&lt;br /&gt;
        local current_lines=$(grep -c &amp;quot;&amp;quot; &amp;quot;$LOG_FILE&amp;quot;)&lt;br /&gt;
        if [ &amp;quot;$current_lines&amp;quot; -gt &amp;quot;$MAX_LOG_LINES&amp;quot; ]; then&lt;br /&gt;
            # 超过行数，保留最后 10 行并标记清空，或者直接清空&lt;br /&gt;
            echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] 日志达到上限，执行自动清理。&amp;quot; &amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
    # 正常写入日志&lt;br /&gt;
    echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] $1&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
send_tg() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    # 发送 TG 消息并根据 curl 状态码记录日志&lt;br /&gt;
    curl -s -X POST &amp;quot;https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=${TG_CHAT_ID}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;text=${msg}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;disable_notification=true&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
    &lt;br /&gt;
    if [ $? -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;Telegram 通知发送成功: $msg&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;Telegram 通知发送失败，请检查网络或 Token。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
call_api() {&lt;br /&gt;
    local endpoint=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local extra_params=&amp;quot;$2&amp;quot;&lt;br /&gt;
    # --max-time 30: 设置最大超时时间为30秒&lt;br /&gt;
    # --retry 3: 如果失败自动重试3次&lt;br /&gt;
    curl -s --max-time 30 --retry 3 -X POST &amp;quot;${API_URL}/${endpoint}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;veid=${VEID}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;api_key=${API_KEY}&amp;quot; \&lt;br /&gt;
         ${extra_params}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 【新增函数】提取快照数据的公共逻辑&lt;br /&gt;
get_snapshot_map() {&lt;br /&gt;
    echo &amp;quot;$SNAPSHOT_LIST&amp;quot; | jq -r --arg tag &amp;quot;$TAG_PREFIX&amp;quot; &#039;&lt;br /&gt;
        .snapshots[] | &lt;br /&gt;
        select(try (.description | @base64d) catch &amp;quot;&amp;quot; | contains($tag)) | &lt;br /&gt;
        &amp;quot;\(.sticky)|\((.description | @base64d))|\(.fileName)&amp;quot;&lt;br /&gt;
    &#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 预检&lt;br /&gt;
if ! command -v jq &amp;amp;&amp;gt; /dev/null; then&lt;br /&gt;
    log &amp;quot;错误: 未安装 jq，请安装jq&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;--- 脚本启动 ---&amp;quot;&lt;br /&gt;
SNAPSHOT_LIST=$(call_api &amp;quot;snapshot/list&amp;quot; &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 1: 开机补刀/清理逻辑 (默认) =================&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; != &amp;quot;-c&amp;quot; ]]; then&lt;br /&gt;
    log &amp;quot;进入开机自检模式...&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 调用函数获取数据&lt;br /&gt;
    MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
    # 使用 grep -c 直接统计行数，不再使用 wc -l&lt;br /&gt;
    count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
    count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    log &amp;quot;开机自检状态: 符合前缀的置顶数 $count_a, 符合前缀的未置顶数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;状态 [0,0]：未发现任何相关快照，无需操作。&amp;quot;&lt;br /&gt;
        exit 0&lt;br /&gt;
&lt;br /&gt;
    elif [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
        NEW_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
        NEW_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
        &lt;br /&gt;
        if [ &amp;quot;$count_a&amp;quot; -eq 1 ]; then&lt;br /&gt;
            OLD_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
            OLD_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
            &lt;br /&gt;
            log &amp;quot;发现新快照，正在删除旧的已置顶快照: $OLD_DESC&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            DEL_RES=$(call_api &amp;quot;snapshot/delete&amp;quot; &amp;quot;--data-urlencode snapshot=${OLD_FILENAME}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            # 【替换 1】使用 jq 判断删除结果&lt;br /&gt;
            API_ERR_DEL=$(echo &amp;quot;$DEL_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
            if [ &amp;quot;$API_ERR_DEL&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
                log &amp;quot;旧快照删除成功。&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;已成功删除旧快照: ${OLD_DESC}。&amp;quot;&lt;br /&gt;
            else&lt;br /&gt;
                log &amp;quot;旧快照删除失败: $DEL_RES&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;删除旧快照发生错误。&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
        # 锁定新快照(b)&lt;br /&gt;
        log &amp;quot;正在锁定新快照: $NEW_DESC ...&amp;quot;&lt;br /&gt;
        LOCK_RES=$(call_api &amp;quot;snapshot/toggleSticky&amp;quot; &amp;quot;--data-urlencode snapshot=${NEW_FILENAME} -d sticky=1&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 使用 jq 解析 error 字段的值是否等于 0&lt;br /&gt;
        API_ERROR=$(echo &amp;quot;$LOCK_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$API_ERROR&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;新快照锁定成功。&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;✅ 快照更替完成！${DEL_MSG}新快照 ${NEW_DESC} 已成功锁定。&amp;quot;&lt;br /&gt;
            exit 0&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;新快照锁定失败，API返回: $LOCK_RES&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;❌ ${DEL_MSG}但新快照 ${NEW_DESC} 锁定失败，解析出的错误码为: ${API_ERROR}&amp;quot;&lt;br /&gt;
            exit 1&lt;br /&gt;
        fi&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;自检状态 [$count_a,$count_b]：不满足自动更替条件。&amp;quot;&lt;br /&gt;
        send_tg &amp;quot;ℹ️ 搬瓦工自检报告：当前状态为 [$count_a,$count_b]，未达到快照更迭条件,脚本未执行操作。请手动查看..&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 2: 创建逻辑 (-c 参数) =================&lt;br /&gt;
log &amp;quot;进入快照创建模式 (-c)...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 调用函数获取数据&lt;br /&gt;
MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;状态检查: 符合前缀的已置顶快照数 $count_a, 符合前缀的未置顶的快照数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
DO_CREATE=false&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,0]：准备发起首次创建。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [1,0]：准备创建新的备份。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,1]：存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;⚠️ 存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 2 ]; then&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：置顶名额满。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;🚫 快照置顶名额已满 (2/2)，请手动删除旧备份。&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：未知状态，停止操作。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;❌ 未创建快照：不清楚的快照数量 (a=$count_a, b=$count_b)。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$DO_CREATE&amp;quot; = true ]; then&lt;br /&gt;
    # 再次检查日期防止重复&lt;br /&gt;
    if echo &amp;quot;$MAP_DATA&amp;quot; | grep -q &amp;quot;$DATE_TODAY&amp;quot;; then&lt;br /&gt;
        log &amp;quot;今日快照已存在，跳过。今日: $DATE_TODAY&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;发起创建快照请求: $CURRENT_SNAPSHOT_NAME&amp;quot;&lt;br /&gt;
        CREATE_RESULT=$(call_api &amp;quot;snapshot/create&amp;quot; &amp;quot;--data-urlencode description=${CURRENT_SNAPSHOT_NAME}&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 【替换 2】使用 jq 判断创建请求结果&lt;br /&gt;
        API_ERR_CREATE=$(echo &amp;quot;$CREATE_RESULT&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
        if [ &amp;quot;$API_ERR_CREATE&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;创建快照请求成功发送。&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;创建快照请求失败: $CREATE_RESULT&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;⚠️ 搬瓦工 API 创建快照请求返回异常。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;授予运行权限&lt;br /&gt;
&lt;br /&gt;
输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod +x /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
查看是否成功 输入 &amp;lt;code&amp;gt;ls -l /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Lsautosnapshot.png|border]]&lt;br /&gt;
&lt;br /&gt;
像蓝色的有x就是成功了 像上面后红色的- 没有x 就是失败了...&lt;br /&gt;
&lt;br /&gt;
=== 配置开机置顶快照 和定期创建快照 ===&lt;br /&gt;
&#039;&#039;&#039;0 3 * * 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
这是一个cron的表达式 用来设定 运行时间的&lt;br /&gt;
&lt;br /&gt;
0 代表 第 0 分钟的时候&lt;br /&gt;
&lt;br /&gt;
3 代表 第3小时的时候 就是凌晨3点&lt;br /&gt;
&lt;br /&gt;
第一个 * 代表 哪一天 *代表所有天&lt;br /&gt;
&lt;br /&gt;
第二个 * 代表 哪一月 *代表所有月&lt;br /&gt;
&lt;br /&gt;
1 代表 星期一&lt;br /&gt;
&lt;br /&gt;
合起来就是&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;每周一的凌晨3:00运行 创建快照的指令&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
输入 timedatectl 查看系统使用的时区&lt;br /&gt;
&lt;br /&gt;
第一个 Localtime 的时区就是 它所使用的时区 还请根据自己的时区自行修改时间 或修改系统使用的时区&lt;br /&gt;
&lt;br /&gt;
可以按照自己的时间自行修改&lt;br /&gt;
&lt;br /&gt;
0 3 * * 1 root /root/autosnapshot.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
开机启动检查是否有未锁定的快照(认为是上一次的快照) &lt;br /&gt;
&lt;br /&gt;
如果文件名 改了的话 这里也要修改 其他的地方不需要 &lt;br /&gt;
&lt;br /&gt;
@reboot root /root/autosnapshot.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
检查定时文件是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkbwgsnap.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面红色框框 就是 文件不存在 不需要修改名字&lt;br /&gt;
&lt;br /&gt;
下面蓝色框框 就是 文件存在 需要文件名字../etc/cron.d/你需要修改的名字  比如/etc/cron.d/bwg_snap1&lt;br /&gt;
&lt;br /&gt;
修改后 复制粘贴到ssh里面 回车就行了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.d/bwg_snap&lt;br /&gt;
# 每周一凌晨 3 点创建快照&lt;br /&gt;
0 3 * * 1 root /root/autosnapshot.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
# 开机自启&lt;br /&gt;
@reboot root /root/autosnapshot.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;# 修改文件权限 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod 644 /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Finalcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
如果你想测试 可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/root/autosnapshot.sh -c&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
根据时间 一天只能创建一个快照....脚本设定 非api设置的...&lt;br /&gt;
&lt;br /&gt;
创建快照 系统会重启 请注意为保存的数据会丢失...&lt;br /&gt;
&lt;br /&gt;
开机后应该会自动置顶这个快照 或 手动输入 &amp;lt;code&amp;gt;/root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
检查是否存在 有前缀名但未置顶的快照 或 有前缀 或 有一个 有前缀的并且置顶的快照 和 一个 有前缀未置顶的快照&lt;br /&gt;
&lt;br /&gt;
都会执行 删除置顶的快照(如果有)然后置顶那个 未置顶的快照&lt;br /&gt;
&lt;br /&gt;
==== 查看日志 &amp;lt;code&amp;gt;cat /var/log/bwg_snapshot.log&amp;lt;/code&amp;gt; ====&lt;br /&gt;
[[File:Finalimg.png|border]]&lt;br /&gt;
[[Category:500 常见应用指南 — Application Guides]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1372</id>
		<title>使用搬瓦工api自动周期性创建快照并telegrambot通知</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1372"/>
		<updated>2026-02-09T08:15:48Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​修正演示使用的路径..增加一条提示&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== &#039;&#039;&#039;&#039;&#039;注意：&#039;&#039;&#039; 创建快照的过程中，虚拟机会自动重启!&#039;&#039; ==&lt;br /&gt;
请先阅读关于 快照、备份与恢复 了解 此项功能 &lt;br /&gt;
&lt;br /&gt;
[[快照、备份与恢复]]&lt;br /&gt;
&lt;br /&gt;
== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBot bot token和chat_id ===&lt;br /&gt;
请参考 [[使用搬瓦工api监控网络流量的使用并telegrambot通知]]&lt;br /&gt;
&lt;br /&gt;
再次提醒：&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
=== 获取搬瓦工api_key和veid(服务器id) ===&lt;br /&gt;
请参考: [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
再次提醒:&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
== 开始配置 ==&lt;br /&gt;
&lt;br /&gt;
=== 切换root账户 ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 先检查是否安装了jq ===&lt;br /&gt;
输入 &amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果输出&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-bash: jq: command not found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
说明没有安装&lt;br /&gt;
&lt;br /&gt;
==== 安装jq ====&lt;br /&gt;
用于解析api返回的复杂内容&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. Debian / Ubuntu&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt update sudo apt install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. CentOS / RHEL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果是 CentOS 7，可能需要先安装 epel-release &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install epel-release -y&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果是 RHEL 8/9 或 AlmaLinux&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo dnf install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 验证是否安装成功 ===&lt;br /&gt;
安装完成后，你可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Jqcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 脚本 ===&lt;br /&gt;
需要修改的地方&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot; #快照描述的前缀 作为判断快照是否创建、删除、锁定的重要依据&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot; # 日志文件路径&lt;br /&gt;
----检查 /root/autosnapshot.sh 是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfilehave.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面 蓝色框框就是文件不存在 就可以不修改文件名&lt;br /&gt;
&lt;br /&gt;
下面的红色框框 就是文件存在 需要修改文件名字 /root/这里是文件名字.sh 比如修改成/root/autosnapshot11.sh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
把下面内容修改后 就可以复制粘贴到ssh里面 然后按回车 就可以创建脚本文件了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/autosnapshot.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区域 =================&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot;&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot;&lt;br /&gt;
# ===========================================&lt;br /&gt;
&lt;br /&gt;
API_URL=&amp;quot;https://api.64clouds.com/v1&amp;quot;&lt;br /&gt;
DATE_TODAY=$(date +&amp;quot;%Y_%m_%d&amp;quot;)&lt;br /&gt;
CURRENT_SNAPSHOT_NAME=&amp;quot;${TAG_PREFIX}_${DATE_TODAY}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
log() {&lt;br /&gt;
    # 检查日志文件是否存在&lt;br /&gt;
    if [ -f &amp;quot;$LOG_FILE&amp;quot; ]; then&lt;br /&gt;
        # 获取当前行数&lt;br /&gt;
        local current_lines=$(grep -c &amp;quot;&amp;quot; &amp;quot;$LOG_FILE&amp;quot;)&lt;br /&gt;
        if [ &amp;quot;$current_lines&amp;quot; -gt &amp;quot;$MAX_LOG_LINES&amp;quot; ]; then&lt;br /&gt;
            # 超过行数，保留最后 10 行并标记清空，或者直接清空&lt;br /&gt;
            echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] 日志达到上限，执行自动清理。&amp;quot; &amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
    # 正常写入日志&lt;br /&gt;
    echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] $1&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
send_tg() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    # 发送 TG 消息并根据 curl 状态码记录日志&lt;br /&gt;
    curl -s -X POST &amp;quot;https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=${TG_CHAT_ID}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;text=${msg}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;disable_notification=true&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
    &lt;br /&gt;
    if [ $? -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;Telegram 通知发送成功: $msg&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;Telegram 通知发送失败，请检查网络或 Token。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
call_api() {&lt;br /&gt;
    local endpoint=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local extra_params=&amp;quot;$2&amp;quot;&lt;br /&gt;
    # --max-time 30: 设置最大超时时间为30秒&lt;br /&gt;
    # --retry 3: 如果失败自动重试3次&lt;br /&gt;
    curl -s --max-time 30 --retry 3 -X POST &amp;quot;${API_URL}/${endpoint}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;veid=${VEID}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;api_key=${API_KEY}&amp;quot; \&lt;br /&gt;
         ${extra_params}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 【新增函数】提取快照数据的公共逻辑&lt;br /&gt;
get_snapshot_map() {&lt;br /&gt;
    echo &amp;quot;$SNAPSHOT_LIST&amp;quot; | jq -r --arg tag &amp;quot;$TAG_PREFIX&amp;quot; &#039;&lt;br /&gt;
        .snapshots[] | &lt;br /&gt;
        select(try (.description | @base64d) catch &amp;quot;&amp;quot; | contains($tag)) | &lt;br /&gt;
        &amp;quot;\(.sticky)|\((.description | @base64d))|\(.fileName)&amp;quot;&lt;br /&gt;
    &#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 预检&lt;br /&gt;
if ! command -v jq &amp;amp;&amp;gt; /dev/null; then&lt;br /&gt;
    log &amp;quot;错误: 未安装 jq，请安装jq&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;--- 脚本启动 ---&amp;quot;&lt;br /&gt;
SNAPSHOT_LIST=$(call_api &amp;quot;snapshot/list&amp;quot; &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 1: 开机补刀/清理逻辑 (默认) =================&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; != &amp;quot;-c&amp;quot; ]]; then&lt;br /&gt;
    log &amp;quot;进入开机自检模式...&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 调用函数获取数据&lt;br /&gt;
    MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
    # 使用 grep -c 直接统计行数，不再使用 wc -l&lt;br /&gt;
    count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
    count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    log &amp;quot;开机自检状态: 符合前缀的置顶数 $count_a, 符合前缀的未置顶数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;状态 [0,0]：未发现任何相关快照，无需操作。&amp;quot;&lt;br /&gt;
        exit 0&lt;br /&gt;
&lt;br /&gt;
    elif [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
        NEW_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
        NEW_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
        &lt;br /&gt;
        if [ &amp;quot;$count_a&amp;quot; -eq 1 ]; then&lt;br /&gt;
            OLD_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
            OLD_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
            &lt;br /&gt;
            log &amp;quot;发现新快照，正在删除旧的已置顶快照: $OLD_DESC&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            DEL_RES=$(call_api &amp;quot;snapshot/delete&amp;quot; &amp;quot;--data-urlencode snapshot=${OLD_FILENAME}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            # 【替换 1】使用 jq 判断删除结果&lt;br /&gt;
            API_ERR_DEL=$(echo &amp;quot;$DEL_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
            if [ &amp;quot;$API_ERR_DEL&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
                log &amp;quot;旧快照删除成功。&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;已成功删除旧快照: ${OLD_DESC}。&amp;quot;&lt;br /&gt;
            else&lt;br /&gt;
                log &amp;quot;旧快照删除失败: $DEL_RES&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;删除旧快照发生错误。&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
        # 锁定新快照(b)&lt;br /&gt;
        log &amp;quot;正在锁定新快照: $NEW_DESC ...&amp;quot;&lt;br /&gt;
        LOCK_RES=$(call_api &amp;quot;snapshot/toggleSticky&amp;quot; &amp;quot;--data-urlencode snapshot=${NEW_FILENAME} -d sticky=1&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 使用 jq 解析 error 字段的值是否等于 0&lt;br /&gt;
        API_ERROR=$(echo &amp;quot;$LOCK_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$API_ERROR&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;新快照锁定成功。&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;✅ 快照更替完成！${DEL_MSG}新快照 ${NEW_DESC} 已成功锁定。&amp;quot;&lt;br /&gt;
            exit 0&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;新快照锁定失败，API返回: $LOCK_RES&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;❌ ${DEL_MSG}但新快照 ${NEW_DESC} 锁定失败，解析出的错误码为: ${API_ERROR}&amp;quot;&lt;br /&gt;
            exit 1&lt;br /&gt;
        fi&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;自检状态 [$count_a,$count_b]：不满足自动更替条件。&amp;quot;&lt;br /&gt;
        send_tg &amp;quot;ℹ️ 搬瓦工自检报告：当前状态为 [$count_a,$count_b]，未达到快照更迭条件,脚本未执行操作。请手动查看..&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 2: 创建逻辑 (-c 参数) =================&lt;br /&gt;
log &amp;quot;进入快照创建模式 (-c)...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 调用函数获取数据&lt;br /&gt;
MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;状态检查: 符合前缀的已置顶快照数 $count_a, 符合前缀的未置顶的快照数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
DO_CREATE=false&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,0]：准备发起首次创建。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [1,0]：准备创建新的备份。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,1]：存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;⚠️ 存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 2 ]; then&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：置顶名额满。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;🚫 快照置顶名额已满 (2/2)，请手动删除旧备份。&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：未知状态，停止操作。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;❌ 未创建快照：不清楚的快照数量 (a=$count_a, b=$count_b)。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$DO_CREATE&amp;quot; = true ]; then&lt;br /&gt;
    # 再次检查日期防止重复&lt;br /&gt;
    if echo &amp;quot;$MAP_DATA&amp;quot; | grep -q &amp;quot;$DATE_TODAY&amp;quot;; then&lt;br /&gt;
        log &amp;quot;今日快照已存在，跳过。今日: $DATE_TODAY&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;发起创建快照请求: $CURRENT_SNAPSHOT_NAME&amp;quot;&lt;br /&gt;
        CREATE_RESULT=$(call_api &amp;quot;snapshot/create&amp;quot; &amp;quot;--data-urlencode description=${CURRENT_SNAPSHOT_NAME}&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 【替换 2】使用 jq 判断创建请求结果&lt;br /&gt;
        API_ERR_CREATE=$(echo &amp;quot;$CREATE_RESULT&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
        if [ &amp;quot;$API_ERR_CREATE&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;创建快照请求成功发送。&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;创建快照请求失败: $CREATE_RESULT&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;⚠️ 搬瓦工 API 创建快照请求返回异常。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;授予运行权限&lt;br /&gt;
&lt;br /&gt;
输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod +x /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
查看是否成功 输入 &amp;lt;code&amp;gt;ls -l /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Lsautosnapshot.png|border]]&lt;br /&gt;
&lt;br /&gt;
像蓝色的有x就是成功了 像上面后红色的- 没有x 就是失败了...&lt;br /&gt;
&lt;br /&gt;
=== 配置开机置顶快照 和定期创建快照 ===&lt;br /&gt;
&#039;&#039;&#039;0 3 * * 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
这是一个cron的表达式 用来设定 运行时间的&lt;br /&gt;
&lt;br /&gt;
0 代表 第 0 分钟的时候&lt;br /&gt;
&lt;br /&gt;
3 代表 第3小时的时候 就是凌晨3点&lt;br /&gt;
&lt;br /&gt;
第一个 * 代表 哪一天 *代表所有天&lt;br /&gt;
&lt;br /&gt;
第二个 * 代表 哪一月 *代表所有月&lt;br /&gt;
&lt;br /&gt;
1 代表 星期一&lt;br /&gt;
&lt;br /&gt;
合起来就是&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;每周一的凌晨3:00运行 创建快照的指令&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
输入 timedatectl 查看系统使用的时区&lt;br /&gt;
&lt;br /&gt;
第一个 Localtime 的时区就是 它所使用的时区 还请根据自己的时区自行修改时间 或修改系统使用的时区&lt;br /&gt;
&lt;br /&gt;
可以按照自己的时间自行修改&lt;br /&gt;
&lt;br /&gt;
0 3 * * 1 root /root/autosnapshot.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
开机启动检查是否有未锁定的快照(认为是上一次的快照) &lt;br /&gt;
&lt;br /&gt;
如果文件名 改了的话 这里也要修改 其他的地方不需要 &lt;br /&gt;
&lt;br /&gt;
@reboot root /root/autosnapshot.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
检查定时文件是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkbwgsnap.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面红色框框 就是 文件不存在 不需要修改名字&lt;br /&gt;
&lt;br /&gt;
下面蓝色框框 就是 文件存在 需要文件名字../etc/cron.d/你需要修改的名字  比如/etc/cron.d/bwg_snap1&lt;br /&gt;
&lt;br /&gt;
修改后 复制粘贴到ssh里面 回车就行了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.d/bwg_snap&lt;br /&gt;
# 每周一凌晨 3 点创建快照&lt;br /&gt;
0 3 * * 1 root /root/autosnapshot.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
# 开机自启&lt;br /&gt;
@reboot root /root/autosnapshot.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;# 修改文件权限 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod 644 /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Finalcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
如果你想测试 可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/root/autosnapshot.sh -c&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
根据时间 一天只能创建一个快照....脚本设定 非api设置的...&lt;br /&gt;
&lt;br /&gt;
创建快照 系统会重启 请注意为保存的数据会丢失...&lt;br /&gt;
&lt;br /&gt;
开机后应该会自动置顶这个快照 或 手动输入 &amp;lt;code&amp;gt;/root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
检查是否存在 有前缀名但未置顶的快照 或 有前缀 或 有一个 有前缀的并且置顶的快照 和 一个 有前缀未置顶的快照&lt;br /&gt;
&lt;br /&gt;
都会执行 删除置顶的快照(如果有)然后置顶那个 未置顶的快照&lt;br /&gt;
&lt;br /&gt;
==== 查看日志 &amp;lt;code&amp;gt;cat /var/log/bwg_snapshot.log&amp;lt;/code&amp;gt; ====&lt;br /&gt;
[[File:Finalimg.png|border]]&lt;br /&gt;
[[index.php?title=Category:500 常见应用指南 — Application Guides]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1335</id>
		<title>使用搬瓦工api自动周期性创建快照并telegrambot通知</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1335"/>
		<updated>2026-02-04T05:31:39Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​修复一个错误的演示路径&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== &#039;&#039;&#039;&#039;&#039;注意：&#039;&#039;&#039; 创建快照的过程中，虚拟机会自动重启!&#039;&#039; ==&lt;br /&gt;
请先阅读关于 快照、备份与恢复 了解 此项功能 &lt;br /&gt;
&lt;br /&gt;
[[快照、备份与恢复]]&lt;br /&gt;
&lt;br /&gt;
== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBot bot token和chat_id ===&lt;br /&gt;
请参考 [[使用搬瓦工api监控网络流量的使用并telegrambot通知]]&lt;br /&gt;
&lt;br /&gt;
再次提醒：&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
=== 获取搬瓦工api_key和veid(服务器id) ===&lt;br /&gt;
请参考: [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
再次提醒:&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
== 开始配置 ==&lt;br /&gt;
&lt;br /&gt;
=== 切换root账户 ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 先检查是否安装了jq ===&lt;br /&gt;
输入 &amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果输出&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-bash: jq: command not found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
说明没有安装&lt;br /&gt;
&lt;br /&gt;
==== 安装jq ====&lt;br /&gt;
用于解析api返回的复杂内容&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. Debian / Ubuntu&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt update sudo apt install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. CentOS / RHEL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果是 CentOS 7，可能需要先安装 epel-release &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install epel-release -y&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果是 RHEL 8/9 或 AlmaLinux&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo dnf install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 验证是否安装成功 ===&lt;br /&gt;
安装完成后，你可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Jqcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 脚本 ===&lt;br /&gt;
需要修改的地方&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot; #快照描述的前缀 作为判断快照是否创建、删除、锁定的重要依据&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot; # 日志文件路径&lt;br /&gt;
----检查 /root/autosnapshot.sh 是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfilehave.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面 蓝色框框就是文件不存在 就可以不修改文件名&lt;br /&gt;
&lt;br /&gt;
下面的红色框框 就是文件存在 需要修改文件名字 /root/这里是文件名字.sh 比如修改成/root/autosnapshot11.sh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
把下面内容修改后 就可以复制粘贴到ssh里面 然后按回车 就可以创建脚本文件了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/autosnapshot.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区域 =================&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot;&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot;&lt;br /&gt;
# ===========================================&lt;br /&gt;
&lt;br /&gt;
API_URL=&amp;quot;https://api.64clouds.com/v1&amp;quot;&lt;br /&gt;
DATE_TODAY=$(date +&amp;quot;%Y_%m_%d&amp;quot;)&lt;br /&gt;
CURRENT_SNAPSHOT_NAME=&amp;quot;${TAG_PREFIX}_${DATE_TODAY}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
log() {&lt;br /&gt;
    # 检查日志文件是否存在&lt;br /&gt;
    if [ -f &amp;quot;$LOG_FILE&amp;quot; ]; then&lt;br /&gt;
        # 获取当前行数&lt;br /&gt;
        local current_lines=$(grep -c &amp;quot;&amp;quot; &amp;quot;$LOG_FILE&amp;quot;)&lt;br /&gt;
        if [ &amp;quot;$current_lines&amp;quot; -gt &amp;quot;$MAX_LOG_LINES&amp;quot; ]; then&lt;br /&gt;
            # 超过行数，保留最后 10 行并标记清空，或者直接清空&lt;br /&gt;
            echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] 日志达到上限，执行自动清理。&amp;quot; &amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
    # 正常写入日志&lt;br /&gt;
    echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] $1&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
send_tg() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    # 发送 TG 消息并根据 curl 状态码记录日志&lt;br /&gt;
    curl -s -X POST &amp;quot;https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=${TG_CHAT_ID}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;text=${msg}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;disable_notification=true&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
    &lt;br /&gt;
    if [ $? -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;Telegram 通知发送成功: $msg&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;Telegram 通知发送失败，请检查网络或 Token。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
call_api() {&lt;br /&gt;
    local endpoint=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local extra_params=&amp;quot;$2&amp;quot;&lt;br /&gt;
    # --max-time 30: 设置最大超时时间为30秒&lt;br /&gt;
    # --retry 3: 如果失败自动重试3次&lt;br /&gt;
    curl -s --max-time 30 --retry 3 -X POST &amp;quot;${API_URL}/${endpoint}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;veid=${VEID}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;api_key=${API_KEY}&amp;quot; \&lt;br /&gt;
         ${extra_params}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 【新增函数】提取快照数据的公共逻辑&lt;br /&gt;
get_snapshot_map() {&lt;br /&gt;
    echo &amp;quot;$SNAPSHOT_LIST&amp;quot; | jq -r --arg tag &amp;quot;$TAG_PREFIX&amp;quot; &#039;&lt;br /&gt;
        .snapshots[] | &lt;br /&gt;
        select(try (.description | @base64d) catch &amp;quot;&amp;quot; | contains($tag)) | &lt;br /&gt;
        &amp;quot;\(.sticky)|\((.description | @base64d))|\(.fileName)&amp;quot;&lt;br /&gt;
    &#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 预检&lt;br /&gt;
if ! command -v jq &amp;amp;&amp;gt; /dev/null; then&lt;br /&gt;
    log &amp;quot;错误: 未安装 jq，请安装jq&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;--- 脚本启动 ---&amp;quot;&lt;br /&gt;
SNAPSHOT_LIST=$(call_api &amp;quot;snapshot/list&amp;quot; &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 1: 开机补刀/清理逻辑 (默认) =================&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; != &amp;quot;-c&amp;quot; ]]; then&lt;br /&gt;
    log &amp;quot;进入开机自检模式...&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 调用函数获取数据&lt;br /&gt;
    MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
    # 使用 grep -c 直接统计行数，不再使用 wc -l&lt;br /&gt;
    count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
    count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    log &amp;quot;开机自检状态: 符合前缀的置顶数 $count_a, 符合前缀的未置顶数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;状态 [0,0]：未发现任何相关快照，无需操作。&amp;quot;&lt;br /&gt;
        exit 0&lt;br /&gt;
&lt;br /&gt;
    elif [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
        NEW_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
        NEW_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
        &lt;br /&gt;
        if [ &amp;quot;$count_a&amp;quot; -eq 1 ]; then&lt;br /&gt;
            OLD_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
            OLD_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
            &lt;br /&gt;
            log &amp;quot;发现新快照，正在删除旧的已置顶快照: $OLD_DESC&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            DEL_RES=$(call_api &amp;quot;snapshot/delete&amp;quot; &amp;quot;--data-urlencode snapshot=${OLD_FILENAME}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            # 【替换 1】使用 jq 判断删除结果&lt;br /&gt;
            API_ERR_DEL=$(echo &amp;quot;$DEL_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
            if [ &amp;quot;$API_ERR_DEL&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
                log &amp;quot;旧快照删除成功。&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;已成功删除旧快照: ${OLD_DESC}。&amp;quot;&lt;br /&gt;
            else&lt;br /&gt;
                log &amp;quot;旧快照删除失败: $DEL_RES&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;删除旧快照发生错误。&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
        # 锁定新快照(b)&lt;br /&gt;
        log &amp;quot;正在锁定新快照: $NEW_DESC ...&amp;quot;&lt;br /&gt;
        LOCK_RES=$(call_api &amp;quot;snapshot/toggleSticky&amp;quot; &amp;quot;--data-urlencode snapshot=${NEW_FILENAME} -d sticky=1&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 使用 jq 解析 error 字段的值是否等于 0&lt;br /&gt;
        API_ERROR=$(echo &amp;quot;$LOCK_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$API_ERROR&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;新快照锁定成功。&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;✅ 快照更替完成！${DEL_MSG}新快照 ${NEW_DESC} 已成功锁定。&amp;quot;&lt;br /&gt;
            exit 0&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;新快照锁定失败，API返回: $LOCK_RES&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;❌ ${DEL_MSG}但新快照 ${NEW_DESC} 锁定失败，解析出的错误码为: ${API_ERROR}&amp;quot;&lt;br /&gt;
            exit 1&lt;br /&gt;
        fi&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;自检状态 [$count_a,$count_b]：不满足自动更替条件。&amp;quot;&lt;br /&gt;
        send_tg &amp;quot;ℹ️ 搬瓦工自检报告：当前状态为 [$count_a,$count_b]，未达到快照更迭条件,脚本未执行操作。请手动查看..&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 2: 创建逻辑 (-c 参数) =================&lt;br /&gt;
log &amp;quot;进入快照创建模式 (-c)...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 调用函数获取数据&lt;br /&gt;
MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;状态检查: 符合前缀的已置顶快照数 $count_a, 符合前缀的未置顶的快照数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
DO_CREATE=false&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,0]：准备发起首次创建。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [1,0]：准备创建新的备份。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,1]：存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;⚠️ 存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 2 ]; then&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：置顶名额满。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;🚫 快照置顶名额已满 (2/2)，请手动删除旧备份。&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：未知状态，停止操作。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;❌ 未创建快照：不清楚的快照数量 (a=$count_a, b=$count_b)。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$DO_CREATE&amp;quot; = true ]; then&lt;br /&gt;
    # 再次检查日期防止重复&lt;br /&gt;
    if echo &amp;quot;$MAP_DATA&amp;quot; | grep -q &amp;quot;$DATE_TODAY&amp;quot;; then&lt;br /&gt;
        log &amp;quot;今日快照已存在，跳过。今日: $DATE_TODAY&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;发起创建快照请求: $CURRENT_SNAPSHOT_NAME&amp;quot;&lt;br /&gt;
        CREATE_RESULT=$(call_api &amp;quot;snapshot/create&amp;quot; &amp;quot;--data-urlencode description=${CURRENT_SNAPSHOT_NAME}&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 【替换 2】使用 jq 判断创建请求结果&lt;br /&gt;
        API_ERR_CREATE=$(echo &amp;quot;$CREATE_RESULT&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
        if [ &amp;quot;$API_ERR_CREATE&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;创建快照请求成功发送。&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;创建快照请求失败: $CREATE_RESULT&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;⚠️ 搬瓦工 API 创建快照请求返回异常。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;授予运行权限&lt;br /&gt;
&lt;br /&gt;
输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod +x /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
查看是否成功 输入 &amp;lt;code&amp;gt;ls -l /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Lsautosnapshot.png|border]]&lt;br /&gt;
&lt;br /&gt;
像蓝色的有x就是成功了 像上面后红色的- 没有x 就是失败了...&lt;br /&gt;
&lt;br /&gt;
=== 配置开机置顶快照 和定期创建快照 ===&lt;br /&gt;
&#039;&#039;&#039;0 3 * * 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
这是一个cron的表达式 用来设定 运行时间的&lt;br /&gt;
&lt;br /&gt;
0 代表 第 0 分钟的时候&lt;br /&gt;
&lt;br /&gt;
3 代表 第3小时的时候 就是凌晨3点&lt;br /&gt;
&lt;br /&gt;
第一个 * 代表 哪一天 *代表所有天&lt;br /&gt;
&lt;br /&gt;
第二个 * 代表 哪一月 *代表所有月&lt;br /&gt;
&lt;br /&gt;
1 代表 星期一&lt;br /&gt;
&lt;br /&gt;
合起来就是&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;每周一的凌晨3:00运行 创建快照的指令&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
可以按照自己的时间自行修改&lt;br /&gt;
&lt;br /&gt;
0 3 * * 1 root /usr/local/bin/bwg_snap.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
开机启动检查是否有未锁定的快照(认为是上一次的快照) &lt;br /&gt;
&lt;br /&gt;
如果文件名 改了的话 这里也要修改 其他的地方不需要 &lt;br /&gt;
&lt;br /&gt;
@reboot root /usr/local/bin/bwg_snap.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
检查定时文件是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkbwgsnap.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面红色框框 就是 文件不存在 不需要修改名字&lt;br /&gt;
&lt;br /&gt;
下面蓝色框框 就是 文件存在 需要文件名字../etc/cron.d/你需要修改的名字  比如/etc/cron.d/bwg_snap1&lt;br /&gt;
&lt;br /&gt;
修改后 复制粘贴到ssh里面 回车就行了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.d/bwg_snap&lt;br /&gt;
# 每周一凌晨 3 点创建快照&lt;br /&gt;
0 3 * * 1 root /root/autosnapshot.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
# 开机自启&lt;br /&gt;
@reboot root /root/autosnapshot.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;# 修改文件权限 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod 644 /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Finalcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
如果你想测试 可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/root/autosnapshot.sh -c&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
根据时间 一天只能创建一个快照....脚本设定 非api设置的...&lt;br /&gt;
&lt;br /&gt;
创建快照 系统会重启 请注意为保存的数据会丢失...&lt;br /&gt;
&lt;br /&gt;
开机后应该会自动置顶这个快照 或 手动输入 &amp;lt;code&amp;gt;/root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
检查是否存在 有前缀名但未置顶的快照 或 有前缀 或 有一个 有前缀的并且置顶的快照 和 一个 有前缀未置顶的快照&lt;br /&gt;
&lt;br /&gt;
都会执行 删除置顶的快照(如果有)然后置顶那个 未置顶的快照&lt;br /&gt;
&lt;br /&gt;
==== 查看日志 &amp;lt;code&amp;gt;cat /var/log/bwg_snapshot.log&amp;lt;/code&amp;gt; ====&lt;br /&gt;
[[File:Finalimg.png|border]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1276</id>
		<title>使用搬瓦工api自动周期性创建快照并telegrambot通知</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1276"/>
		<updated>2026-01-29T01:18:30Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​增加一条解释&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== &#039;&#039;&#039;&#039;&#039;注意：&#039;&#039;&#039; 创建快照的过程中，虚拟机会自动重启!&#039;&#039; ==&lt;br /&gt;
请先阅读关于 快照、备份与恢复 了解 此项功能 &lt;br /&gt;
&lt;br /&gt;
[[快照、备份与恢复]]&lt;br /&gt;
&lt;br /&gt;
== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBot bot token和chat_id ===&lt;br /&gt;
请参考 [[使用搬瓦工api监控网络流量的使用并telegrambot通知]]&lt;br /&gt;
&lt;br /&gt;
再次提醒：&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
=== 获取搬瓦工api_key和veid(服务器id) ===&lt;br /&gt;
请参考: [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
再次提醒:&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
== 开始配置 ==&lt;br /&gt;
&lt;br /&gt;
=== 切换root账户 ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 先检查是否安装了jq ===&lt;br /&gt;
输入 &amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果输出&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-bash: jq: command not found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
说明没有安装&lt;br /&gt;
&lt;br /&gt;
==== 安装jq ====&lt;br /&gt;
用于解析api返回的复杂内容&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. Debian / Ubuntu&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt update sudo apt install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. CentOS / RHEL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果是 CentOS 7，可能需要先安装 epel-release &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install epel-release -y&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果是 RHEL 8/9 或 AlmaLinux&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo dnf install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 验证是否安装成功 ===&lt;br /&gt;
安装完成后，你可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Jqcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 脚本 ===&lt;br /&gt;
需要修改的地方&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot; #快照描述的前缀 作为判断快照是否创建、删除、锁定的重要依据&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot; # 日志文件路径&lt;br /&gt;
----检查 /root/autosnapshot.sh 是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfilehave.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面 蓝色框框就是文件不存在 就可以不修改文件名&lt;br /&gt;
&lt;br /&gt;
下面的红色框框 就是文件存在 需要修改文件名字 /root/这里是文件名字.sh 比如修改成/root/autosnapshot11.sh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
把下面内容修改后 就可以复制粘贴到ssh里面 然后按回车 就可以创建脚本文件了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/autosnapshot.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区域 =================&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot;&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot;&lt;br /&gt;
# ===========================================&lt;br /&gt;
&lt;br /&gt;
API_URL=&amp;quot;https://api.64clouds.com/v1&amp;quot;&lt;br /&gt;
DATE_TODAY=$(date +&amp;quot;%Y_%m_%d&amp;quot;)&lt;br /&gt;
CURRENT_SNAPSHOT_NAME=&amp;quot;${TAG_PREFIX}_${DATE_TODAY}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
log() {&lt;br /&gt;
    # 检查日志文件是否存在&lt;br /&gt;
    if [ -f &amp;quot;$LOG_FILE&amp;quot; ]; then&lt;br /&gt;
        # 获取当前行数&lt;br /&gt;
        local current_lines=$(grep -c &amp;quot;&amp;quot; &amp;quot;$LOG_FILE&amp;quot;)&lt;br /&gt;
        if [ &amp;quot;$current_lines&amp;quot; -gt &amp;quot;$MAX_LOG_LINES&amp;quot; ]; then&lt;br /&gt;
            # 超过行数，保留最后 10 行并标记清空，或者直接清空&lt;br /&gt;
            echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] 日志达到上限，执行自动清理。&amp;quot; &amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
    # 正常写入日志&lt;br /&gt;
    echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] $1&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
send_tg() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    # 发送 TG 消息并根据 curl 状态码记录日志&lt;br /&gt;
    curl -s -X POST &amp;quot;https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=${TG_CHAT_ID}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;text=${msg}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;disable_notification=true&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
    &lt;br /&gt;
    if [ $? -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;Telegram 通知发送成功: $msg&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;Telegram 通知发送失败，请检查网络或 Token。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
call_api() {&lt;br /&gt;
    local endpoint=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local extra_params=&amp;quot;$2&amp;quot;&lt;br /&gt;
    # --max-time 30: 设置最大超时时间为30秒&lt;br /&gt;
    # --retry 3: 如果失败自动重试3次&lt;br /&gt;
    curl -s --max-time 30 --retry 3 -X POST &amp;quot;${API_URL}/${endpoint}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;veid=${VEID}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;api_key=${API_KEY}&amp;quot; \&lt;br /&gt;
         ${extra_params}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 【新增函数】提取快照数据的公共逻辑&lt;br /&gt;
get_snapshot_map() {&lt;br /&gt;
    echo &amp;quot;$SNAPSHOT_LIST&amp;quot; | jq -r --arg tag &amp;quot;$TAG_PREFIX&amp;quot; &#039;&lt;br /&gt;
        .snapshots[] | &lt;br /&gt;
        select(try (.description | @base64d) catch &amp;quot;&amp;quot; | contains($tag)) | &lt;br /&gt;
        &amp;quot;\(.sticky)|\((.description | @base64d))|\(.fileName)&amp;quot;&lt;br /&gt;
    &#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 预检&lt;br /&gt;
if ! command -v jq &amp;amp;&amp;gt; /dev/null; then&lt;br /&gt;
    log &amp;quot;错误: 未安装 jq，请安装jq&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;--- 脚本启动 ---&amp;quot;&lt;br /&gt;
SNAPSHOT_LIST=$(call_api &amp;quot;snapshot/list&amp;quot; &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 1: 开机补刀/清理逻辑 (默认) =================&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; != &amp;quot;-c&amp;quot; ]]; then&lt;br /&gt;
    log &amp;quot;进入开机自检模式...&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 调用函数获取数据&lt;br /&gt;
    MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
    # 使用 grep -c 直接统计行数，不再使用 wc -l&lt;br /&gt;
    count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
    count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    log &amp;quot;开机自检状态: 符合前缀的置顶数 $count_a, 符合前缀的未置顶数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;状态 [0,0]：未发现任何相关快照，无需操作。&amp;quot;&lt;br /&gt;
        exit 0&lt;br /&gt;
&lt;br /&gt;
    elif [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
        NEW_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
        NEW_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
        &lt;br /&gt;
        if [ &amp;quot;$count_a&amp;quot; -eq 1 ]; then&lt;br /&gt;
            OLD_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
            OLD_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
            &lt;br /&gt;
            log &amp;quot;发现新快照，正在删除旧的已置顶快照: $OLD_DESC&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            DEL_RES=$(call_api &amp;quot;snapshot/delete&amp;quot; &amp;quot;--data-urlencode snapshot=${OLD_FILENAME}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            # 【替换 1】使用 jq 判断删除结果&lt;br /&gt;
            API_ERR_DEL=$(echo &amp;quot;$DEL_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
            if [ &amp;quot;$API_ERR_DEL&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
                log &amp;quot;旧快照删除成功。&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;已成功删除旧快照: ${OLD_DESC}。&amp;quot;&lt;br /&gt;
            else&lt;br /&gt;
                log &amp;quot;旧快照删除失败: $DEL_RES&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;删除旧快照发生错误。&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
        # 锁定新快照(b)&lt;br /&gt;
        log &amp;quot;正在锁定新快照: $NEW_DESC ...&amp;quot;&lt;br /&gt;
        LOCK_RES=$(call_api &amp;quot;snapshot/toggleSticky&amp;quot; &amp;quot;--data-urlencode snapshot=${NEW_FILENAME} -d sticky=1&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 使用 jq 解析 error 字段的值是否等于 0&lt;br /&gt;
        API_ERROR=$(echo &amp;quot;$LOCK_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$API_ERROR&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;新快照锁定成功。&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;✅ 快照更替完成！${DEL_MSG}新快照 ${NEW_DESC} 已成功锁定。&amp;quot;&lt;br /&gt;
            exit 0&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;新快照锁定失败，API返回: $LOCK_RES&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;❌ ${DEL_MSG}但新快照 ${NEW_DESC} 锁定失败，解析出的错误码为: ${API_ERROR}&amp;quot;&lt;br /&gt;
            exit 1&lt;br /&gt;
        fi&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;自检状态 [$count_a,$count_b]：不满足自动更替条件。&amp;quot;&lt;br /&gt;
        send_tg &amp;quot;ℹ️ 搬瓦工自检报告：当前状态为 [$count_a,$count_b]，未达到快照更迭条件,脚本未执行操作。请手动查看..&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 2: 创建逻辑 (-c 参数) =================&lt;br /&gt;
log &amp;quot;进入快照创建模式 (-c)...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 调用函数获取数据&lt;br /&gt;
MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;状态检查: 符合前缀的已置顶快照数 $count_a, 符合前缀的未置顶的快照数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
DO_CREATE=false&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,0]：准备发起首次创建。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [1,0]：准备创建新的备份。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,1]：存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;⚠️ 存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 2 ]; then&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：置顶名额满。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;🚫 快照置顶名额已满 (2/2)，请手动删除旧备份。&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：未知状态，停止操作。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;❌ 未创建快照：不清楚的快照数量 (a=$count_a, b=$count_b)。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$DO_CREATE&amp;quot; = true ]; then&lt;br /&gt;
    # 再次检查日期防止重复&lt;br /&gt;
    if echo &amp;quot;$MAP_DATA&amp;quot; | grep -q &amp;quot;$DATE_TODAY&amp;quot;; then&lt;br /&gt;
        log &amp;quot;今日快照已存在，跳过。今日: $DATE_TODAY&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;发起创建快照请求: $CURRENT_SNAPSHOT_NAME&amp;quot;&lt;br /&gt;
        CREATE_RESULT=$(call_api &amp;quot;snapshot/create&amp;quot; &amp;quot;--data-urlencode description=${CURRENT_SNAPSHOT_NAME}&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 【替换 2】使用 jq 判断创建请求结果&lt;br /&gt;
        API_ERR_CREATE=$(echo &amp;quot;$CREATE_RESULT&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
        if [ &amp;quot;$API_ERR_CREATE&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;创建快照请求成功发送。&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;创建快照请求失败: $CREATE_RESULT&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;⚠️ 搬瓦工 API 创建快照请求返回异常。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;授予运行权限&lt;br /&gt;
&lt;br /&gt;
输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod +x /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
查看是否成功 输入 &amp;lt;code&amp;gt;ls -l /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Lsautosnapshot.png|border]]&lt;br /&gt;
&lt;br /&gt;
像蓝色的有x就是成功了 像上面后红色的- 没有x 就是失败了...&lt;br /&gt;
&lt;br /&gt;
=== 配置开机置顶快照 和定期创建快照 ===&lt;br /&gt;
&#039;&#039;&#039;0 3 * * 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
这是一个cron的表达式 用来设定 运行时间的&lt;br /&gt;
&lt;br /&gt;
0 代表 第 0 分钟的时候&lt;br /&gt;
&lt;br /&gt;
3 代表 第3小时的时候 就是凌晨3点&lt;br /&gt;
&lt;br /&gt;
第一个 * 代表 哪一天 *代表所有天&lt;br /&gt;
&lt;br /&gt;
第二个 * 代表 哪一月 *代表所有月&lt;br /&gt;
&lt;br /&gt;
1 代表 星期一&lt;br /&gt;
&lt;br /&gt;
合起来就是&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;每周一的凌晨3:00运行 创建快照的指令&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
可以按照自己的时间自行修改&lt;br /&gt;
&lt;br /&gt;
0 3 * * 1 root /usr/local/bin/bwg_snap.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
开机启动检查是否有未锁定的快照(认为是上一次的快照) &lt;br /&gt;
&lt;br /&gt;
如果文件名 改了的话 这里也要修改 其他的地方不需要 &lt;br /&gt;
&lt;br /&gt;
@reboot root /usr/local/bin/bwg_snap.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
检查定时文件是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkbwgsnap.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面红色框框 就是 文件不存在 不需要修改名字&lt;br /&gt;
&lt;br /&gt;
下面蓝色框框 就是 文件存在 需要文件名字../etc/cron.d/你需要修改的名字  比如/etc/cron.d/bwg_snap1&lt;br /&gt;
&lt;br /&gt;
修改后 复制粘贴到ssh里面 回车就行了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.d/bwg_snap&lt;br /&gt;
# 每周一凌晨 3 点创建快照&lt;br /&gt;
0 3 * * 1 root /usr/local/bin/bwg_snap.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
# 开机自启&lt;br /&gt;
@reboot root /usr/local/bin/bwg_snap.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;# 修改文件权限 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod 644 /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Finalcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
如果你想测试 可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/root/autosnapshot.sh -c&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
根据时间 一天只能创建一个快照....脚本设定 非api设置的...&lt;br /&gt;
&lt;br /&gt;
创建快照 系统会重启 请注意为保存的数据会丢失...&lt;br /&gt;
&lt;br /&gt;
开机后应该会自动置顶这个快照 或 手动输入 &amp;lt;code&amp;gt;/root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
检查是否存在 有前缀名但未置顶的快照 或 有前缀 或 有一个 有前缀的并且置顶的快照 和 一个 有前缀未置顶的快照&lt;br /&gt;
&lt;br /&gt;
都会执行 删除置顶的快照(如果有)然后置顶那个 未置顶的快照&lt;br /&gt;
&lt;br /&gt;
==== 查看日志 &amp;lt;code&amp;gt;cat /var/log/bwg_snapshot.log&amp;lt;/code&amp;gt; ====&lt;br /&gt;
[[File:Finalimg.png|border]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1265</id>
		<title>使用搬瓦工api自动周期性创建快照并telegrambot通知</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1265"/>
		<updated>2026-01-28T08:04:57Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​修复排版问题&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== &#039;&#039;&#039;&#039;&#039;注意：&#039;&#039;&#039; 创建快照的过程中，虚拟机会自动重启!&#039;&#039; ==&lt;br /&gt;
请先阅读关于 快照、备份与恢复 了解 此项功能 &lt;br /&gt;
&lt;br /&gt;
[[快照、备份与恢复]]&lt;br /&gt;
&lt;br /&gt;
== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBot bot token和chat_id ===&lt;br /&gt;
请参考 [[使用搬瓦工api监控网络流量的使用并telegrambot通知]]&lt;br /&gt;
&lt;br /&gt;
再次提醒：&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
=== 获取搬瓦工api_key和veid(服务器id) ===&lt;br /&gt;
请参考: [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
再次提醒:&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
== 开始配置 ==&lt;br /&gt;
&lt;br /&gt;
=== 切换root账户 ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 先检查是否安装了jq ===&lt;br /&gt;
输入 &amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果输出&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-bash: jq: command not found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
说明没有安装&lt;br /&gt;
&lt;br /&gt;
==== 安装jq ====&lt;br /&gt;
&#039;&#039;&#039;1. Debian / Ubuntu&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt update sudo apt install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. CentOS / RHEL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果是 CentOS 7，可能需要先安装 epel-release &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install epel-release -y&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果是 RHEL 8/9 或 AlmaLinux&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo dnf install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 验证是否安装成功 ===&lt;br /&gt;
安装完成后，你可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Jqcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 脚本 ===&lt;br /&gt;
需要修改的地方&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot; #快照描述的前缀 作为判断快照是否创建、删除、锁定的重要依据&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot; # 日志文件路径&lt;br /&gt;
----检查 /root/autosnapshot.sh 是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfilehave.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面 蓝色框框就是文件不存在 就可以不修改文件名&lt;br /&gt;
&lt;br /&gt;
下面的红色框框 就是文件存在 需要修改文件名字 /root/这里是文件名字.sh 比如修改成/root/autosnapshot11.sh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
把下面内容修改后 就可以复制粘贴到ssh里面 然后按回车 就可以创建脚本文件了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/autosnapshot.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区域 =================&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot;&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot;&lt;br /&gt;
# ===========================================&lt;br /&gt;
&lt;br /&gt;
API_URL=&amp;quot;https://api.64clouds.com/v1&amp;quot;&lt;br /&gt;
DATE_TODAY=$(date +&amp;quot;%Y_%m_%d&amp;quot;)&lt;br /&gt;
CURRENT_SNAPSHOT_NAME=&amp;quot;${TAG_PREFIX}_${DATE_TODAY}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
log() {&lt;br /&gt;
    # 检查日志文件是否存在&lt;br /&gt;
    if [ -f &amp;quot;$LOG_FILE&amp;quot; ]; then&lt;br /&gt;
        # 获取当前行数&lt;br /&gt;
        local current_lines=$(grep -c &amp;quot;&amp;quot; &amp;quot;$LOG_FILE&amp;quot;)&lt;br /&gt;
        if [ &amp;quot;$current_lines&amp;quot; -gt &amp;quot;$MAX_LOG_LINES&amp;quot; ]; then&lt;br /&gt;
            # 超过行数，保留最后 10 行并标记清空，或者直接清空&lt;br /&gt;
            echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] 日志达到上限，执行自动清理。&amp;quot; &amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
    # 正常写入日志&lt;br /&gt;
    echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] $1&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
send_tg() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    # 发送 TG 消息并根据 curl 状态码记录日志&lt;br /&gt;
    curl -s -X POST &amp;quot;https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=${TG_CHAT_ID}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;text=${msg}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;disable_notification=true&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
    &lt;br /&gt;
    if [ $? -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;Telegram 通知发送成功: $msg&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;Telegram 通知发送失败，请检查网络或 Token。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
call_api() {&lt;br /&gt;
    local endpoint=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local extra_params=&amp;quot;$2&amp;quot;&lt;br /&gt;
    # --max-time 30: 设置最大超时时间为30秒&lt;br /&gt;
    # --retry 3: 如果失败自动重试3次&lt;br /&gt;
    curl -s --max-time 30 --retry 3 -X POST &amp;quot;${API_URL}/${endpoint}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;veid=${VEID}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;api_key=${API_KEY}&amp;quot; \&lt;br /&gt;
         ${extra_params}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 【新增函数】提取快照数据的公共逻辑&lt;br /&gt;
get_snapshot_map() {&lt;br /&gt;
    echo &amp;quot;$SNAPSHOT_LIST&amp;quot; | jq -r --arg tag &amp;quot;$TAG_PREFIX&amp;quot; &#039;&lt;br /&gt;
        .snapshots[] | &lt;br /&gt;
        select(try (.description | @base64d) catch &amp;quot;&amp;quot; | contains($tag)) | &lt;br /&gt;
        &amp;quot;\(.sticky)|\((.description | @base64d))|\(.fileName)&amp;quot;&lt;br /&gt;
    &#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 预检&lt;br /&gt;
if ! command -v jq &amp;amp;&amp;gt; /dev/null; then&lt;br /&gt;
    log &amp;quot;错误: 未安装 jq，请安装jq&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;--- 脚本启动 ---&amp;quot;&lt;br /&gt;
SNAPSHOT_LIST=$(call_api &amp;quot;snapshot/list&amp;quot; &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 1: 开机补刀/清理逻辑 (默认) =================&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; != &amp;quot;-c&amp;quot; ]]; then&lt;br /&gt;
    log &amp;quot;进入开机自检模式...&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 调用函数获取数据&lt;br /&gt;
    MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
    # 使用 grep -c 直接统计行数，不再使用 wc -l&lt;br /&gt;
    count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
    count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    log &amp;quot;开机自检状态: 符合前缀的置顶数 $count_a, 符合前缀的未置顶数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;状态 [0,0]：未发现任何相关快照，无需操作。&amp;quot;&lt;br /&gt;
        exit 0&lt;br /&gt;
&lt;br /&gt;
    elif [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
        NEW_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
        NEW_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
        &lt;br /&gt;
        if [ &amp;quot;$count_a&amp;quot; -eq 1 ]; then&lt;br /&gt;
            OLD_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
            OLD_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
            &lt;br /&gt;
            log &amp;quot;发现新快照，正在删除旧的已置顶快照: $OLD_DESC&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            DEL_RES=$(call_api &amp;quot;snapshot/delete&amp;quot; &amp;quot;--data-urlencode snapshot=${OLD_FILENAME}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            # 【替换 1】使用 jq 判断删除结果&lt;br /&gt;
            API_ERR_DEL=$(echo &amp;quot;$DEL_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
            if [ &amp;quot;$API_ERR_DEL&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
                log &amp;quot;旧快照删除成功。&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;已成功删除旧快照: ${OLD_DESC}。&amp;quot;&lt;br /&gt;
            else&lt;br /&gt;
                log &amp;quot;旧快照删除失败: $DEL_RES&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;删除旧快照发生错误。&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
        # 锁定新快照(b)&lt;br /&gt;
        log &amp;quot;正在锁定新快照: $NEW_DESC ...&amp;quot;&lt;br /&gt;
        LOCK_RES=$(call_api &amp;quot;snapshot/toggleSticky&amp;quot; &amp;quot;--data-urlencode snapshot=${NEW_FILENAME} -d sticky=1&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 使用 jq 解析 error 字段的值是否等于 0&lt;br /&gt;
        API_ERROR=$(echo &amp;quot;$LOCK_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$API_ERROR&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;新快照锁定成功。&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;✅ 快照更替完成！${DEL_MSG}新快照 ${NEW_DESC} 已成功锁定。&amp;quot;&lt;br /&gt;
            exit 0&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;新快照锁定失败，API返回: $LOCK_RES&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;❌ ${DEL_MSG}但新快照 ${NEW_DESC} 锁定失败，解析出的错误码为: ${API_ERROR}&amp;quot;&lt;br /&gt;
            exit 1&lt;br /&gt;
        fi&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;自检状态 [$count_a,$count_b]：不满足自动更替条件。&amp;quot;&lt;br /&gt;
        send_tg &amp;quot;ℹ️ 搬瓦工自检报告：当前状态为 [$count_a,$count_b]，未达到快照更迭条件,脚本未执行操作。请手动查看..&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 2: 创建逻辑 (-c 参数) =================&lt;br /&gt;
log &amp;quot;进入快照创建模式 (-c)...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 调用函数获取数据&lt;br /&gt;
MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;状态检查: 符合前缀的已置顶快照数 $count_a, 符合前缀的未置顶的快照数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
DO_CREATE=false&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,0]：准备发起首次创建。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [1,0]：准备创建新的备份。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,1]：存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;⚠️ 存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 2 ]; then&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：置顶名额满。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;🚫 快照置顶名额已满 (2/2)，请手动删除旧备份。&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：未知状态，停止操作。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;❌ 未创建快照：不清楚的快照数量 (a=$count_a, b=$count_b)。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$DO_CREATE&amp;quot; = true ]; then&lt;br /&gt;
    # 再次检查日期防止重复&lt;br /&gt;
    if echo &amp;quot;$MAP_DATA&amp;quot; | grep -q &amp;quot;$DATE_TODAY&amp;quot;; then&lt;br /&gt;
        log &amp;quot;今日快照已存在，跳过。今日: $DATE_TODAY&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;发起创建快照请求: $CURRENT_SNAPSHOT_NAME&amp;quot;&lt;br /&gt;
        CREATE_RESULT=$(call_api &amp;quot;snapshot/create&amp;quot; &amp;quot;--data-urlencode description=${CURRENT_SNAPSHOT_NAME}&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 【替换 2】使用 jq 判断创建请求结果&lt;br /&gt;
        API_ERR_CREATE=$(echo &amp;quot;$CREATE_RESULT&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
        if [ &amp;quot;$API_ERR_CREATE&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;创建快照请求成功发送。&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;创建快照请求失败: $CREATE_RESULT&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;⚠️ 搬瓦工 API 创建快照请求返回异常。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;授予运行权限&lt;br /&gt;
&lt;br /&gt;
输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod +x /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
查看是否成功 输入 &amp;lt;code&amp;gt;ls -l /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Lsautosnapshot.png|border]]&lt;br /&gt;
&lt;br /&gt;
像蓝色的有x就是成功了 像上面后红色的- 没有x 就是失败了...&lt;br /&gt;
&lt;br /&gt;
=== 配置开机置顶快照 和定期创建快照 ===&lt;br /&gt;
&#039;&#039;&#039;0 3 * * 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
这是一个cron的表达式 用来设定 运行时间的&lt;br /&gt;
&lt;br /&gt;
0 代表 第 0 分钟的时候&lt;br /&gt;
&lt;br /&gt;
3 代表 第3小时的时候 就是凌晨3点&lt;br /&gt;
&lt;br /&gt;
第一个 * 代表 哪一天 *代表所有天&lt;br /&gt;
&lt;br /&gt;
第二个 * 代表 哪一月 *代表所有月&lt;br /&gt;
&lt;br /&gt;
1 代表 星期一&lt;br /&gt;
&lt;br /&gt;
合起来就是&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;每周一的凌晨3:00运行 创建快照的指令&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
可以按照自己的时间自行修改&lt;br /&gt;
&lt;br /&gt;
0 3 * * 1 root /usr/local/bin/bwg_snap.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
开机启动检查是否有未锁定的快照(认为是上一次的快照) &lt;br /&gt;
&lt;br /&gt;
如果文件名 改了的话 这里也要修改 其他的地方不需要 &lt;br /&gt;
&lt;br /&gt;
@reboot root /usr/local/bin/bwg_snap.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
检查定时文件是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkbwgsnap.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面红色框框 就是 文件不存在 不需要修改名字&lt;br /&gt;
&lt;br /&gt;
下面蓝色框框 就是 文件存在 需要文件名字../etc/cron.d/你需要修改的名字  比如/etc/cron.d/bwg_snap1&lt;br /&gt;
&lt;br /&gt;
修改后 复制粘贴到ssh里面 回车就行了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.d/bwg_snap&lt;br /&gt;
# 每周一凌晨 3 点创建快照&lt;br /&gt;
0 3 * * 1 root /usr/local/bin/bwg_snap.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
# 开机自启&lt;br /&gt;
@reboot root /usr/local/bin/bwg_snap.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;# 修改文件权限 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod 644 /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Finalcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
如果你想测试 可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/root/autosnapshot.sh -c&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
根据时间 一天只能创建一个快照....脚本设定 非api设置的...&lt;br /&gt;
&lt;br /&gt;
创建快照 系统会重启 请注意为保存的数据会丢失...&lt;br /&gt;
&lt;br /&gt;
开机后应该会自动置顶这个快照 或 手动输入 &amp;lt;code&amp;gt;/root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
检查是否存在 有前缀名但未置顶的快照 或 有前缀 或 有一个 有前缀的并且置顶的快照 和 一个 有前缀未置顶的快照&lt;br /&gt;
&lt;br /&gt;
都会执行 删除置顶的快照(如果有)然后置顶那个 未置顶的快照&lt;br /&gt;
&lt;br /&gt;
==== 查看日志 &amp;lt;code&amp;gt;cat /var/log/bwg_snapshot.log&amp;lt;/code&amp;gt; ====&lt;br /&gt;
[[File:Finalimg.png|border]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1264</id>
		<title>使用搬瓦工api自动周期性创建快照并telegrambot通知</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E8%87%AA%E5%8A%A8%E5%91%A8%E6%9C%9F%E6%80%A7%E5%88%9B%E5%BB%BA%E5%BF%AB%E7%85%A7%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1264"/>
		<updated>2026-01-28T01:22:56Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​创建使用搬瓦工api自动周期性创建快照并telegrambot通知&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== &#039;&#039;&#039;&#039;&#039;注意：&#039;&#039;&#039; 创建快照的过程中，虚拟机会自动重启!&#039;&#039; ==&lt;br /&gt;
请先阅读关于 快照、备份与恢复 了解 此项功能 &lt;br /&gt;
&lt;br /&gt;
[[快照、备份与恢复]]&lt;br /&gt;
&lt;br /&gt;
== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBot bot token和chat_id ===&lt;br /&gt;
请参考 [[使用搬瓦工api监控网络流量的使用并telegrambot通知]]&lt;br /&gt;
&lt;br /&gt;
再次提醒：&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
务必保管好bot token 和 chat_id&lt;br /&gt;
&lt;br /&gt;
== 获取搬瓦工api_key和veid(服务器id) ==&lt;br /&gt;
请参考: [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
再次提醒:&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
务必保管好api_key 和 veid 不要泄漏给任何人知道&lt;br /&gt;
&lt;br /&gt;
== 开始配置 ==&lt;br /&gt;
&lt;br /&gt;
=== 切换root账户 ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 先检查是否安装了jq ===&lt;br /&gt;
输入 &amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果输出&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-bash: jq: command not found&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
说明没有安装&lt;br /&gt;
&lt;br /&gt;
==== 安装jq ====&lt;br /&gt;
&#039;&#039;&#039;1. Debian / Ubuntu&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt update sudo apt install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. CentOS / RHEL&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果是 CentOS 7，可能需要先安装 epel-release &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install epel-release -y&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo yum install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果是 RHEL 8/9 或 AlmaLinux&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo dnf install jq -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 验证是否安装成功 ===&lt;br /&gt;
安装完成后，你可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;jq --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Jqcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 脚本 ===&lt;br /&gt;
需要修改的地方&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot; #快照描述的前缀 作为判断快照是否创建、删除、锁定的重要依据&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot; # 日志文件路径&lt;br /&gt;
----检查 /root/autosnapshot.sh 是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfilehave.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面 蓝色框框就是文件不存在 就可以不修改文件名&lt;br /&gt;
&lt;br /&gt;
下面的红色框框 就是文件存在 需要修改文件名字 /root/这里是文件名字.sh 比如修改成/root/autosnapshot11.sh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
把下面内容修改后 就可以复制粘贴到ssh里面 然后按回车 就可以创建脚本文件了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/autosnapshot.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区域 =================&lt;br /&gt;
VEID=&amp;quot;你的VEID&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的API_KEY&amp;quot;&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的BOT_TOKEN&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的CHAT_ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
MAX_LOG_LINES=500  # 设置日志最大保留行数，超过则清空&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;/var/log/bwg_snapshot.log&amp;quot;&lt;br /&gt;
TAG_PREFIX=&amp;quot;bwgApiAutoSnapShot&amp;quot;&lt;br /&gt;
# ===========================================&lt;br /&gt;
&lt;br /&gt;
API_URL=&amp;quot;https://api.64clouds.com/v1&amp;quot;&lt;br /&gt;
DATE_TODAY=$(date +&amp;quot;%Y_%m_%d&amp;quot;)&lt;br /&gt;
CURRENT_SNAPSHOT_NAME=&amp;quot;${TAG_PREFIX}_${DATE_TODAY}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
log() {&lt;br /&gt;
    # 检查日志文件是否存在&lt;br /&gt;
    if [ -f &amp;quot;$LOG_FILE&amp;quot; ]; then&lt;br /&gt;
        # 获取当前行数&lt;br /&gt;
        local current_lines=$(grep -c &amp;quot;&amp;quot; &amp;quot;$LOG_FILE&amp;quot;)&lt;br /&gt;
        if [ &amp;quot;$current_lines&amp;quot; -gt &amp;quot;$MAX_LOG_LINES&amp;quot; ]; then&lt;br /&gt;
            # 超过行数，保留最后 10 行并标记清空，或者直接清空&lt;br /&gt;
            echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] 日志达到上限，执行自动清理。&amp;quot; &amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
    # 正常写入日志&lt;br /&gt;
    echo &amp;quot;[$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)] $1&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
send_tg() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    # 发送 TG 消息并根据 curl 状态码记录日志&lt;br /&gt;
    curl -s -X POST &amp;quot;https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=${TG_CHAT_ID}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;text=${msg}&amp;quot; \&lt;br /&gt;
        -d &amp;quot;disable_notification=true&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
    &lt;br /&gt;
    if [ $? -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;Telegram 通知发送成功: $msg&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;Telegram 通知发送失败，请检查网络或 Token。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
call_api() {&lt;br /&gt;
    local endpoint=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local extra_params=&amp;quot;$2&amp;quot;&lt;br /&gt;
    # --max-time 30: 设置最大超时时间为30秒&lt;br /&gt;
    # --retry 3: 如果失败自动重试3次&lt;br /&gt;
    curl -s --max-time 30 --retry 3 -X POST &amp;quot;${API_URL}/${endpoint}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;veid=${VEID}&amp;quot; \&lt;br /&gt;
         -d &amp;quot;api_key=${API_KEY}&amp;quot; \&lt;br /&gt;
         ${extra_params}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 【新增函数】提取快照数据的公共逻辑&lt;br /&gt;
get_snapshot_map() {&lt;br /&gt;
    echo &amp;quot;$SNAPSHOT_LIST&amp;quot; | jq -r --arg tag &amp;quot;$TAG_PREFIX&amp;quot; &#039;&lt;br /&gt;
        .snapshots[] | &lt;br /&gt;
        select(try (.description | @base64d) catch &amp;quot;&amp;quot; | contains($tag)) | &lt;br /&gt;
        &amp;quot;\(.sticky)|\((.description | @base64d))|\(.fileName)&amp;quot;&lt;br /&gt;
    &#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 预检&lt;br /&gt;
if ! command -v jq &amp;amp;&amp;gt; /dev/null; then&lt;br /&gt;
    log &amp;quot;错误: 未安装 jq，请安装jq&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;--- 脚本启动 ---&amp;quot;&lt;br /&gt;
SNAPSHOT_LIST=$(call_api &amp;quot;snapshot/list&amp;quot; &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 1: 开机补刀/清理逻辑 (默认) =================&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; != &amp;quot;-c&amp;quot; ]]; then&lt;br /&gt;
    log &amp;quot;进入开机自检模式...&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 调用函数获取数据&lt;br /&gt;
    MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
    # 使用 grep -c 直接统计行数，不再使用 wc -l&lt;br /&gt;
    count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
    count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    log &amp;quot;开机自检状态: 符合前缀的置顶数 $count_a, 符合前缀的未置顶数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
        log &amp;quot;状态 [0,0]：未发现任何相关快照，无需操作。&amp;quot;&lt;br /&gt;
        exit 0&lt;br /&gt;
&lt;br /&gt;
    elif [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
        NEW_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
        NEW_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^false&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
        &lt;br /&gt;
        if [ &amp;quot;$count_a&amp;quot; -eq 1 ]; then&lt;br /&gt;
            OLD_FILENAME=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 3)&lt;br /&gt;
            OLD_DESC=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep &amp;quot;^true&amp;quot; | cut -d &amp;quot;|&amp;quot; -f 2)&lt;br /&gt;
            &lt;br /&gt;
            log &amp;quot;发现新快照，正在删除旧的已置顶快照: $OLD_DESC&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            DEL_RES=$(call_api &amp;quot;snapshot/delete&amp;quot; &amp;quot;--data-urlencode snapshot=${OLD_FILENAME}&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            # 【替换 1】使用 jq 判断删除结果&lt;br /&gt;
            API_ERR_DEL=$(echo &amp;quot;$DEL_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
            if [ &amp;quot;$API_ERR_DEL&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
                log &amp;quot;旧快照删除成功。&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;已成功删除旧快照: ${OLD_DESC}。&amp;quot;&lt;br /&gt;
            else&lt;br /&gt;
                log &amp;quot;旧快照删除失败: $DEL_RES&amp;quot;&lt;br /&gt;
                DEL_MSG=&amp;quot;删除旧快照发生错误。&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
        # 锁定新快照(b)&lt;br /&gt;
        log &amp;quot;正在锁定新快照: $NEW_DESC ...&amp;quot;&lt;br /&gt;
        LOCK_RES=$(call_api &amp;quot;snapshot/toggleSticky&amp;quot; &amp;quot;--data-urlencode snapshot=${NEW_FILENAME} -d sticky=1&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 使用 jq 解析 error 字段的值是否等于 0&lt;br /&gt;
        API_ERROR=$(echo &amp;quot;$LOCK_RES&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$API_ERROR&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;新快照锁定成功。&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;✅ 快照更替完成！${DEL_MSG}新快照 ${NEW_DESC} 已成功锁定。&amp;quot;&lt;br /&gt;
            exit 0&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;新快照锁定失败，API返回: $LOCK_RES&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;❌ ${DEL_MSG}但新快照 ${NEW_DESC} 锁定失败，解析出的错误码为: ${API_ERROR}&amp;quot;&lt;br /&gt;
            exit 1&lt;br /&gt;
        fi&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;自检状态 [$count_a,$count_b]：不满足自动更替条件。&amp;quot;&lt;br /&gt;
        send_tg &amp;quot;ℹ️ 搬瓦工自检报告：当前状态为 [$count_a,$count_b]，未达到快照更迭条件,脚本未执行操作。请手动查看..&amp;quot;&lt;br /&gt;
        exit 1&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# ================= 模式 2: 创建逻辑 (-c 参数) =================&lt;br /&gt;
log &amp;quot;进入快照创建模式 (-c)...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 调用函数获取数据&lt;br /&gt;
MAP_DATA=$(get_snapshot_map)&lt;br /&gt;
&lt;br /&gt;
count_a=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^true&amp;quot;)&lt;br /&gt;
count_b=$(echo &amp;quot;$MAP_DATA&amp;quot; | grep -c &amp;quot;^false&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
log &amp;quot;状态检查: 符合前缀的已置顶快照数 $count_a, 符合前缀的未置顶的快照数 $count_b&amp;quot;&lt;br /&gt;
&lt;br /&gt;
DO_CREATE=false&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,0]：准备发起首次创建。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 0 ]; then&lt;br /&gt;
    log &amp;quot;状态 [1,0]：准备创建新的备份。&amp;quot;&lt;br /&gt;
    DO_CREATE=true&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 0 ] &amp;amp;&amp;amp; [ &amp;quot;$count_b&amp;quot; -eq 1 ]; then&lt;br /&gt;
    log &amp;quot;状态 [0,1]：存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;⚠️ 存在未锁定的快照，无法判断未锁定的快照目的,停止自动化创建快照,请锁定或删除旧快照或更改description&amp;quot;&lt;br /&gt;
elif [ &amp;quot;$count_a&amp;quot; -eq 2 ]; then&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：置顶名额满。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;🚫 快照置顶名额已满 (2/2)，请手动删除旧备份。&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    log &amp;quot;状态 [$count_a,$count_b]：未知状态，停止操作。&amp;quot;&lt;br /&gt;
    send_tg &amp;quot;❌ 未创建快照：不清楚的快照数量 (a=$count_a, b=$count_b)。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$DO_CREATE&amp;quot; = true ]; then&lt;br /&gt;
    # 再次检查日期防止重复&lt;br /&gt;
    if echo &amp;quot;$MAP_DATA&amp;quot; | grep -q &amp;quot;$DATE_TODAY&amp;quot;; then&lt;br /&gt;
        log &amp;quot;今日快照已存在，跳过。今日: $DATE_TODAY&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        log &amp;quot;发起创建快照请求: $CURRENT_SNAPSHOT_NAME&amp;quot;&lt;br /&gt;
        CREATE_RESULT=$(call_api &amp;quot;snapshot/create&amp;quot; &amp;quot;--data-urlencode description=${CURRENT_SNAPSHOT_NAME}&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        # 【替换 2】使用 jq 判断创建请求结果&lt;br /&gt;
        API_ERR_CREATE=$(echo &amp;quot;$CREATE_RESULT&amp;quot; | jq -r &#039;.error&#039; 2&amp;gt;/dev/null)&lt;br /&gt;
        if [ &amp;quot;$API_ERR_CREATE&amp;quot; = &amp;quot;0&amp;quot; ]; then&lt;br /&gt;
            log &amp;quot;创建快照请求成功发送。&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log &amp;quot;创建快照请求失败: $CREATE_RESULT&amp;quot;&lt;br /&gt;
            send_tg &amp;quot;⚠️ 搬瓦工 API 创建快照请求返回异常。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;授予运行权限&lt;br /&gt;
&lt;br /&gt;
输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod +x /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
查看是否成功 输入 &amp;lt;code&amp;gt;ls -l /root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Lsautosnapshot.png|border]]&lt;br /&gt;
&lt;br /&gt;
像蓝色的有x就是成功了 像上面后红色的- 没有x 就是失败了...&lt;br /&gt;
&lt;br /&gt;
=== 配置开机置顶快照 和定期创建快照 ===&lt;br /&gt;
&#039;&#039;&#039;0 3 * * 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
这是一个cron的表达式 用来设定 运行时间的&lt;br /&gt;
&lt;br /&gt;
0 代表 第 0 分钟的时候&lt;br /&gt;
&lt;br /&gt;
3 代表 第3小时的时候 就是凌晨3点&lt;br /&gt;
&lt;br /&gt;
第一个 * 代表 哪一天 *代表所有天&lt;br /&gt;
&lt;br /&gt;
第二个 * 代表 哪一月 *代表所有月&lt;br /&gt;
&lt;br /&gt;
1 代表 星期一&lt;br /&gt;
&lt;br /&gt;
合起来就是&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;每周一的凌晨3:00运行 创建快照的指令&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
可以按照自己的时间自行修改&lt;br /&gt;
&lt;br /&gt;
0 3 * * 1 root /usr/local/bin/bwg_snap.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
开机启动检查是否有未锁定的快照(认为是上一次的快照) &lt;br /&gt;
&lt;br /&gt;
如果文件名 改了的话 这里也要修改 其他的地方不需要 &lt;br /&gt;
&lt;br /&gt;
@reboot root /usr/local/bin/bwg_snap.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
检查定时文件是否存在&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkbwgsnap.png|border]]&lt;br /&gt;
&lt;br /&gt;
上面红色框框 就是 文件不存在 不需要修改名字&lt;br /&gt;
&lt;br /&gt;
下面蓝色框框 就是 文件存在 需要文件名字../etc/cron.d/你需要修改的名字  比如/etc/cron.d/bwg_snap1&lt;br /&gt;
&lt;br /&gt;
修改后 复制粘贴到ssh里面 回车就行了&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.d/bwg_snap&lt;br /&gt;
# 每周一凌晨 3 点创建快照&lt;br /&gt;
0 3 * * 1 root /usr/local/bin/bwg_snap.sh -c &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
# 开机自启&lt;br /&gt;
@reboot root /usr/local/bin/bwg_snap.sh &amp;gt;&amp;gt; /var/log/bwg_snapshot.log 2&amp;gt;&amp;amp;1&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;# 修改文件权限 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chmod 644 /etc/cron.d/bwg_snap&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Finalcheck.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
如果你想测试 可以输入&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/root/autosnapshot.sh -c&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
根据时间 一天只能创建一个快照....脚本设定 非api设置的...&lt;br /&gt;
&lt;br /&gt;
创建快照 系统会重启 请注意为保存的数据会丢失...&lt;br /&gt;
&lt;br /&gt;
开机后应该会自动置顶这个快照 或 手动输入 &amp;lt;code&amp;gt;/root/autosnapshot.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
检查是否存在 有前缀名但未置顶的快照 或 有前缀 或 有一个 有前缀的并且置顶的快照 和 一个 有前缀未置顶的快照&lt;br /&gt;
&lt;br /&gt;
都会执行 删除置顶的快照(如果有)然后置顶那个 未置顶的快照&lt;br /&gt;
&lt;br /&gt;
==== 查看日志 &amp;lt;code&amp;gt;cat /var/log/bwg_snapshot.log&amp;lt;/code&amp;gt; ====&lt;br /&gt;
[[File:Finalimg.png|border]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Finalimg.png&amp;diff=1263</id>
		<title>File:Finalimg.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Finalimg.png&amp;diff=1263"/>
		<updated>2026-01-28T01:22:10Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;finalimg&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Finalcheck.png&amp;diff=1262</id>
		<title>File:Finalcheck.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Finalcheck.png&amp;diff=1262"/>
		<updated>2026-01-28T01:17:05Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;finalcheck&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Checkbwgsnap.png&amp;diff=1261</id>
		<title>File:Checkbwgsnap.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Checkbwgsnap.png&amp;diff=1261"/>
		<updated>2026-01-28T01:14:52Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;checkbwgsnap&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Lsautosnapshot.png&amp;diff=1260</id>
		<title>File:Lsautosnapshot.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Lsautosnapshot.png&amp;diff=1260"/>
		<updated>2026-01-28T01:05:44Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;lsautosnapshot&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Checkfilehave.png&amp;diff=1259</id>
		<title>File:Checkfilehave.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Checkfilehave.png&amp;diff=1259"/>
		<updated>2026-01-28T00:59:55Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;checkfilehave&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Jqcheck.png&amp;diff=1258</id>
		<title>File:Jqcheck.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Jqcheck.png&amp;diff=1258"/>
		<updated>2026-01-28T00:56:49Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;jqcheck&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1202</id>
		<title>使用搬瓦工api操作nftables实现更新白名单ip</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1202"/>
		<updated>2026-01-26T11:31:19Z</updated>

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

		<summary type="html">&lt;p&gt;EliToviyah：​修改一些错别字 添加一些说明&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取veid和apikey ===&lt;br /&gt;
请参考 [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBOT和bot token和chat_id ===&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;同样需要保存好bot token和chat_id&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;同样需要保存好bot token和chat_id&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;同样需要保存好bot token和chat_id&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== bot token ====&lt;br /&gt;
&lt;br /&gt;
在telegram搜索框搜索 botfather 找到如图的这位&lt;br /&gt;
&lt;br /&gt;
[[File:Botfather.png|border]]&lt;br /&gt;
&lt;br /&gt;
点击打开 如果你没有任何机器人的话 点击&amp;lt;code&amp;gt;create a new bot&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot1.png|border]]&lt;br /&gt;
&lt;br /&gt;
在这两个地方 输入 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Bot Name&amp;lt;/code&amp;gt;  bot的名字用于显示&lt;br /&gt;
&lt;br /&gt;
和&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;username_bot&amp;lt;/code&amp;gt; 搜索bot时用的&lt;br /&gt;
&lt;br /&gt;
其中&amp;lt;code&amp;gt;username_bot&amp;lt;/code&amp;gt; 必须是bot结尾 比如 abot或者a_bot&lt;br /&gt;
&lt;br /&gt;
输入后 显示是绿色的文本提示(你的username_bot名字 is available)就可以按&amp;lt;code&amp;gt;create bot&amp;lt;/code&amp;gt;了&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot2.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot3.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
创建好后按copy 就复制bot token了 &lt;br /&gt;
&lt;br /&gt;
马赛克区域(绿色框)可以点一下 就可以显示bot token (不要给别人看...)&lt;br /&gt;
&lt;br /&gt;
如果你有bot 选择一个你要推送消息的bot 就会跳到上面这个页面 直接点copy就可以复制bot token了&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;chat_id&#039;&#039;&#039; ====&lt;br /&gt;
需要先获取bot token&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot3.png|border]]&lt;br /&gt;
&lt;br /&gt;
点击bot的username_bot名字的位置 就是黄色框框&lt;br /&gt;
&lt;br /&gt;
[[File:Botinfo.png|border]]&lt;br /&gt;
&lt;br /&gt;
弹出界面 选 蓝色框框的图标&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramstart.png|border]]&lt;br /&gt;
&lt;br /&gt;
打开这个页面 选择 &#039;&#039;&#039;开始&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
然后用浏览器打开&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://api.telegram.org/bot替换成你复制的bottoken/getUpdates&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
把 &#039;&#039;&#039;替换成你复制的bottoken&#039;&#039;&#039; 这几个字删掉 然后粘贴copy的token 就可以了&lt;br /&gt;
&lt;br /&gt;
[[File:Webgettelegramchatid.png|border]]&lt;br /&gt;
&lt;br /&gt;
在chat后面跟着的这个id就是你的chat_id了&lt;br /&gt;
&lt;br /&gt;
== 配置检测脚本 ==&lt;br /&gt;
&lt;br /&gt;
=== &#039;&#039;&#039;切换到root账户&#039;&#039;&#039; ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 服务器脚本 ===&lt;br /&gt;
&#039;&#039;&#039;veid和api_key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的veid&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的api_key&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;通知telegram的阈值数组 (百分比)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
NOTIFY_THRESHOLDS=(50 80 90 95)&lt;br /&gt;
&lt;br /&gt;
默认在50 80 90 95的时候通知&lt;br /&gt;
&lt;br /&gt;
每一个数代表一个超过就通知的百分比你可以添加(30 50 80 90 95)&lt;br /&gt;
&lt;br /&gt;
每个中间要有空格&lt;br /&gt;
&lt;br /&gt;
也可以减少 比如(50  95)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Telegram 配置&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的机器人token&amp;quot; &lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的telegram ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
设定telegram静音推送的开始时间和结束时间&lt;br /&gt;
&lt;br /&gt;
(使用 UTC 小时 0-23) 北京时间 19:00 - 08:00 对应 UTC 11:00 - 00:00&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;静音开始时间&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
SILENT_UTC_START=11&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;静音结束时间&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
SILENT_UTC_END=0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果你明白以下内容也可以修改&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;存储目录&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
STORAGE_DIR=&amp;quot;$HOME/.cache/bwg_monitor&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;缓存数据存放的文件&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
DATA_FILE=&amp;quot;$STORAGE_DIR/usage_data.json&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;日志存放文件&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;$STORAGE_DIR/monitor.log&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;日志超过多少就删除&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
LOG_MAX_LINES=500&lt;br /&gt;
&lt;br /&gt;
==== 检查文件是否存在 ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/bwg_monitor.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfile1.png|border]]&lt;br /&gt;
&lt;br /&gt;
如果是红色框框 就是&#039;&#039;&#039;文件已经存在&#039;&#039;&#039;你就换个名字比如 /root/bwg_monitor1.sh 如果改了文件名 下面的这个就都要改成 /root/bwg_monitor1.sh&lt;br /&gt;
&lt;br /&gt;
如果是蓝色框框 就是&#039;&#039;&#039;文件不存在&#039;&#039;&#039; 就不用改&lt;br /&gt;
&lt;br /&gt;
修改后&lt;br /&gt;
&lt;br /&gt;
复制代码块的所有内容 粘贴到ssh里面 按回车就行了&lt;br /&gt;
&lt;br /&gt;
脚本代码块:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/bwg_monitor.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区 =================&lt;br /&gt;
VEID=&amp;quot;你的veid&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的api_key&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 预警阈值数组 (百分比)&lt;br /&gt;
NOTIFY_THRESHOLDS=(50 80 90 95)&lt;br /&gt;
&lt;br /&gt;
# Telegram 配置&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的机器人token&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的telegram ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# (使用 UTC 小时 0-23) 北京时间 19:00 - 08:00 对应 UTC 11:00 - 00:00&lt;br /&gt;
SILENT_UTC_START=11&lt;br /&gt;
SILENT_UTC_END=0&lt;br /&gt;
&lt;br /&gt;
STORAGE_DIR=&amp;quot;$HOME/.cache/bwg_monitor&amp;quot;&lt;br /&gt;
DATA_FILE=&amp;quot;$STORAGE_DIR/usage_data.json&amp;quot;&lt;br /&gt;
LOG_FILE=&amp;quot;$STORAGE_DIR/monitor.log&amp;quot;&lt;br /&gt;
LOG_MAX_LINES=500&lt;br /&gt;
# ==========================================&lt;br /&gt;
&lt;br /&gt;
mkdir -p &amp;quot;$STORAGE_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 增强型 JSON 提取函数 (无需 jq, 适配各种空格格式)&lt;br /&gt;
get_json_num() {&lt;br /&gt;
    echo &amp;quot;$1&amp;quot; | grep -o &amp;quot;\&amp;quot;$2\&amp;quot;:[^,}]*&amp;quot; | sed &#039;s/.*://;s/[ &amp;quot;]*//g&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
log_message() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local timestamp=$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)&lt;br /&gt;
    echo &amp;quot;$timestamp $msg&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
    # 自动清理旧日志&lt;br /&gt;
    if [ $(wc -l &amp;lt; &amp;quot;$LOG_FILE&amp;quot;) -gt &amp;quot;$LOG_MAX_LINES&amp;quot; ]; then&lt;br /&gt;
        sed -i &amp;quot;1,$((LOG_MAX_LINES/2))d&amp;quot; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        echo &amp;quot;$(date &#039;+%Y-%m-%d %H:%M:%S&#039;) [INFO] 日志已自动减半清理。&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
send_tg_msg() {&lt;br /&gt;
    local message=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local is_test_mode=&amp;quot;$2&amp;quot;&lt;br /&gt;
    local silent_param=&amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 获取当前 UTC 小时&lt;br /&gt;
    local current_utc_hour=$(date -u +%H)&lt;br /&gt;
    current_utc_hour=$((10#$current_utc_hour))&lt;br /&gt;
&lt;br /&gt;
    # --- 跨午夜逻辑判断 ---&lt;br /&gt;
    local is_silent=false&lt;br /&gt;
    if [ &amp;quot;$SILENT_UTC_START&amp;quot; -lt &amp;quot;$SILENT_UTC_END&amp;quot; ]; then&lt;br /&gt;
        # 情况 A: 时间段不跨越午夜 (如 10:00-18:00)&lt;br /&gt;
        if [ &amp;quot;$current_utc_hour&amp;quot; -ge &amp;quot;$SILENT_UTC_START&amp;quot; ] &amp;amp;&amp;amp; [ &amp;quot;$current_utc_hour&amp;quot; -lt &amp;quot;$SILENT_UTC_END&amp;quot; ]; then&lt;br /&gt;
            is_silent=true&lt;br /&gt;
        fi&lt;br /&gt;
    else&lt;br /&gt;
        # 情况 B: 时间段跨越午夜 (如 11:00-00:00)&lt;br /&gt;
        if [ &amp;quot;$current_utc_hour&amp;quot; -ge &amp;quot;$SILENT_UTC_START&amp;quot; ] || [ &amp;quot;$current_utc_hour&amp;quot; -lt &amp;quot;$SILENT_UTC_END&amp;quot; ]; then&lt;br /&gt;
            is_silent=true&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    [ &amp;quot;$is_silent&amp;quot; = true ] &amp;amp;&amp;amp; silent_param=&amp;quot;-d disable_notification=true&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    # 执行发送&lt;br /&gt;
    local res=$(curl -s -X POST &amp;quot;https://api.telegram.org/bot$TG_BOT_TOKEN/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=$TG_CHAT_ID&amp;quot; \&lt;br /&gt;
        $silent_param \&lt;br /&gt;
        -d &amp;quot;text=$message&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    if [[ &amp;quot;$res&amp;quot; == *&amp;quot;\&amp;quot;ok\&amp;quot;:true&amp;quot;* ]]; then&lt;br /&gt;
        if [[ &amp;quot;$is_test_mode&amp;quot; == &amp;quot;-t&amp;quot; ]]; then&lt;br /&gt;
            echo &amp;quot;✅ Telegram 发送成功！ (静音模式: $is_silent)&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log_message &amp;quot;[SUCCESS] Telegram 预警已送达 (静音: $is_silent)。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
        return 0&lt;br /&gt;
    else&lt;br /&gt;
        if [[ &amp;quot;$is_test_mode&amp;quot; == &amp;quot;-t&amp;quot; ]]; then&lt;br /&gt;
            echo &amp;quot;❌ Telegram 错误: $res&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log_message &amp;quot;[ERROR] Telegram 失败: $res&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
        return 1&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- 处理测试参数 -t ---&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; == &amp;quot;-t&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;--- 正在进入模拟测试模式 ---&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 模拟输入值&lt;br /&gt;
    MOCK_PLAN=2147483648000&lt;br /&gt;
    MOCK_USED=77207619653&lt;br /&gt;
    MOCK_MULT=1&lt;br /&gt;
    # 模拟重置时间：当前时间 + 1天 (86400秒)&lt;br /&gt;
    MOCK_NEXT_RESET=$(($(date +%s) + 86400))&lt;br /&gt;
    &lt;br /&gt;
    # 计算逻辑&lt;br /&gt;
    M_PLAN_KB=$(( MOCK_PLAN * MOCK_MULT / 1024 ))&lt;br /&gt;
    M_USED_KB=$(( MOCK_USED * MOCK_MULT / 1024 ))&lt;br /&gt;
    M_USAGE_PERCENT=$(( M_USED_KB * 100 / M_PLAN_KB ))&lt;br /&gt;
    &lt;br /&gt;
    M_USED_GB=$(( M_USED_KB / 1024 / 1024 ))&lt;br /&gt;
    M_TOTAL_GB=$(( M_PLAN_KB / 1024 / 1024 ))&lt;br /&gt;
    M_RESET_DATE=$(date -d @&amp;quot;$MOCK_NEXT_RESET&amp;quot; &#039;+%Y-%m-%d %H:%M&#039;)&lt;br /&gt;
&lt;br /&gt;
    TEST_MSG=&amp;quot;🔔 搬瓦工监控模拟测试&lt;br /&gt;
当前进度: $M_USAGE_PERCENT%&lt;br /&gt;
已用流量: ${M_USED_GB} GB&lt;br /&gt;
总量配额: ${M_TOTAL_GB} GB&lt;br /&gt;
下次重置: $M_RESET_DATE&lt;br /&gt;
------------------&lt;br /&gt;
如果收到此消息，说明 Bot 配置与计算逻辑正常。&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    echo &amp;quot;计算结果: 已用 $M_USAGE_PERCENT% ($M_USED_GB/$M_TOTAL_GB GB)&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if send_tg_msg &amp;quot;$TEST_MSG&amp;quot;; then&lt;br /&gt;
        echo &amp;quot;✅ 测试成功！请检查 Telegram。&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        echo &amp;quot;❌ 测试失败！请检查 Token/ID 或网络连接。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
    &lt;br /&gt;
    echo &amp;quot;--- 测试结束 (数据未保存) ---&amp;quot;&lt;br /&gt;
    exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# --- 正式运行逻辑 ---&lt;br /&gt;
RESPONSE=$(curl -s -X POST &amp;quot;https://api.64clouds.com/v1/getServiceInfo&amp;quot; -d &amp;quot;veid=$VEID&amp;quot; -d &amp;quot;api_key=$API_KEY&amp;quot;)&lt;br /&gt;
ERROR_CODE=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;error&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if [[ &amp;quot;$ERROR_CODE&amp;quot; != &amp;quot;0&amp;quot; ]]; then&lt;br /&gt;
    log_message &amp;quot;[ERROR] API 请求失败: $RESPONSE&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# 提取关键数据&lt;br /&gt;
PLAN=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;plan_monthly_data&amp;quot;)&lt;br /&gt;
USED=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;data_counter&amp;quot;)&lt;br /&gt;
MULT=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;monthly_data_multiplier&amp;quot;)&lt;br /&gt;
NEXT_RESET=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;data_next_reset&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# 转换 KB 预防 32 位溢出&lt;br /&gt;
PLAN_KB=$(( PLAN * MULT / 1024 ))&lt;br /&gt;
USED_KB=$(( USED * MULT / 1024 ))&lt;br /&gt;
USAGE_PERCENT=$(( USED_KB * 100 / PLAN_KB ))&lt;br /&gt;
&lt;br /&gt;
# 读取历史数据&lt;br /&gt;
HIST_NOTIFIED=$(get_json_num &amp;quot;$(cat &amp;quot;$DATA_FILE&amp;quot; 2&amp;gt;/dev/null || echo &#039;{&amp;quot;notified_level&amp;quot;:-1}&#039;)&amp;quot; &amp;quot;notified_level&amp;quot;)&lt;br /&gt;
HIST_RESET_TS=$(get_json_num &amp;quot;$(cat &amp;quot;$DATA_FILE&amp;quot; 2&amp;gt;/dev/null || echo &#039;{&amp;quot;last_check_ts&amp;quot;:0}&#039;)&amp;quot; &amp;quot;last_check_ts&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# 跨周期重置&lt;br /&gt;
if [[ &amp;quot;$NEXT_RESET&amp;quot; != &amp;quot;$HIST_RESET_TS&amp;quot; ]]; then&lt;br /&gt;
    HIST_NOTIFIED=-1&lt;br /&gt;
    log_message &amp;quot;[INFO] 进入新计费周期。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
FINAL_NOTIFY_LEVEL=$HIST_NOTIFIED&lt;br /&gt;
for level in &amp;quot;${NOTIFY_THRESHOLDS[@]}&amp;quot;; do&lt;br /&gt;
    if [ &amp;quot;$USAGE_PERCENT&amp;quot; -ge &amp;quot;$level&amp;quot; ] &amp;amp;&amp;amp; [ &amp;quot;$level&amp;quot; -gt &amp;quot;$HIST_NOTIFIED&amp;quot; ]; then&lt;br /&gt;
        USED_GB=$(( USED_KB / 1024 / 1024 ))&lt;br /&gt;
        TOTAL_GB=$(( PLAN_KB / 1024 / 1024 ))&lt;br /&gt;
        RESET_DATE=$(date -d @&amp;quot;$NEXT_RESET&amp;quot; &#039;+%Y-%m-%d %H:%M&#039;)&lt;br /&gt;
        &lt;br /&gt;
        MSG=&amp;quot;🚨 搬瓦工流量预警&lt;br /&gt;
进度: $USAGE_PERCENT%&lt;br /&gt;
已用: ${USED_GB} GB&lt;br /&gt;
总量: ${TOTAL_GB} GB&lt;br /&gt;
重置日期: $RESET_DATE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        if send_tg_msg &amp;quot;$MSG&amp;quot;; then&lt;br /&gt;
            FINAL_NOTIFY_LEVEL=$level&lt;br /&gt;
            log_message &amp;quot;[SUCCESS] 发送 $level% 通知。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
done&lt;br /&gt;
&lt;br /&gt;
# 保存状态&lt;br /&gt;
echo &amp;quot;{\&amp;quot;total_usage_kb\&amp;quot;:$USED_KB,\&amp;quot;last_check_ts\&amp;quot;:$NEXT_RESET,\&amp;quot;notified_level\&amp;quot;:$FINAL_NOTIFY_LEVEL}&amp;quot; &amp;gt; &amp;quot;$DATA_FILE&amp;quot;&lt;br /&gt;
log_message &amp;quot;[INFO] 检查完毕: $USAGE_PERCENT%&amp;quot;&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 授予执行权限 ====&lt;br /&gt;
输入 &amp;lt;code&amp;gt;chmod +x /root/bwg_monitor.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后输入 &amp;lt;code&amp;gt;ls -l /root/bwg_monitor.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果像蓝色框框 有x就说明有执行权限了 如果是红色的-就说明还没执行的权限[[File:Chmod1.png|border]]&lt;br /&gt;
&lt;br /&gt;
==== 测试telegram推送 ====&lt;br /&gt;
输入 &amp;lt;code&amp;gt;/root/bwg_monitor.sh -t&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramsend.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramtest.png|border]]&lt;br /&gt;
&lt;br /&gt;
如下是失败了 你的bot token或 chat_id 不正确&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramsenderror.png|border]]&lt;br /&gt;
&lt;br /&gt;
正常就可以配置开机自动启动了&lt;br /&gt;
&lt;br /&gt;
=== 配置开机自动启动 ===&lt;br /&gt;
&lt;br /&gt;
=== 第一步：创建 Service 文件 ===&lt;br /&gt;
这个文件告诉系统“要运行什么”。&lt;br /&gt;
&lt;br /&gt;
这里你也可以去 &amp;lt;code&amp;gt;ls /etc/systemd/system/bwg-monitor.service&amp;lt;/code&amp;gt;  检查一下这个文件是否存在防止被覆盖... 但一般不会有吧.... 如果存在就 把 &amp;lt;code&amp;gt;/etc/systemd/system/bwg-monitor.service&amp;lt;/code&amp;gt;改成&amp;lt;code&amp;gt;/etc/systemd/system/bwg-monitor1.service&amp;lt;/code&amp;gt;之类的 只改最后一个/ 后面 和 . 之前的 就是 /etc/systemd/system/这里可以修改.service&lt;br /&gt;
&lt;br /&gt;
如果你修改了 下面的也要改 并且要一样才行&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
替换你的实际文件名和路径 &lt;br /&gt;
&lt;br /&gt;
ExecStart=/bin/bash /root/bwg_monitor.sh 如果你上面没改 那就这样就行&lt;br /&gt;
&lt;br /&gt;
bwg_monitor.sh 是你的文件名&lt;br /&gt;
&lt;br /&gt;
/root/ 代表是 / 目录下的 root 目录 下的 bwg_monitor.sh 文件&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /etc/systemd/system/bwg-monitor.service&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=BandwagonHost Traffic Monitor Service&lt;br /&gt;
After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=oneshot&lt;br /&gt;
# 替换为你的实际文件名&lt;br /&gt;
ExecStart=/bin/bash /root/bwg_monitor.sh&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 第二步：创建 Timer 文件 ===&lt;br /&gt;
这个文件告诉系统“什么时候运行”。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/etc/systemd/system/bwg-monitor.timer&amp;lt;/code&amp;gt;同上你可以检查一下&lt;br /&gt;
&lt;br /&gt;
如果你修改了文件名 需要注意的是 两个文件名的名字需要一致 &amp;lt;code&amp;gt;bwg-a.timer&amp;lt;/code&amp;gt;  &amp;lt;code&amp;gt;bwg-a.service&amp;lt;/code&amp;gt;  .timer和.service 之前的那个文件名&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
有几个可以修改的地方&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 开机后 2 分钟开始第一次执行 &lt;br /&gt;
&lt;br /&gt;
OnBootSec=2min &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 之后每隔 1 小时执行一次 &lt;br /&gt;
&lt;br /&gt;
OnUnitActiveSec=1h &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果关机错过了时间，开机后立刻补跑 &lt;br /&gt;
&lt;br /&gt;
Persistent=true&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /etc/systemd/system/bwg-monitor.timer&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Run BandwagonHost Traffic Monitor every hour&lt;br /&gt;
&lt;br /&gt;
[Timer]&lt;br /&gt;
# 开机后 2 分钟开始第一次执行&lt;br /&gt;
OnBootSec=2min&lt;br /&gt;
# 之后每隔 1 小时执行一次&lt;br /&gt;
OnUnitActiveSec=1h&lt;br /&gt;
# 如果关机错过了时间，开机后立刻补跑&lt;br /&gt;
Persistent=true&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=timers.target&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 第三步：启动并激活 ===&lt;br /&gt;
在终端执行以下命令：&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# 重新加载配置&lt;br /&gt;
systemctl daemon-reload&lt;br /&gt;
&lt;br /&gt;
# 启动定时器并设置为开机自启&lt;br /&gt;
systemctl enable --now bwg-monitor.timer&lt;br /&gt;
&lt;br /&gt;
# 查看定时器状态&lt;br /&gt;
systemctl status bwg-monitor.timer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[File:Systemctlstatusbwg.png|border]]&lt;br /&gt;
&lt;br /&gt;
蓝色框框说明是定时器已经正常运行&lt;br /&gt;
&lt;br /&gt;
红色框框是已经开启了 自动开机启动&lt;br /&gt;
&lt;br /&gt;
绿色的框框代表service正在等待 只要不是红色的就行了&lt;br /&gt;
&lt;br /&gt;
=== 缓存和日志的存储位置 ===&lt;br /&gt;
在 默认位置在 &#039;&#039;&#039;$HOME/.cache/bwg_monitor/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
如果你是按照本文来的那$HOME就是/root 完整路径就是 &#039;&#039;&#039;/root/.cache/bwg_monitor/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Store.png|border]]&lt;br /&gt;
&lt;br /&gt;
monitor.log是执行过程记录的日志&lt;br /&gt;
&lt;br /&gt;
usage_data.json是缓存信息&lt;br /&gt;
&lt;br /&gt;
如果需要重复测试的话 需要删除usage_data.json缓存信息 &lt;br /&gt;
&lt;br /&gt;
如果你按照本文的来的话切换了root账户&lt;br /&gt;
&lt;br /&gt;
就输入 &amp;lt;code&amp;gt;rm /root/.cache/bwg_monitor/usage_data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你不是root账户的话  就用这个 &amp;lt;code&amp;gt;rm $HOME/.cache/bwg_monitor/usage_data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果出现错误和非预期情况请检查 &amp;lt;code&amp;gt;cat $HOME/.cache/bwg_monitor/monitor.log&amp;lt;/code&amp;gt;的信息&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E7%9B%91%E6%8E%A7%E7%BD%91%E7%BB%9C%E6%B5%81%E9%87%8F%E7%9A%84%E4%BD%BF%E7%94%A8%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1200</id>
		<title>使用搬瓦工api监控网络流量的使用并telegrambot通知</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E7%9B%91%E6%8E%A7%E7%BD%91%E7%BB%9C%E6%B5%81%E9%87%8F%E7%9A%84%E4%BD%BF%E7%94%A8%E5%B9%B6telegrambot%E9%80%9A%E7%9F%A5&amp;diff=1200"/>
		<updated>2026-01-26T06:50:30Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​添加使用搬瓦工api监控网络流量并推送到telegrambot&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== 使用前 ==&lt;br /&gt;
&lt;br /&gt;
=== 获取veid和apikey ===&lt;br /&gt;
请参考 [[搬瓦工api使用]]&lt;br /&gt;
&lt;br /&gt;
=== 获取telegramBOT和bot token和chat_id ===&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;同样需要保存好bot token和chat_id&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;同样需要保存好bot token和chat_id&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;同样需要保存好bot token和chat_id&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== bot token ====&lt;br /&gt;
&lt;br /&gt;
在telegram搜索框搜索 botfather 找到如图的这位&lt;br /&gt;
&lt;br /&gt;
[[File:Botfather.png|border]]&lt;br /&gt;
&lt;br /&gt;
点击打开 如果你没有任何机器人的话 点击&amp;lt;code&amp;gt;create a new bot&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot1.png|border]]&lt;br /&gt;
&lt;br /&gt;
在这两个地方 输入 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Bot Name&amp;lt;/code&amp;gt;  bot的名字用于显示&lt;br /&gt;
&lt;br /&gt;
和&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;username_bot&amp;lt;/code&amp;gt; 搜索bot时用的&lt;br /&gt;
&lt;br /&gt;
其中&lt;br /&gt;
&lt;br /&gt;
username_bot 比如是bot结尾 比如 abot或者a_bot&lt;br /&gt;
&lt;br /&gt;
输入后 显示时绿色的文本提示(你的username_bot名字 is available)就可以按&amp;lt;code&amp;gt;create bot&amp;lt;/code&amp;gt;了&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot2.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot3.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
创建好后按copy 就复制bot token了 &lt;br /&gt;
&lt;br /&gt;
马赛克区域(绿色框)可以点一下 就可以显示bot token&lt;br /&gt;
&lt;br /&gt;
如果你有bot 选择一个你要推送消息的bot 就会跳到上面这个页面 直接点copy就可以复制bot token了&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;chat_id&#039;&#039;&#039; ====&lt;br /&gt;
需要先获取bot token&lt;br /&gt;
&lt;br /&gt;
[[File:Createtelegrambot3.png|border]]&lt;br /&gt;
&lt;br /&gt;
点击bot的username_bot名字的位置 就是黄色框框&lt;br /&gt;
&lt;br /&gt;
[[File:Botinfo.png|border]]&lt;br /&gt;
&lt;br /&gt;
弹出界面 选 蓝色框框的图标&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramstart.png|border]]&lt;br /&gt;
&lt;br /&gt;
打开这个页面 选择开始&lt;br /&gt;
&lt;br /&gt;
然后用浏览器打开&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://api.telegram.org/bot替换成你复制的bottoken/getUpdates&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
把 &#039;&#039;&#039;替换成你复制的bottoken&#039;&#039;&#039; 这几个字删掉 然后粘贴copy的token 就可以了&lt;br /&gt;
&lt;br /&gt;
[[File:Webgettelegramchatid.png|border]]&lt;br /&gt;
&lt;br /&gt;
在chat后面跟着的这个id就是你的chat_id了&lt;br /&gt;
&lt;br /&gt;
== 配置检测脚本 ==&lt;br /&gt;
&lt;br /&gt;
=== &#039;&#039;&#039;切换到root账户&#039;&#039;&#039; ===&lt;br /&gt;
&lt;br /&gt;
* 输入命令：&amp;lt;code&amp;gt;su -&amp;lt;/code&amp;gt;&#039;&#039;(注意：su 后面有个空格和减号，这很重要，代表同时切换环境变量)&#039;&#039;&lt;br /&gt;
* 输入 Root 密码（输入时看不见）。&lt;br /&gt;
* 此时你的提示符会变成 &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;，代表你又是 Root 了。&lt;br /&gt;
&lt;br /&gt;
[[File:Su-.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 服务器脚本 ===&lt;br /&gt;
&#039;&#039;&#039;veid和api_key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
VEID=&amp;quot;你的veid&amp;quot;&lt;br /&gt;
&lt;br /&gt;
API_KEY=&amp;quot;你的api_key&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;通知telegram的阈值数组 (百分比)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
NOTIFY_THRESHOLDS=(50 80 90 95)&lt;br /&gt;
&lt;br /&gt;
默认在50 80 90 95的时候通知&lt;br /&gt;
&lt;br /&gt;
每一个数代表百分比你可以添加(30 50 80 90 95)&lt;br /&gt;
&lt;br /&gt;
每个中间会有空格&lt;br /&gt;
&lt;br /&gt;
也可以减少(50  95)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Telegram 配置&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的机器人token&amp;quot; &lt;br /&gt;
&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的telegram ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
设定telegram静音推送的开始时间和结束时间&lt;br /&gt;
&lt;br /&gt;
(使用 UTC 小时 0-23) 北京时间 19:00 - 08:00 对应 UTC 11:00 - 00:00&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;静音开始时间&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
SILENT_UTC_START=11&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;静音结束时间&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
SILENT_UTC_END=0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果你明白以下内容也可以修改&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;存储目录&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
STORAGE_DIR=&amp;quot;$HOME/.cache/bwg_monitor&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;缓存数据存放的文件&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
DATA_FILE=&amp;quot;$STORAGE_DIR/usage_data.json&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;日志存放文件&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
LOG_FILE=&amp;quot;$STORAGE_DIR/monitor.log&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;日志超过多少就删除&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
LOG_MAX_LINES=500&lt;br /&gt;
&lt;br /&gt;
==== 检查文件是否存在 ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
输入 &amp;lt;code&amp;gt;ls /root/bwg_monitor.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Checkfile1.png|border]]&lt;br /&gt;
&lt;br /&gt;
如果是红色框框 就是&#039;&#039;&#039;文件已经存在&#039;&#039;&#039; 你就换个名字比如 /root/bwg_monitor1.sh 如果改了文件名 下面的这个就都要改成 /root/bwg_monitor1.sh&lt;br /&gt;
&lt;br /&gt;
如果是蓝色框框 就是&#039;&#039;&#039;文件不存在&#039;&#039;&#039; 就不用改&lt;br /&gt;
&lt;br /&gt;
修改后&lt;br /&gt;
&lt;br /&gt;
复制代码块的所有内容 粘贴到ssh里面 按回车就行了&lt;br /&gt;
&lt;br /&gt;
脚本代码块:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /root/bwg_monitor.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 配置区 =================&lt;br /&gt;
VEID=&amp;quot;你的veid&amp;quot;&lt;br /&gt;
API_KEY=&amp;quot;你的api_key&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 预警阈值数组 (百分比)&lt;br /&gt;
NOTIFY_THRESHOLDS=(50 80 90 95)&lt;br /&gt;
&lt;br /&gt;
# Telegram 配置&lt;br /&gt;
TG_BOT_TOKEN=&amp;quot;你的机器人token&amp;quot;&lt;br /&gt;
TG_CHAT_ID=&amp;quot;你的telegram ID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# (使用 UTC 小时 0-23) 北京时间 19:00 - 08:00 对应 UTC 11:00 - 00:00&lt;br /&gt;
SILENT_UTC_START=11&lt;br /&gt;
SILENT_UTC_END=0&lt;br /&gt;
&lt;br /&gt;
STORAGE_DIR=&amp;quot;$HOME/.cache/bwg_monitor&amp;quot;&lt;br /&gt;
DATA_FILE=&amp;quot;$STORAGE_DIR/usage_data.json&amp;quot;&lt;br /&gt;
LOG_FILE=&amp;quot;$STORAGE_DIR/monitor.log&amp;quot;&lt;br /&gt;
LOG_MAX_LINES=500&lt;br /&gt;
# ==========================================&lt;br /&gt;
&lt;br /&gt;
mkdir -p &amp;quot;$STORAGE_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 增强型 JSON 提取函数 (无需 jq, 适配各种空格格式)&lt;br /&gt;
get_json_num() {&lt;br /&gt;
    echo &amp;quot;$1&amp;quot; | grep -o &amp;quot;\&amp;quot;$2\&amp;quot;:[^,}]*&amp;quot; | sed &#039;s/.*://;s/[ &amp;quot;]*//g&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
log_message() {&lt;br /&gt;
    local msg=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local timestamp=$(date &#039;+%Y-%m-%d %H:%M:%S&#039;)&lt;br /&gt;
    echo &amp;quot;$timestamp $msg&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
    # 自动清理旧日志&lt;br /&gt;
    if [ $(wc -l &amp;lt; &amp;quot;$LOG_FILE&amp;quot;) -gt &amp;quot;$LOG_MAX_LINES&amp;quot; ]; then&lt;br /&gt;
        sed -i &amp;quot;1,$((LOG_MAX_LINES/2))d&amp;quot; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
        echo &amp;quot;$(date &#039;+%Y-%m-%d %H:%M:%S&#039;) [INFO] 日志已自动减半清理。&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
send_tg_msg() {&lt;br /&gt;
    local message=&amp;quot;$1&amp;quot;&lt;br /&gt;
    local is_test_mode=&amp;quot;$2&amp;quot;&lt;br /&gt;
    local silent_param=&amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 获取当前 UTC 小时&lt;br /&gt;
    local current_utc_hour=$(date -u +%H)&lt;br /&gt;
    current_utc_hour=$((10#$current_utc_hour))&lt;br /&gt;
&lt;br /&gt;
    # --- 跨午夜逻辑判断 ---&lt;br /&gt;
    local is_silent=false&lt;br /&gt;
    if [ &amp;quot;$SILENT_UTC_START&amp;quot; -lt &amp;quot;$SILENT_UTC_END&amp;quot; ]; then&lt;br /&gt;
        # 情况 A: 时间段不跨越午夜 (如 10:00-18:00)&lt;br /&gt;
        if [ &amp;quot;$current_utc_hour&amp;quot; -ge &amp;quot;$SILENT_UTC_START&amp;quot; ] &amp;amp;&amp;amp; [ &amp;quot;$current_utc_hour&amp;quot; -lt &amp;quot;$SILENT_UTC_END&amp;quot; ]; then&lt;br /&gt;
            is_silent=true&lt;br /&gt;
        fi&lt;br /&gt;
    else&lt;br /&gt;
        # 情况 B: 时间段跨越午夜 (如 11:00-00:00)&lt;br /&gt;
        if [ &amp;quot;$current_utc_hour&amp;quot; -ge &amp;quot;$SILENT_UTC_START&amp;quot; ] || [ &amp;quot;$current_utc_hour&amp;quot; -lt &amp;quot;$SILENT_UTC_END&amp;quot; ]; then&lt;br /&gt;
            is_silent=true&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    [ &amp;quot;$is_silent&amp;quot; = true ] &amp;amp;&amp;amp; silent_param=&amp;quot;-d disable_notification=true&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    # 执行发送&lt;br /&gt;
    local res=$(curl -s -X POST &amp;quot;https://api.telegram.org/bot$TG_BOT_TOKEN/sendMessage&amp;quot; \&lt;br /&gt;
        -d &amp;quot;chat_id=$TG_CHAT_ID&amp;quot; \&lt;br /&gt;
        $silent_param \&lt;br /&gt;
        -d &amp;quot;text=$message&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    if [[ &amp;quot;$res&amp;quot; == *&amp;quot;\&amp;quot;ok\&amp;quot;:true&amp;quot;* ]]; then&lt;br /&gt;
        if [[ &amp;quot;$is_test_mode&amp;quot; == &amp;quot;-t&amp;quot; ]]; then&lt;br /&gt;
            echo &amp;quot;✅ Telegram 发送成功！ (静音模式: $is_silent)&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log_message &amp;quot;[SUCCESS] Telegram 预警已送达 (静音: $is_silent)。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
        return 0&lt;br /&gt;
    else&lt;br /&gt;
        if [[ &amp;quot;$is_test_mode&amp;quot; == &amp;quot;-t&amp;quot; ]]; then&lt;br /&gt;
            echo &amp;quot;❌ Telegram 错误: $res&amp;quot;&lt;br /&gt;
        else&lt;br /&gt;
            log_message &amp;quot;[ERROR] Telegram 失败: $res&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
        return 1&lt;br /&gt;
    fi&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# --- 处理测试参数 -t ---&lt;br /&gt;
if [[ &amp;quot;$1&amp;quot; == &amp;quot;-t&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;--- 正在进入模拟测试模式 ---&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    # 模拟输入值&lt;br /&gt;
    MOCK_PLAN=2147483648000&lt;br /&gt;
    MOCK_USED=77207619653&lt;br /&gt;
    MOCK_MULT=1&lt;br /&gt;
    # 模拟重置时间：当前时间 + 1天 (86400秒)&lt;br /&gt;
    MOCK_NEXT_RESET=$(($(date +%s) + 86400))&lt;br /&gt;
    &lt;br /&gt;
    # 计算逻辑&lt;br /&gt;
    M_PLAN_KB=$(( MOCK_PLAN * MOCK_MULT / 1024 ))&lt;br /&gt;
    M_USED_KB=$(( MOCK_USED * MOCK_MULT / 1024 ))&lt;br /&gt;
    M_USAGE_PERCENT=$(( M_USED_KB * 100 / M_PLAN_KB ))&lt;br /&gt;
    &lt;br /&gt;
    M_USED_GB=$(( M_USED_KB / 1024 / 1024 ))&lt;br /&gt;
    M_TOTAL_GB=$(( M_PLAN_KB / 1024 / 1024 ))&lt;br /&gt;
    M_RESET_DATE=$(date -d @&amp;quot;$MOCK_NEXT_RESET&amp;quot; &#039;+%Y-%m-%d %H:%M&#039;)&lt;br /&gt;
&lt;br /&gt;
    TEST_MSG=&amp;quot;🔔 搬瓦工监控模拟测试&lt;br /&gt;
当前进度: $M_USAGE_PERCENT%&lt;br /&gt;
已用流量: ${M_USED_GB} GB&lt;br /&gt;
总量配额: ${M_TOTAL_GB} GB&lt;br /&gt;
下次重置: $M_RESET_DATE&lt;br /&gt;
------------------&lt;br /&gt;
如果收到此消息，说明 Bot 配置与计算逻辑正常。&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    echo &amp;quot;计算结果: 已用 $M_USAGE_PERCENT% ($M_USED_GB/$M_TOTAL_GB GB)&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if send_tg_msg &amp;quot;$TEST_MSG&amp;quot;; then&lt;br /&gt;
        echo &amp;quot;✅ 测试成功！请检查 Telegram。&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        echo &amp;quot;❌ 测试失败！请检查 Token/ID 或网络连接。&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
    &lt;br /&gt;
    echo &amp;quot;--- 测试结束 (数据未保存) ---&amp;quot;&lt;br /&gt;
    exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# --- 正式运行逻辑 ---&lt;br /&gt;
RESPONSE=$(curl -s -X POST &amp;quot;https://api.64clouds.com/v1/getServiceInfo&amp;quot; -d &amp;quot;veid=$VEID&amp;quot; -d &amp;quot;api_key=$API_KEY&amp;quot;)&lt;br /&gt;
ERROR_CODE=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;error&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if [[ &amp;quot;$ERROR_CODE&amp;quot; != &amp;quot;0&amp;quot; ]]; then&lt;br /&gt;
    log_message &amp;quot;[ERROR] API 请求失败: $RESPONSE&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
# 提取关键数据&lt;br /&gt;
PLAN=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;plan_monthly_data&amp;quot;)&lt;br /&gt;
USED=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;data_counter&amp;quot;)&lt;br /&gt;
MULT=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;monthly_data_multiplier&amp;quot;)&lt;br /&gt;
NEXT_RESET=$(get_json_num &amp;quot;$RESPONSE&amp;quot; &amp;quot;data_next_reset&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# 转换 KB 预防 32 位溢出&lt;br /&gt;
PLAN_KB=$(( PLAN * MULT / 1024 ))&lt;br /&gt;
USED_KB=$(( USED * MULT / 1024 ))&lt;br /&gt;
USAGE_PERCENT=$(( USED_KB * 100 / PLAN_KB ))&lt;br /&gt;
&lt;br /&gt;
# 读取历史数据&lt;br /&gt;
HIST_NOTIFIED=$(get_json_num &amp;quot;$(cat &amp;quot;$DATA_FILE&amp;quot; 2&amp;gt;/dev/null || echo &#039;{&amp;quot;notified_level&amp;quot;:-1}&#039;)&amp;quot; &amp;quot;notified_level&amp;quot;)&lt;br /&gt;
HIST_RESET_TS=$(get_json_num &amp;quot;$(cat &amp;quot;$DATA_FILE&amp;quot; 2&amp;gt;/dev/null || echo &#039;{&amp;quot;last_check_ts&amp;quot;:0}&#039;)&amp;quot; &amp;quot;last_check_ts&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# 跨周期重置&lt;br /&gt;
if [[ &amp;quot;$NEXT_RESET&amp;quot; != &amp;quot;$HIST_RESET_TS&amp;quot; ]]; then&lt;br /&gt;
    HIST_NOTIFIED=-1&lt;br /&gt;
    log_message &amp;quot;[INFO] 进入新计费周期。&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
FINAL_NOTIFY_LEVEL=$HIST_NOTIFIED&lt;br /&gt;
for level in &amp;quot;${NOTIFY_THRESHOLDS[@]}&amp;quot;; do&lt;br /&gt;
    if [ &amp;quot;$USAGE_PERCENT&amp;quot; -ge &amp;quot;$level&amp;quot; ] &amp;amp;&amp;amp; [ &amp;quot;$level&amp;quot; -gt &amp;quot;$HIST_NOTIFIED&amp;quot; ]; then&lt;br /&gt;
        USED_GB=$(( USED_KB / 1024 / 1024 ))&lt;br /&gt;
        TOTAL_GB=$(( PLAN_KB / 1024 / 1024 ))&lt;br /&gt;
        RESET_DATE=$(date -d @&amp;quot;$NEXT_RESET&amp;quot; &#039;+%Y-%m-%d %H:%M&#039;)&lt;br /&gt;
        &lt;br /&gt;
        MSG=&amp;quot;🚨 搬瓦工流量预警&lt;br /&gt;
进度: $USAGE_PERCENT%&lt;br /&gt;
已用: ${USED_GB} GB&lt;br /&gt;
总量: ${TOTAL_GB} GB&lt;br /&gt;
重置日期: $RESET_DATE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        if send_tg_msg &amp;quot;$MSG&amp;quot;; then&lt;br /&gt;
            FINAL_NOTIFY_LEVEL=$level&lt;br /&gt;
            log_message &amp;quot;[SUCCESS] 发送 $level% 通知。&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
    fi&lt;br /&gt;
done&lt;br /&gt;
&lt;br /&gt;
# 保存状态&lt;br /&gt;
echo &amp;quot;{\&amp;quot;total_usage_kb\&amp;quot;:$USED_KB,\&amp;quot;last_check_ts\&amp;quot;:$NEXT_RESET,\&amp;quot;notified_level\&amp;quot;:$FINAL_NOTIFY_LEVEL}&amp;quot; &amp;gt; &amp;quot;$DATA_FILE&amp;quot;&lt;br /&gt;
log_message &amp;quot;[INFO] 检查完毕: $USAGE_PERCENT%&amp;quot;&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 授予执行权限 ====&lt;br /&gt;
输入 &amp;lt;code&amp;gt;chmod +x /root/bwg_monitor.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
然后输入 &amp;lt;code&amp;gt;ls -l /root/bwg_monitor.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果像蓝色框框 有x就说明有执行权限了 如果是红色的-就说明还没执行的权限[[File:Chmod1.png|border]]&lt;br /&gt;
&lt;br /&gt;
==== 测试telegram推送 ====&lt;br /&gt;
输入 &amp;lt;code&amp;gt;/root/bwg_monitor.sh -t&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramsend.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramtest.png|border]]&lt;br /&gt;
&lt;br /&gt;
如下是失败了 你的bot token或 chat_id 不正确&lt;br /&gt;
&lt;br /&gt;
[[File:Telegramsenderror.png|border]]&lt;br /&gt;
&lt;br /&gt;
正常就可以配置开机自动启动了&lt;br /&gt;
&lt;br /&gt;
=== 配置开机自动启动 ===&lt;br /&gt;
&lt;br /&gt;
=== 第一步：创建 Service 文件 ===&lt;br /&gt;
这个文件告诉系统“要运行什么”。&lt;br /&gt;
&lt;br /&gt;
这里你也可以去 &amp;lt;code&amp;gt;ls /etc/systemd/system/bwg-monitor.service&amp;lt;/code&amp;gt;  检查一下这个文件是否存在防止被覆盖... 但一般不会有吧.... 如果存在就 把 &amp;lt;code&amp;gt;/etc/systemd/system/bwg-monitor.service&amp;lt;/code&amp;gt;改成&amp;lt;code&amp;gt;/etc/systemd/system/bwg-monitor1.service&amp;lt;/code&amp;gt;之类的 只改最后一个/ 后面 和 . 之前的 就是 /etc/systemd/system/这里可以修改.service&lt;br /&gt;
&lt;br /&gt;
如果你修改了 下面的也要改 并且要一样才行&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
这里 替换为你的实际文件名 &lt;br /&gt;
&lt;br /&gt;
ExecStart=/bin/bash /root/bwg_monitor.sh 如果你上面没改 那就这样就行&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /etc/systemd/system/bwg-monitor.service&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=BandwagonHost Traffic Monitor Service&lt;br /&gt;
After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=oneshot&lt;br /&gt;
# 替换为你的实际文件名&lt;br /&gt;
ExecStart=/bin/bash /root/bwg_monitor.sh&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 第二步：创建 Timer 文件 ===&lt;br /&gt;
这个文件告诉系统“什么时候运行”。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;文件路径&#039;&#039;&#039;：&amp;lt;code&amp;gt;/etc/systemd/system/bwg-monitor.timer&amp;lt;/code&amp;gt;同上你可以检查一下&lt;br /&gt;
&lt;br /&gt;
如果你修改了文件名 需要注意的是 两个文件名的名字需要一致 &amp;lt;code&amp;gt;bwg-a.timer&amp;lt;/code&amp;gt;  &amp;lt;code&amp;gt;bwg-a.service&amp;lt;/code&amp;gt;  .timer和.service 之前的那个文件名&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
有几个可以修改的地方&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 开机后 2 分钟开始第一次执行 &lt;br /&gt;
&lt;br /&gt;
OnBootSec=2min &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 之后每隔 1 小时执行一次 &lt;br /&gt;
&lt;br /&gt;
OnUnitActiveSec=1h &lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 如果关机错过了时间，开机后立刻补跑 &lt;br /&gt;
&lt;br /&gt;
Persistent=true&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt; &#039;EOF&#039; &amp;gt; /etc/systemd/system/bwg-monitor.timer&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Run BandwagonHost Traffic Monitor every hour&lt;br /&gt;
&lt;br /&gt;
[Timer]&lt;br /&gt;
# 开机后 2 分钟开始第一次执行&lt;br /&gt;
OnBootSec=2min&lt;br /&gt;
# 之后每隔 1 小时执行一次&lt;br /&gt;
OnUnitActiveSec=1h&lt;br /&gt;
# 如果关机错过了时间，开机后立刻补跑&lt;br /&gt;
Persistent=true&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=timers.target&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 第三步：启动并激活 ===&lt;br /&gt;
在终端执行以下命令：&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# 重新加载配置&lt;br /&gt;
systemctl daemon-reload&lt;br /&gt;
&lt;br /&gt;
# 启动定时器并设置为开机自启&lt;br /&gt;
systemctl enable --now bwg-monitor.timer&lt;br /&gt;
&lt;br /&gt;
# 查看定时器状态&lt;br /&gt;
systemctl status bwg-monitor.timer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;[[File:Systemctlstatusbwg.png|border]]&lt;br /&gt;
&lt;br /&gt;
蓝色框框说明是定时器已经正常运行&lt;br /&gt;
&lt;br /&gt;
红色框框是已经开启了 自动开机启动&lt;br /&gt;
&lt;br /&gt;
绿色的框框代表service正在等待 只要不是红色的就行了&lt;br /&gt;
&lt;br /&gt;
=== 缓存和日志的存储位置 ===&lt;br /&gt;
在 默认位置在 &#039;&#039;&#039;$HOME/.cache/bwg_monitor/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
如果你是按照本文来的那$HOME就是/root 完整路径就是 &#039;&#039;&#039;/root/.cache/bwg_monitor/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Store.png|border]]&lt;br /&gt;
&lt;br /&gt;
monitor.log是执行日志&lt;br /&gt;
&lt;br /&gt;
usage_data.json是缓存信息&lt;br /&gt;
&lt;br /&gt;
如果需要重复测试的话 需要删除usage_data.json缓存信息 &lt;br /&gt;
&lt;br /&gt;
如果你按照本文的来的话切换了root账户&lt;br /&gt;
&lt;br /&gt;
就输入 &amp;lt;code&amp;gt;rm /root/.cache/bwg_monitor/usage_data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果你不是root账户的话  就用这个 &amp;lt;code&amp;gt;rm $HOME/.cache/bwg_monitor/usage_data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果出现错误请检查 &amp;lt;code&amp;gt;cat $HOME/.cache/bwg_monitor/monitor.log&amp;lt;/code&amp;gt;的信息&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Store.png&amp;diff=1199</id>
		<title>File:Store.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Store.png&amp;diff=1199"/>
		<updated>2026-01-26T06:39:31Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;store&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Telegramtest.png&amp;diff=1198</id>
		<title>File:Telegramtest.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Telegramtest.png&amp;diff=1198"/>
		<updated>2026-01-26T06:38:08Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;telegramtest&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Systemctlstatusbwg.png&amp;diff=1197</id>
		<title>File:Systemctlstatusbwg.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Systemctlstatusbwg.png&amp;diff=1197"/>
		<updated>2026-01-26T06:34:46Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;systemctlstatusbwg&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Telegramsenderror.png&amp;diff=1196</id>
		<title>File:Telegramsenderror.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Telegramsenderror.png&amp;diff=1196"/>
		<updated>2026-01-26T06:18:39Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;telegramsenderror&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Telegramsend.png&amp;diff=1195</id>
		<title>File:Telegramsend.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Telegramsend.png&amp;diff=1195"/>
		<updated>2026-01-26T06:17:49Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;telegramsend&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Chmod1.png&amp;diff=1194</id>
		<title>File:Chmod1.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Chmod1.png&amp;diff=1194"/>
		<updated>2026-01-26T06:12:11Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;chmod1&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Checkfile1.png&amp;diff=1193</id>
		<title>File:Checkfile1.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Checkfile1.png&amp;diff=1193"/>
		<updated>2026-01-26T06:08:31Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;checkfile1&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1192</id>
		<title>使用搬瓦工api操作nftables实现更新白名单ip</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1192"/>
		<updated>2026-01-26T06:01:28Z</updated>

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

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

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;webgettelegramchatid&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Telegramstart.png&amp;diff=1189</id>
		<title>File:Telegramstart.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Telegramstart.png&amp;diff=1189"/>
		<updated>2026-01-26T05:53:24Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;telegramstart&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Botinfo.png&amp;diff=1188</id>
		<title>File:Botinfo.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Botinfo.png&amp;diff=1188"/>
		<updated>2026-01-26T05:52:46Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;botinfo&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Createtelegrambot3.png&amp;diff=1187</id>
		<title>File:Createtelegrambot3.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Createtelegrambot3.png&amp;diff=1187"/>
		<updated>2026-01-26T05:48:38Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;createtelegrambot3&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Createtelegrambot2.png&amp;diff=1186</id>
		<title>File:Createtelegrambot2.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Createtelegrambot2.png&amp;diff=1186"/>
		<updated>2026-01-26T05:46:19Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;createtelegrambot2&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Createtelegrambot1.png&amp;diff=1185</id>
		<title>File:Createtelegrambot1.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Createtelegrambot1.png&amp;diff=1185"/>
		<updated>2026-01-26T05:44:19Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;createtelegrambot1&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Createtelegrambot.png&amp;diff=1184</id>
		<title>File:Createtelegrambot.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Createtelegrambot.png&amp;diff=1184"/>
		<updated>2026-01-26T05:43:08Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;createtelegrambot&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Botfather.png&amp;diff=1183</id>
		<title>File:Botfather.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Botfather.png&amp;diff=1183"/>
		<updated>2026-01-26T05:42:23Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;botfather&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1182</id>
		<title>使用搬瓦工api操作nftables实现更新白名单ip</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1182"/>
		<updated>2026-01-26T02:37:50Z</updated>

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

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

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;catcheckfilehave&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Catcheckhave.png&amp;diff=1179</id>
		<title>File:Catcheckhave.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Catcheckhave.png&amp;diff=1179"/>
		<updated>2026-01-26T02:32:21Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;catcheckhave&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E6%90%AC%E7%93%A6%E5%B7%A5api%E4%BD%BF%E7%94%A8&amp;diff=1178</id>
		<title>搬瓦工api使用</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E6%90%AC%E7%93%A6%E5%B7%A5api%E4%BD%BF%E7%94%A8&amp;diff=1178"/>
		<updated>2026-01-25T23:42:03Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​优化排版&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== 什么是API ==&lt;br /&gt;
API 的全称是 Application Programming Interface（应用程序编程接口）。通俗地讲就是可以通过直接访问网站地址(url)执行一些功能&lt;br /&gt;
&lt;br /&gt;
== API能干嘛 ==&lt;br /&gt;
可以在不使用kiwiVM面板里面的按钮或界面执行脚本,发送http请求完成自动开机、关机、查询服务器状态、执行指令等等的目的，这些都是官方提供的功能，适用于自动化程序控制服务器。&lt;br /&gt;
&lt;br /&gt;
== 如何使用API ==&lt;br /&gt;
使用浏览器或curl或编程语言发送http请求&lt;br /&gt;
&lt;br /&gt;
=== 使用浏览器直接在地址栏输入完整的url 虽然能但一般不使用这种方式 ===&lt;br /&gt;
[[File:Chromehttp.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 使用curl wget等命令行工具发送http请求 ===&lt;br /&gt;
[[File:Curlhttp.png|border]]&lt;br /&gt;
&lt;br /&gt;
=== 使用php python golang等编程语言发送http请求 ===&lt;br /&gt;
[[File:Golanghttp.png|border]]&lt;br /&gt;
&lt;br /&gt;
== 使用前准备 ==&lt;br /&gt;
&lt;br /&gt;
=== &#039;&#039;&#039;获取api key和 服务器id(veid)&#039;&#039;&#039; ===&lt;br /&gt;
&lt;br /&gt;
==== 打开kiwivm面板 登陆后打开https://bandwagonhost.com/services 打开你要用api控制的服务器 ====&lt;br /&gt;
[[File:Openkiwivm1.png|border]]&lt;br /&gt;
[[File:Kiwivm2.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[File:Kiwivm3.png|border]][[File:Kiwivm4.png|border]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;再次提醒:请务必保管好API Key 不要泄露给任何人 防止服务器和API被盗用 如果发生泄漏请使用Reset API Key重置也应当保护调用API所在的设备&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;再次提醒:请务必保管好API Key 不要泄露给任何人 防止服务器和API被盗用 如果发生泄漏请使用Reset API Key重置也应当保护调用API所在的设备&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;再次提醒:请务必保管好API Key 不要泄露给任何人 防止服务器和API被盗用 如果发生泄漏请使用Reset API Key重置也应当保护调用API所在的设备&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;再次提醒:请务必保管好API Key 不要泄露给任何人 防止服务器和API被盗用 如果发生泄漏请使用Reset API Key重置也应当保护调用API所在的设备&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== API的使用 ==&lt;br /&gt;
基础的url是:&#039;&#039;&#039;&amp;lt;nowiki&amp;gt;https://api.64clouds.com/v1/&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
需要携带 &#039;&#039;&#039;veid&#039;&#039;&#039; 和 &#039;&#039;&#039;api_key&#039;&#039;&#039; 参数&lt;br /&gt;
&lt;br /&gt;
get与post请求方法都可以 但get请求有长度限制.如果执行一些长的命令 最终还是需要post请求方法 post也更加安全&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;nowiki&amp;gt;https://api.64clouds.com/v1/api的请求路径?veid=YOUR_VEID&amp;amp;api_key=YOUR_API_KEY&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;使用时需要把文字部分替换为指定的内容&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
3处要替换的地方&lt;br /&gt;
&lt;br /&gt;
1. api的请求路径  也就是你需要执行的功能&lt;br /&gt;
&lt;br /&gt;
2. YOUR_VEID  你的服务器id&lt;br /&gt;
&lt;br /&gt;
3. YOUR_API_KEY 你的API_Key&lt;br /&gt;
&lt;br /&gt;
=== 以获取服务器信息为例 ===&lt;br /&gt;
例如你的 veid=33333   api_key=privateewww324242&lt;br /&gt;
&lt;br /&gt;
需要调用 &#039;&#039;&#039;获取服务器信息&#039;&#039;&#039; 的功能 那么 请求路径 &#039;&#039;&#039;getServiceInfo&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;https://api.64clouds.com/v1/getServiceInfo?veid=33333&amp;amp;api_key=privateewww324242&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;使用时请替换为自己实际的内容 使用你选择的工具发送http请求&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
顺利的话会返回很多数据 关键信息已经隐藏…&lt;br /&gt;
&lt;br /&gt;
成功的返回值 不同请求路径 返回的内容不一样&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;vm_type&amp;quot;: &amp;quot;kvm&amp;quot;,&lt;br /&gt;
    &amp;quot;hostname&amp;quot;: &amp;quot;xxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;node_alias&amp;quot;: &amp;quot;xxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;node_location_id&amp;quot;: &amp;quot;xxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;node_location&amp;quot;: &amp;quot;xxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;node_datacenter&amp;quot;: &amp;quot;xxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;location_ipv6_ready&amp;quot;: true,&lt;br /&gt;
    &amp;quot;plan&amp;quot;: &amp;quot;xxxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;plan_monthly_data&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;monthly_data_multiplier&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;plan_disk&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;plan_ram&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;plan_swap&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;plan_max_ipv6s&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;os&amp;quot;: &amp;quot;xxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;email&amp;quot;: &amp;quot;xxxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;data_counter&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;data_next_reset&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;ip_addresses&amp;quot;: [&lt;br /&gt;
        &amp;quot;xxx.xxx.xxx.xx&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    等一些信息 在此省略&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;失败的返回值 这个示范是 身份验证错误 就是api_key或者veid不正确&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&amp;quot;error&amp;quot;: 700005,&lt;br /&gt;
&amp;quot;message&amp;quot;: &amp;quot;Authentication failure&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 其他示例 ===&lt;br /&gt;
&lt;br /&gt;
==== curl 调用：开启服务器 (start) ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#GET 方法&lt;br /&gt;
curl &amp;quot;https://api.64clouds.com/v1/start?veid=YOUR_VEID&amp;amp;api_key=YOUR_API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#POST方法&lt;br /&gt;
curl -X POST &amp;quot;https://api.64clouds.com/v1/start&amp;quot; \&lt;br /&gt;
     -d &amp;quot;veid=YOUR_VEID&amp;quot; \&lt;br /&gt;
     -d &amp;quot;api_key=YOUR_API_KEY&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== wget 调用：关闭服务器 (stop) ====&lt;br /&gt;
wget默认会下载文件，加上-qO-可以让它像 curl 一样直接输出结果到屏幕，而不保存文件。&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#GET方法&lt;br /&gt;
wget -qO- &amp;quot;https://api.64clouds.com/v1/stop?veid=YOUR_VEID&amp;amp;api_key=YOUR_API_KEY&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#POST方法&lt;br /&gt;
wget -qO- --post-data=&amp;quot;veid=YOUR_VEID&amp;amp;api_key=YOUR_API_KEY&amp;quot; &amp;quot;https://api.64clouds.com/v1/stop&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Python调用:执行命令在服务器的/tmp目录下创建一个文件(basicShell/exec) ====&lt;br /&gt;
Python 的 &#039;&#039;&#039;requests&#039;&#039;&#039; 库会自动处理 URL 编码，非常方便。&lt;br /&gt;
&lt;br /&gt;
如果没安装的话&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;pip install requests&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
#GET&lt;br /&gt;
import requests&lt;br /&gt;
import json&lt;br /&gt;
&lt;br /&gt;
# 配置信息&lt;br /&gt;
url = &amp;quot;https://api.64clouds.com/v1/basicShell/exec&amp;quot;&lt;br /&gt;
params = {&lt;br /&gt;
    &amp;quot;veid&amp;quot;: &amp;quot;YOUR_VEID&amp;quot;,&lt;br /&gt;
    &amp;quot;api_key&amp;quot;: &amp;quot;YOUR_API_KEY&amp;quot;,&lt;br /&gt;
    &amp;quot;command&amp;quot;: &amp;quot;touch /tmp/python_test.txt&amp;quot;  # 创建一个文件&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 发送请求&lt;br /&gt;
try:&lt;br /&gt;
    response = requests.get(url, params=params)&lt;br /&gt;
    response.raise_for_status() # 检查 HTTP 错误&lt;br /&gt;
    &lt;br /&gt;
    result = response.json()&lt;br /&gt;
    print(f&amp;quot;执行状态码: {result.get(&#039;error&#039;)}&amp;quot;)&lt;br /&gt;
    print(f&amp;quot;返回消息: {result.get(&#039;message&#039;)}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
except Exception as e:&lt;br /&gt;
    print(f&amp;quot;请求出错: {e}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
#POST&lt;br /&gt;
import requests&lt;br /&gt;
import json&lt;br /&gt;
&lt;br /&gt;
# 配置信息&lt;br /&gt;
url = &amp;quot;https://api.64clouds.com/v1/basicShell/exec&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# 这里的字典数据将作为 POST 的 Body 发送&lt;br /&gt;
payload = {&lt;br /&gt;
    &amp;quot;veid&amp;quot;: &amp;quot;YOUR_VEID&amp;quot;,&lt;br /&gt;
    &amp;quot;api_key&amp;quot;: &amp;quot;YOUR_API_KEY&amp;quot;,&lt;br /&gt;
    &amp;quot;command&amp;quot;: &amp;quot;touch /tmp/python_test_post.txt&amp;quot;  # 我改了下文件名，方便你区分&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 发送请求&lt;br /&gt;
try:&lt;br /&gt;
    response = requests.post(url, data=payload)&lt;br /&gt;
    &lt;br /&gt;
    response.raise_for_status() # 检查 HTTP 错误&lt;br /&gt;
    &lt;br /&gt;
    result = response.json()&lt;br /&gt;
    print(f&amp;quot;执行状态码: {result.get(&#039;error&#039;)}&amp;quot;)&lt;br /&gt;
    print(f&amp;quot;返回消息: {result.get(&#039;message&#039;)}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
except Exception as e:&lt;br /&gt;
    print(f&amp;quot;请求出错: {e}&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PHP 示例:执行命令在服务器的/tmp目录下创建一个文件(basicShell/exec) ====&lt;br /&gt;
PHP 自带的&#039;&#039;&#039;http_build_query&#039;&#039;&#039;函数可以完美处理 URL 编码。&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
#GET&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$url = &amp;quot;https://api.64clouds.com/v1/basicShell/exec&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$data = [&lt;br /&gt;
    &#039;veid&#039;    =&amp;gt; &#039;YOUR_VEID&#039;,&lt;br /&gt;
    &#039;api_key&#039; =&amp;gt; &#039;YOUR_API_KEY&#039;,&lt;br /&gt;
    &#039;command&#039; =&amp;gt; &#039;touch /tmp/php_test.txt&#039; // 创建一个文件&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
// 构建查询字符串 (会自动进行 URL 编码)&lt;br /&gt;
$queryString = http_build_query($data);&lt;br /&gt;
$requestUrl = $url . &#039;?&#039; . $queryString;&lt;br /&gt;
&lt;br /&gt;
// 发送请求&lt;br /&gt;
$response = file_get_contents($requestUrl);&lt;br /&gt;
&lt;br /&gt;
if ($response !== false) {&lt;br /&gt;
    $json = json_decode($response, true);&lt;br /&gt;
    echo &amp;quot;Error Code: &amp;quot; . $json[&#039;error&#039;] . &amp;quot;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;Message: &amp;quot; . $json[&#039;message&#039;] . &amp;quot;\n&amp;quot;;&lt;br /&gt;
} else {&lt;br /&gt;
    echo &amp;quot;请求失败\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#POST&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$url = &amp;quot;https://api.64clouds.com/v1/basicShell/exec&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// 你的数据 (保持不变)&lt;br /&gt;
$data = [&lt;br /&gt;
    &#039;veid&#039;    =&amp;gt; &#039;YOUR_VEID&#039;,&lt;br /&gt;
    &#039;api_key&#039; =&amp;gt; &#039;YOUR_API_KEY&#039;,&lt;br /&gt;
    &#039;command&#039; =&amp;gt; &#039;touch /tmp/php_post_test.txt&#039; // 改了个文件名方便区分&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
// 1. 依然需要用 http_build_query 处理数据，&lt;br /&gt;
// 但这次不是拼在 URL 后面，而是放在 Body 里&lt;br /&gt;
$content = http_build_query($data);&lt;br /&gt;
&lt;br /&gt;
// 2. 创建 HTTP 头部选项&lt;br /&gt;
$options = [&lt;br /&gt;
    &#039;http&#039; =&amp;gt; [&lt;br /&gt;
        &#039;method&#039;  =&amp;gt; &#039;POST&#039;, // 显式指定 POST&lt;br /&gt;
        &#039;header&#039;  =&amp;gt; &#039;Content-type: application/x-www-form-urlencoded&#039;, // 必须告诉服务器这是表单数据&lt;br /&gt;
        &#039;content&#039; =&amp;gt; $content // 把数据放入 Body&lt;br /&gt;
    ]&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
// 3. 创建流上下文 (Stream Context)&lt;br /&gt;
$context  = stream_context_create($options);&lt;br /&gt;
&lt;br /&gt;
// 4. 发送请求&lt;br /&gt;
// 注意：这里 URL 还是干净的 URL，把 $context 作为第三个参数传进去&lt;br /&gt;
$response = file_get_contents($url, false, $context);&lt;br /&gt;
&lt;br /&gt;
// 5. 处理结果&lt;br /&gt;
if ($response !== false) {&lt;br /&gt;
    $json = json_decode($response, true);&lt;br /&gt;
    // 加上 isset 检查防止报错&lt;br /&gt;
    $err = isset($json[&#039;error&#039;]) ? $json[&#039;error&#039;] : &#039;Unknown&#039;;&lt;br /&gt;
    $msg = isset($json[&#039;message&#039;]) ? $json[&#039;message&#039;] : &#039;No message&#039;;&lt;br /&gt;
    &lt;br /&gt;
    echo &amp;quot;Error Code: &amp;quot; . $err . &amp;quot;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;Message: &amp;quot; . $msg . &amp;quot;\n&amp;quot;;&lt;br /&gt;
} else {&lt;br /&gt;
    echo &amp;quot;请求失败\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Golang 示例:执行命令在服务器的/tmp目录下创建一个文件(basicShell/exec) ====&lt;br /&gt;
Go 语言使用&#039;&#039;&#039;net/url&#039;&#039;&#039;包来构建参数。&amp;lt;syntaxhighlight lang=&amp;quot;go&amp;quot;&amp;gt;&lt;br /&gt;
#GET&lt;br /&gt;
package main&lt;br /&gt;
&lt;br /&gt;
import (&lt;br /&gt;
	&amp;quot;encoding/json&amp;quot;&lt;br /&gt;
	&amp;quot;fmt&amp;quot;&lt;br /&gt;
	&amp;quot;io/ioutil&amp;quot;&lt;br /&gt;
	&amp;quot;net/http&amp;quot;&lt;br /&gt;
	&amp;quot;net/url&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
func main() {&lt;br /&gt;
	baseURL := &amp;quot;https://api.64clouds.com/v1/basicShell/exec&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	// 设置参数&lt;br /&gt;
	params := url.Values{}&lt;br /&gt;
	params.Add(&amp;quot;veid&amp;quot;, &amp;quot;YOUR_VEID&amp;quot;)&lt;br /&gt;
	params.Add(&amp;quot;api_key&amp;quot;, &amp;quot;YOUR_API_KEY&amp;quot;)&lt;br /&gt;
	params.Add(&amp;quot;command&amp;quot;, &amp;quot;touch /tmp/golang_test.txt&amp;quot;) // 创建一个文件&lt;br /&gt;
&lt;br /&gt;
	// 拼接 URL (Encode() 会自动处理空格和特殊字符)&lt;br /&gt;
	fullURL := fmt.Sprintf(&amp;quot;%s?%s&amp;quot;, baseURL, params.Encode())&lt;br /&gt;
&lt;br /&gt;
	// 发送 GET 请求&lt;br /&gt;
	resp, err := http.Get(fullURL)&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		fmt.Printf(&amp;quot;请求失败: %s\n&amp;quot;, err)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
	defer resp.Body.Close()&lt;br /&gt;
&lt;br /&gt;
	// 读取响应&lt;br /&gt;
	body, _ := ioutil.ReadAll(resp.Body)&lt;br /&gt;
&lt;br /&gt;
	// 解析 JSON (可选)&lt;br /&gt;
	var result map[string]interface{}&lt;br /&gt;
	if err := json.Unmarshal(body, &amp;amp;result); err == nil {&lt;br /&gt;
		fmt.Printf(&amp;quot;Error Code: %v\n&amp;quot;, result[&amp;quot;error&amp;quot;])&lt;br /&gt;
		fmt.Printf(&amp;quot;Message: %v\n&amp;quot;, result[&amp;quot;message&amp;quot;])&lt;br /&gt;
	} else {&lt;br /&gt;
		fmt.Println(string(body))&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#POST&lt;br /&gt;
package main&lt;br /&gt;
&lt;br /&gt;
import (&lt;br /&gt;
	&amp;quot;encoding/json&amp;quot;&lt;br /&gt;
	&amp;quot;fmt&amp;quot;&lt;br /&gt;
	&amp;quot;io/ioutil&amp;quot;&lt;br /&gt;
	&amp;quot;net/http&amp;quot;&lt;br /&gt;
	&amp;quot;net/url&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
func main() {&lt;br /&gt;
	// 1. 这里的 URL 不需要拼接参数了，只写接口地址&lt;br /&gt;
	baseURL := &amp;quot;https://api.64clouds.com/v1/basicShell/exec&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	// 2. 设置参数 (这一步保持不变，还是用 url.Values)&lt;br /&gt;
	params := url.Values{}&lt;br /&gt;
	params.Set(&amp;quot;veid&amp;quot;, &amp;quot;YOUR_VEID&amp;quot;)      // 习惯上用 Set 替换 Add (防止重复)，效果一样&lt;br /&gt;
	params.Set(&amp;quot;api_key&amp;quot;, &amp;quot;YOUR_API_KEY&amp;quot;)&lt;br /&gt;
	params.Set(&amp;quot;command&amp;quot;, &amp;quot;touch /tmp/golang_post_test.txt&amp;quot;) // 改个文件名区分&lt;br /&gt;
&lt;br /&gt;
	// 3. 发送 POST 请求 (核心修改)&lt;br /&gt;
	// http.PostForm 这个函数专门用于发送 form-data&lt;br /&gt;
	// 它会自动设置 Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
	// 并且把 params 编码放在 Body 里&lt;br /&gt;
	resp, err := http.PostForm(baseURL, params)&lt;br /&gt;
	&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		fmt.Printf(&amp;quot;请求失败: %s\n&amp;quot;, err)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
	defer resp.Body.Close()&lt;br /&gt;
&lt;br /&gt;
	//读取响应&lt;br /&gt;
	body, _ := ioutil.ReadAll(resp.Body)&lt;br /&gt;
&lt;br /&gt;
	// 解析 JSON&lt;br /&gt;
	var result map[string]interface{}&lt;br /&gt;
	if err := json.Unmarshal(body, &amp;amp;result); err == nil {&lt;br /&gt;
		fmt.Printf(&amp;quot;Error Code: %v\n&amp;quot;, result[&amp;quot;error&amp;quot;])&lt;br /&gt;
		fmt.Printf(&amp;quot;Message: %v\n&amp;quot;, result[&amp;quot;message&amp;quot;])&lt;br /&gt;
	} else {&lt;br /&gt;
		fmt.Println(string(body))&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 常见错误 ==&lt;br /&gt;
[[File:Apierror1.png]]&lt;br /&gt;
&lt;br /&gt;
这个是说没有发现veid参数 有可能没写或者写错了&lt;br /&gt;
&lt;br /&gt;
比如 &amp;lt;nowiki&amp;gt;https://api.64clouds.com/v1/getServiceInfo?这里缺少了veid&amp;amp;api_key=privateewww324242&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Apierror2.png|border]]&lt;br /&gt;
&lt;br /&gt;
这个自然就是缺少api_key啦&lt;br /&gt;
&lt;br /&gt;
比如 &amp;lt;nowiki&amp;gt;https://api.64clouds.com/v1/getServiceInfo?veid=3232&amp;amp;这里缺少了api_key&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Apierror3.png|border]]&lt;br /&gt;
&lt;br /&gt;
API:invalidrequest 未验证的请求&lt;br /&gt;
&lt;br /&gt;
请求路径错误…就是说&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;https://api.64clouds.com/v1/stare?veid=33333&amp;amp;api_key=privateewww324242&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; 把start错误拼写成了stare&lt;br /&gt;
&lt;br /&gt;
[[File:Apierror4.png|border]]&lt;br /&gt;
&lt;br /&gt;
身份验证失败&lt;br /&gt;
&lt;br /&gt;
veid或api_key错误&lt;br /&gt;
&lt;br /&gt;
== 请注意 ==&lt;br /&gt;
&lt;br /&gt;
* 当您的参数中含有特殊字符 例如调用basicShell/exec 接口执行ls -la /var/log指令时 &amp;lt;nowiki&amp;gt;https://api.64clouds.com/v1/basicShell/exec?veid=33333&amp;amp;api_key=privateewww324242&amp;amp;command=ls&amp;lt;/nowiki&amp;gt; -la /var/log  您需要使用url编码变为&amp;amp;command=ls%20-la%20%2Fvar%2Flog 具体怎样url编码和您使用的工具有关&lt;br /&gt;
* &#039;&#039;&#039;请妥善使用API 避免滥用等问题&#039;&#039;&#039;&lt;br /&gt;
* 当您在短时间内执行过多的 API 调用时，KiwiVM API 可能会开始在几分钟内丢弃您的请求。此调用允许监控此事。详情请查看 请求路径 getRateLimitStatus 的描述和返回值列&lt;br /&gt;
&lt;br /&gt;
== 官方API文档的翻译 ==&lt;br /&gt;
更新时间utc 2026/01/20 05:58&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!请求路径&lt;br /&gt;
!参数&lt;br /&gt;
!描述和返回值&lt;br /&gt;
!&lt;br /&gt;
|-&lt;br /&gt;
|start&lt;br /&gt;
|&lt;br /&gt;
|启动 VPS&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|stop&lt;br /&gt;
|&lt;br /&gt;
|停止 VPS&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|restart&lt;br /&gt;
|&lt;br /&gt;
|重启 VPS&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|kill&lt;br /&gt;
|&lt;br /&gt;
|允许强制停止一个卡死且无法通过正常手段停止的 VPS。请务必谨慎使用此功能，因为任何未保存的数据都将丢失&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getServiceInfo&lt;br /&gt;
|&lt;br /&gt;
|获取服务信息&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：                                  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;vm_type&#039;&#039;&#039;: 虚拟化类型（ovz 或 kvm）                                                   &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;hostname&#039;&#039;&#039;: VPS 的主机名                                  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;node_alias&#039;&#039;&#039;: 物理节点的内部昵称                                  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;node_location&#039;&#039;&#039;: 物理位置（国家，州/省）                                                          &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;location_ipv6_ready&#039;&#039;&#039;: 当前位置是否支持 IPv6                                  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;plan&#039;&#039;&#039;: 套餐名称&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;plan_disk&#039;&#039;&#039;: 磁盘配额（字节）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;plan_ram&#039;&#039;&#039;: 内存（字节）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;plan_swap&#039;&#039;&#039;: SWAP 交换分区（字节）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;os&#039;&#039;&#039;: 操作系统&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;email&#039;&#039;&#039;: 账户的主电子邮件地址&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;plan_monthly_data&#039;&#039;&#039;: 每月允许的数据传输量（字节）。需要乘以 &amp;lt;code&amp;gt;monthly_data_multiplier&amp;lt;/code&amp;gt;（见下文）。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;data_counter&#039;&#039;&#039;: 当前计费月份已使用的数据传输量。需要乘以 &amp;lt;code&amp;gt;monthly_data_multiplier&amp;lt;/code&amp;gt;（见下文）。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;monthly_data_multiplier&#039;&#039;&#039;: 某些位置提供更昂贵的带宽；此变量包含带宽核算系数。&lt;br /&gt;
&#039;&#039;&#039;data_next_reset&#039;&#039;&#039;: 传输计数器重置的日期和时间（UNIX 时间戳）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ip_addresses&#039;&#039;&#039;: 分配给 VPS 的 IPv4 地址和 IPv6 /64 子网（数组）&#039;&#039;&#039;private_ip_addresses&#039;&#039;&#039;: 分配给 VPS 的私有 IPv4 地址（数组）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ip_nullroutes&#039;&#039;&#039;: 关于 (D)DoS 攻击期间 IP 地址空路由封禁的信息（数组）。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;iso1&#039;&#039;&#039;: 已挂载的镜像 #1&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;iso2&#039;&#039;&#039;: 已挂载的镜像 #2（目前不支持）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;available_isos&#039;&#039;&#039;: 可供使用的 ISO 镜像数组&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;plan_max_ipv6s&#039;&#039;&#039;: 套餐允许的最大 IPv6 /64 子网数量&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;rdns_api_available&#039;&#039;&#039;: 是否可以通过 API 设置 rDNS 记录&#039;&#039;&#039;plan_private_network_available&#039;&#039;&#039;: 此套餐是否可以使用私有网络功能&#039;&#039;&#039;location_private_network_available&#039;&#039;&#039;: 此位置是否可以使用私有网络功能&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ptr&#039;&#039;&#039;: rDNS 记录（二维数组：ip=&amp;gt;value）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;suspended&#039;&#039;&#039;: VPS 是否被暂停&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;policy_violation&#039;&#039;&#039;: 是否有需要注意的活动策略违规（参见 &amp;lt;code&amp;gt;getPolicyViolations&amp;lt;/code&amp;gt;）&#039;&#039;&#039;suspension_count&#039;&#039;&#039;: 本日历年内服务被暂停的次数&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;total_abuse_points&#039;&#039;&#039;: 本日历年内累计的滥用积分总数&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;max_abuse_points&#039;&#039;&#039;: 套餐在本日历年内允许的最大滥用积分&lt;br /&gt;
&lt;br /&gt;
 [ip_nullroutes] =&amp;gt; Array&lt;br /&gt;
&lt;br /&gt;
         (&lt;br /&gt;
&lt;br /&gt;
             [1.2.3.4] =&amp;gt; Array&lt;br /&gt;
&lt;br /&gt;
                 (&lt;br /&gt;
&lt;br /&gt;
                     [nullroute_timestamp] =&amp;gt; 1556678627      // start of attack&lt;br /&gt;
&lt;br /&gt;
                     [nullroute_duration_s] =&amp;gt; 360            // duration of nullroute&lt;br /&gt;
&lt;br /&gt;
                     [log] =&amp;gt; &amp;quot;Packet dump data of the attack (multi-line)&amp;quot; // raw log of attack&lt;br /&gt;
&lt;br /&gt;
                 )&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
         )&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getLiveServiceInfo&lt;br /&gt;
|&lt;br /&gt;
|此函数返回 &amp;lt;code&amp;gt;getServiceInfo&amp;lt;/code&amp;gt; 提供的所有数据。此外，它还提供 VPS 的详细状态。请注意，此调用可能需要长达 15 秒才能完成。&lt;br /&gt;
根据虚拟化程序的不同，此调用将返回以下信息： &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Returns [OVZ hypervisor] (OVZ 虚拟化返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;vz_status&#039;&#039;&#039;: 包含 OpenVZ beancounters、系统平均负载、进程数、打开的文件、套接字、内存使用情况等的数组&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;vz_quota&#039;&#039;&#039;: 包含 OpenVZ 磁盘大小、inode 和使用情况信息的数组&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;is_cpu_throttled&#039;&#039;&#039;: 0 = CPU 未被限制，1 = 由于高使用率 CPU 被限制。限制每 2 小时自动重置一次。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ssh_port&#039;&#039;&#039;: VPS 的 SSH 端口&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Returns [KVM hypervisor] (KVM 虚拟化返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ve_status&#039;&#039;&#039;: Starting（启动中）、Running（运行中）或 Stopped（已停止）&#039;&#039;&#039;ve_mac1&#039;&#039;&#039;: 主网络接口的 MAC 地址&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ve_used_disk_space_b&#039;&#039;&#039;: 已占用（映射）的磁盘空间（字节）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ve_disk_quota_gb&#039;&#039;&#039;: 磁盘镜像的实际大小（GB）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;is_cpu_throttled&#039;&#039;&#039;: 0 = CPU 未被限制，1 = 由于高使用率 CPU 被限制。限制每 2 小时自动重置一次。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;is_disk_throttled&#039;&#039;&#039;: 0 = 磁盘 I/O 未被限制，1 = 由于高使用率磁盘 I/O 被限制。限制根据持续存储 I/O 利用率每 15-180 分钟自动重置一次。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ssh_port&#039;&#039;&#039;: VPS 的 SSH 端口（仅当 VPS 运行时返回）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;live_hostname&#039;&#039;&#039;: 在 VPS 内部执行 &amp;quot;hostname&amp;quot; 命令的结果&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;load_average&#039;&#039;&#039;: 原始平均负载字符串&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mem_available_kb&#039;&#039;&#039;: 可用内存量（KB）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swap_total_kb&#039;&#039;&#039;: Swap 总量（KB）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swap_available_kb&#039;&#039;&#039;: 可用 Swap 量（KB）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;screendump_png_base64&#039;&#039;&#039;: VGA 控制台的 base64 编码 png 截图&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getAvailableOS&lt;br /&gt;
|&lt;br /&gt;
|获取可用操作系统&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;installed&#039;&#039;&#039;: 当前安装的操作系统&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;templates&#039;&#039;&#039;: 可用操作系统的数组&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|reinstallOS&lt;br /&gt;
|&amp;lt;mark&amp;gt;os&amp;lt;/mark&amp;gt;&lt;br /&gt;
|重装操作系统。必须通过 &amp;lt;code&amp;gt;os&amp;lt;/code&amp;gt; 变量指定操作系统。使用 &amp;lt;code&amp;gt;getAvailableOS&amp;lt;/code&amp;gt; 调用获取可用系统列表。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;rootPassword&#039;&#039;&#039;: 新的 root 密码&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;sshPort&#039;&#039;&#039;: SSH 端口&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;sshKeys&#039;&#039;&#039;: 上传到 &amp;lt;code&amp;gt;/root/.ssh/authorized_keys&amp;lt;/code&amp;gt; 的 SSH 密钥&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;sshKeysBrief&#039;&#039;&#039;: SSH 密钥（视觉展示用的缩短版）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notificationEmail&#039;&#039;&#039;: 完成时发送通知的电子邮件地址&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|updateSshKeys&lt;br /&gt;
|&amp;lt;mark&amp;gt;ssh_keys&amp;lt;/mark&amp;gt;&lt;br /&gt;
|更新 Hypervisor 保管库中的每 VM SSH 密钥。这些密钥将在 &amp;lt;code&amp;gt;reinstallOS&amp;lt;/code&amp;gt; 调用期间写入 &amp;lt;code&amp;gt;/root/.ssh/authorized_keys&amp;lt;/code&amp;gt;。这些密钥将覆盖计费门户中设置的任何密钥。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getSshKeys&lt;br /&gt;
|&lt;br /&gt;
|获取存储在 Hypervisor 保管库以及计费门户中的 SSH 密钥。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ssh_keys_veid&#039;&#039;&#039;: 存储在 Hypervisor 保管库中的每 VM SSH 密钥&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ssh_keys_user&#039;&#039;&#039;: 存储在计费门户中的每账户 SSH 密钥&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ssh_keys_preferred&#039;&#039;&#039;: 在 &amp;lt;code&amp;gt;reinstallOS&amp;lt;/code&amp;gt; 调用期间实际使用的 SSH 密钥（每 VM 密钥将始终覆盖每账户密钥）&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;shortened_ssh_keys_veid&#039;&#039;&#039;: 视觉缩短后的密钥&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;shortened_ssh_keys_user&#039;&#039;&#039;: 视觉缩短后的密钥&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;shortened_ssh_keys_preferred&#039;&#039;&#039;: 视觉缩短后的密钥&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|resetRootPassword&lt;br /&gt;
|&lt;br /&gt;
|生成并设置一个新的 root 密码。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&#039;&#039;&#039;password&#039;&#039;&#039;: 新的 root 密码&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getUsageGraphs&lt;br /&gt;
|&lt;br /&gt;
|已废弃，请改用 &amp;lt;code&amp;gt;getRawUsageStats&amp;lt;/code&amp;gt;。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getRawUsageStats&lt;br /&gt;
|&lt;br /&gt;
|返回一个二维数组，包含 KiwiVM 中“Detailed Statistics（详细统计）”下显示的详细使用统计数据。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getAuditLog&lt;br /&gt;
|&lt;br /&gt;
|返回一个数组，包含 KiwiVM 中“Audit Log（审计日志）”下显示的详细审计日志。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|setHostname&lt;br /&gt;
|&amp;lt;mark&amp;gt;newHostname&amp;lt;/mark&amp;gt;&lt;br /&gt;
|设置新的主机名。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|setPTR&lt;br /&gt;
|&amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ptr&amp;lt;/code&amp;gt;&lt;br /&gt;
|为 IP 设置新的 PTR (rDNS) 记录。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|iso/mount&lt;br /&gt;
|&amp;lt;mark&amp;gt;iso&amp;lt;/mark&amp;gt;&lt;br /&gt;
|设置要引导的 ISO 镜像。在此 API 调用后，VM 必须完全关机并重新启动。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;iso/unmount&#039;&#039;&#039;&lt;br /&gt;
|&lt;br /&gt;
|移除 ISO 镜像并配置 VM 从主存储引导。在此 API 调用后，VM 必须完全关机并重新启动。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|basicShell/cd&lt;br /&gt;
|&amp;lt;code&amp;gt;currentDir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;newDir&amp;lt;/code&amp;gt;&lt;br /&gt;
|模拟 VPS 内部的目录更改。可用于构建类似“Basic shell”的 shell。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&#039;&#039;&#039;pwd&#039;&#039;&#039;: 更改后的 &amp;quot;pwd&amp;quot; 命令结果。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|basicShell/exec&lt;br /&gt;
|&amp;lt;mark&amp;gt;command&amp;lt;/mark&amp;gt;&lt;br /&gt;
|在 VPS 上执行 shell 命令（同步）。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;error&#039;&#039;&#039;: 已执行命令的退出状态代码&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;message&#039;&#039;&#039;: 已执行命令的控制台输出&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|shellScript/exec&lt;br /&gt;
|&amp;lt;mark&amp;gt;script&amp;lt;/mark&amp;gt;&lt;br /&gt;
|在 VPS 上执行 shell 脚本（异步）。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;log&#039;&#039;&#039;: 输出日志文件的名称。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|snapshot/create&lt;br /&gt;
|&amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; (可选)&lt;br /&gt;
|创建快照&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notificationEmail&#039;&#039;&#039;: 任务完成后将发送通知的文件中的电子邮件地址。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|snapshot/list&lt;br /&gt;
|&lt;br /&gt;
|获取快照列表。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;snapshots&#039;&#039;&#039;: 快照数组（fileName, os, description, size, md5, sticky, purgesIn, downloadLink, downloadLinkSSL）。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|snapshot/delete&lt;br /&gt;
|&amp;lt;mark&amp;gt;snapshot&amp;lt;/mark&amp;gt;&lt;br /&gt;
|按文件名删除快照（可通过 &amp;lt;code&amp;gt;snapshot/list&amp;lt;/code&amp;gt; 调用检索）。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|snapshot/restore&lt;br /&gt;
|&amp;lt;mark&amp;gt;snapshot&amp;lt;/mark&amp;gt;&lt;br /&gt;
|按文件名还原快照（可通过 &amp;lt;code&amp;gt;snapshot/list&amp;lt;/code&amp;gt; 调用检索）。这将覆盖 VPS 上的所有数据。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|snapshot/toggleSticky&lt;br /&gt;
|&amp;lt;code&amp;gt;snapshot&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sticky&amp;lt;/code&amp;gt;&lt;br /&gt;
|设置或移除 sticky 属性（“sticky”快照永远不会被清除）。快照名称可通过 &amp;lt;code&amp;gt;snapshot/list&amp;lt;/code&amp;gt; &lt;br /&gt;
调用检索——查找 fileName 变量。设置 sticky = 1 以设置 sticky 属性设置 sticky = 0 以移除 sticky 属性&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|snapshot/export&lt;br /&gt;
|&amp;lt;mark&amp;gt;snapshot&amp;lt;/mark&amp;gt;&lt;br /&gt;
|生成一个令牌 (token)，通过该令牌可以将快照传输到另一个实例。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|snapshot/import&lt;br /&gt;
|&amp;lt;code&amp;gt;sourceVeid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sourceToken&amp;lt;/code&amp;gt;&lt;br /&gt;
|从由 VEID 和 Token 标识的另一个实例导入快照。VEID 和 Token 必须事先通过 &amp;lt;code&amp;gt;snapshot/export&amp;lt;/code&amp;gt; 调用从另一个实例获得。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|backup/list&lt;br /&gt;
|&lt;br /&gt;
|获取自动备份列表。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;backups&#039;&#039;&#039;: 备份数组（backupToken, size, os, md5, timestamp）。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|backup/copyToSnapshot&lt;br /&gt;
|&amp;lt;mark&amp;gt;backupToken&amp;lt;/mark&amp;gt;&lt;br /&gt;
|将由 &amp;lt;code&amp;gt;backupToken&amp;lt;/code&amp;gt;（由 &amp;lt;code&amp;gt;backup/list&amp;lt;/code&amp;gt; 返回）标识的备份复制为可还原的快照。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|ipv6/add&lt;br /&gt;
|&lt;br /&gt;
|分配一个新的 IPv6 /64 子网。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&#039;&#039;&#039;assigned_subnet&#039;&#039;&#039;: 新分配的 IPv6 /64 子网&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|ipv6/delete&lt;br /&gt;
|&amp;lt;mark&amp;gt;ip&amp;lt;/mark&amp;gt;&lt;br /&gt;
|释放指定的 IPv6 /64 子网。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|migrate/getLocations&lt;br /&gt;
|&lt;br /&gt;
|返回所有可能的迁移位置。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&#039;&#039;&#039;currentLocation&#039;&#039;&#039;: 当前位置的 ID&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;locations&#039;&#039;&#039;: 可迁移到的位置 ID&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;descriptions&#039;&#039;&#039;: 可用位置的友好描述&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dataTransferMultipliers&#039;&#039;&#039;: 某些位置可能提供更昂贵的带宽，其每月限额将更低。此数组包含每个位置的每月数据传输限额倍率。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|migrate/start&lt;br /&gt;
|&amp;lt;mark&amp;gt;location&amp;lt;/mark&amp;gt;&lt;br /&gt;
|启动 VPS 迁移到新位置。输入新位置 ID。请注意，这将导致所有 IPv4 地址被替换。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notificationEmail&#039;&#039;&#039;: 任务完成后将发送通知的文件中的电子邮件地址。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;newIps&#039;&#039;&#039;: 分配给 VPS 的新 IP 地址数组。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|cloneFromExternalServer&lt;br /&gt;
|&amp;lt;code&amp;gt;externalServerIP&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;externalServerSSHport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;externalServerRootPassword&amp;lt;/code&amp;gt;&lt;br /&gt;
|(仅限 OVZ) 克隆远程服务器或 VPS。请参阅“从另一台服务器迁移”以了解其工作原理的示例。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getSuspensionDetails&lt;br /&gt;
|&lt;br /&gt;
|检索与服务暂停相关的信息。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&#039;&#039;&#039;suspension_count&#039;&#039;&#039;: 本日历年内服务被暂停的次数&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;total_abuse_points&#039;&#039;&#039;: 本日历年内累计的滥用积分总数&#039;&#039;&#039;max_abuse_points&#039;&#039;&#039;: 套餐在本日历年内允许的最大滥用积分&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;suspensions&#039;&#039;&#039;: 所有未解决问题的数组以及滥用的支持证据。参见下方示例。&lt;br /&gt;
&#039;&#039;&#039;evidence&#039;&#039;&#039;: 投诉的全文或有关问题的更多详细信息&lt;br /&gt;
&lt;br /&gt;
服务暂停时的示例输出:&lt;br /&gt;
&lt;br /&gt;
 [suspensions] =&amp;gt; Array&lt;br /&gt;
&lt;br /&gt;
         (&lt;br /&gt;
&lt;br /&gt;
             [0] =&amp;gt; stdClass Object&lt;br /&gt;
&lt;br /&gt;
                 (&lt;br /&gt;
&lt;br /&gt;
                     [record_id] =&amp;gt; 11851         // Case ID, needed to unsuspend &lt;br /&gt;
&lt;br /&gt;
                                                  // the service via &amp;quot;unsuspend&amp;quot; API call&lt;br /&gt;
&lt;br /&gt;
                     [flag] =&amp;gt; copyright          // Type of abuse&lt;br /&gt;
&lt;br /&gt;
                     [is_soft] =&amp;gt; 1               // 0 = must contact support to unsuspend&lt;br /&gt;
&lt;br /&gt;
                                                  // 1 = can unsuspend via API call&lt;br /&gt;
&lt;br /&gt;
                     [evidence_record_id] =&amp;gt; 2207 // Detailed abuse report ID (see below)&lt;br /&gt;
&lt;br /&gt;
                     [abuse_points] =&amp;gt; 100        // Each abuse incident increases total_abuse_points counter&lt;br /&gt;
&lt;br /&gt;
                 )&lt;br /&gt;
&lt;br /&gt;
         )&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
     [evidence] =&amp;gt; stdClass Object&lt;br /&gt;
&lt;br /&gt;
         (&lt;br /&gt;
&lt;br /&gt;
             [2207] =&amp;gt; &amp;quot;Full text of abuse complaint here&amp;quot;&lt;br /&gt;
&lt;br /&gt;
         )&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
     [suspension_count] =&amp;gt; 2&lt;br /&gt;
&lt;br /&gt;
     [total_abuse_points] =&amp;gt; 200&lt;br /&gt;
&lt;br /&gt;
     [max_abuse_points] =&amp;gt; 1500&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getPolicyViolations&lt;br /&gt;
|&lt;br /&gt;
|检索与活动策略违规相关的信息。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&#039;&#039;&#039;total_abuse_points&#039;&#039;&#039;: 本日历年内累计的滥用积分总数&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;max_abuse_points&#039;&#039;&#039;: 套餐在本日历年内允许的最大滥用积分&#039;&#039;&#039;policy_violations&#039;&#039;&#039;: 所有未解决问题的数组以及滥用的支持证据。参见下方示例。&lt;br /&gt;
&lt;br /&gt;
当存在活跃的策略违规时，&lt;br /&gt;
&lt;br /&gt;
示例输出如下&lt;br /&gt;
&lt;br /&gt;
 [policy_violations] =&amp;gt; Array&lt;br /&gt;
&lt;br /&gt;
         (&lt;br /&gt;
&lt;br /&gt;
             [0] =&amp;gt; Array&lt;br /&gt;
&lt;br /&gt;
                 (&lt;br /&gt;
&lt;br /&gt;
                     [record_id] =&amp;gt; 14            // Case ID, for resolvePolicyViolation &lt;br /&gt;
&lt;br /&gt;
                     [timestamp] =&amp;gt; 1571469818    // Unix timestamp when record was created&lt;br /&gt;
&lt;br /&gt;
                     [suspend_at] =&amp;gt; 1571599418   // Service will be suspended if not resolved by this time&lt;br /&gt;
&lt;br /&gt;
                     [flag] =&amp;gt; copyright          // Type of abuse&lt;br /&gt;
&lt;br /&gt;
                     [is_soft] =&amp;gt; 1               // 0 = must contact support to unsuspend&lt;br /&gt;
&lt;br /&gt;
                                                  // 1 = can unsuspend via API call&lt;br /&gt;
&lt;br /&gt;
                     [abuse_points] =&amp;gt; 100        // Each abuse incident increases total_abuse_points counter&lt;br /&gt;
&lt;br /&gt;
                     [evidence_data] =&amp;gt;           // Details of violation (text)&lt;br /&gt;
&lt;br /&gt;
                 )&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
         )&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
     [total_abuse_points] =&amp;gt; 200&lt;br /&gt;
&lt;br /&gt;
     [max_abuse_points] =&amp;gt; 1500&lt;br /&gt;
&lt;br /&gt;
     [error] =&amp;gt; 0&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|unsuspend&lt;br /&gt;
|&amp;lt;mark&amp;gt;record_id&amp;lt;/mark&amp;gt;&lt;br /&gt;
|清除由 &amp;lt;code&amp;gt;record_id&amp;lt;/code&amp;gt; 标识的滥用问题并解封 VPS。详情请参阅 &amp;lt;code&amp;gt;getSuspensionDetails&amp;lt;/code&amp;gt; 调用。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|resolvePolicyViolation&lt;br /&gt;
|&amp;lt;mark&amp;gt;record_id&amp;lt;/mark&amp;gt;&lt;br /&gt;
|将策略违规标记为已解决。这是避免服务暂停所必需的。详情请参阅 &amp;lt;code&amp;gt;getPolicyViolations&amp;lt;/code&amp;gt; 调用。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|getRateLimitStatus&lt;br /&gt;
|&lt;br /&gt;
|当您在短时间内执行过多的 API 调用时，KiwiVM API 可能会开始在几分钟内丢弃您的请求。此调用允许监控此事。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;remaining_points_15min&#039;&#039;&#039;: 当前 15 分钟间隔内可使用的“点数”&#039;&#039;&#039;remaining_points_24h&#039;&#039;&#039;: 当前 24 小时间隔内可使用的“点数”&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|privateIp/getAvailableIps&lt;br /&gt;
|&lt;br /&gt;
|返回所有可用的（空闲）IPv4 地址，您可以在 VM 上激活这些地址&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&#039;&#039;&#039;available_ips&#039;&#039;&#039;: 可用私有 IP 地址的数组。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|privateIp/assign&lt;br /&gt;
|&amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; (可选)&lt;br /&gt;
|分配私有 IP 地址。如果未指定 IP 地址，将分配一个随机地址。&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&#039;&#039;&#039;assigned_ips&#039;&#039;&#039;: 成功分配的私有 IP 地址数组&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|privateIp/delete&lt;br /&gt;
|&amp;lt;mark&amp;gt;ip&amp;lt;/mark&amp;gt;&lt;br /&gt;
|删除私有 IP 地址。&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|kiwivm/getNotificationPreferences&lt;br /&gt;
|&lt;br /&gt;
|返回所有可用的通知设置及其状态&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&#039;&#039;&#039;email_preferences&#039;&#039;&#039;: 可用通知及其状态的数组&lt;br /&gt;
&#039;&#039;&#039;notificationEmail&#039;&#039;&#039;: 当前配置的发送通知的电子邮件地址&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|kiwivm/setNotificationPreferences&lt;br /&gt;
|&amp;lt;code&amp;gt;json_notification_preferences&amp;lt;/code&amp;gt; (json 格式的数组, preference_id:0/1)&lt;br /&gt;
|更改通知首选项&lt;br /&gt;
&#039;&#039;&#039;Returns (返回值)&#039;&#039;&#039;：&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;submitted_email_preferences&#039;&#039;&#039;: 已提交更改的数组&#039;&#039;&#039;updated_email_preferences&#039;&#039;&#039;: 实际已更改的首选项数组&#039;&#039;&#039;friendly_descriptions&#039;&#039;&#039;: 所有首选项的友好描述&lt;br /&gt;
|&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E6%90%AC%E7%93%A6%E5%B7%A5_VPS_%E9%81%AD%E9%81%87_DDoS_%E6%94%BB%E5%87%BB%E6%B7%B1%E5%BA%A6%E6%8E%92%E9%9A%9C%E4%B8%8E%E8%87%AA%E6%95%91%E6%89%8B%E5%86%8C&amp;diff=1175</id>
		<title>搬瓦工 VPS 遭遇 DDoS 攻击深度排障与自救手册</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E6%90%AC%E7%93%A6%E5%B7%A5_VPS_%E9%81%AD%E9%81%87_DDoS_%E6%94%BB%E5%87%BB%E6%B7%B1%E5%BA%A6%E6%8E%92%E9%9A%9C%E4%B8%8E%E8%87%AA%E6%95%91%E6%89%8B%E5%86%8C&amp;diff=1175"/>
		<updated>2026-01-25T10:18:18Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​修复排版&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;面对搬瓦工 VPS 突然全网失联且无法 &amp;lt;code&amp;gt;Ping&amp;lt;/code&amp;gt; 通的情况，需要结合实际运维经验，深入剖析 DDoS 攻击下的空路由（Nullrouted）触发机制，并建立一套从精准判定到彻底自救的实战方案。&lt;br /&gt;
&lt;br /&gt;
== 一、 为什么我的搬瓦工服务器会突然“消失”？ ==&lt;br /&gt;
&lt;br /&gt;
搬瓦工绝大多数套餐方案在设计上更偏向于提供高性能的底层架构，而非自带硬件级 DDoS 防御。这意味着当特定 IP 遭遇大规模恶意请求时，机房会采取自动化保护措施以确保整体链路的稳定。&lt;br /&gt;
&lt;br /&gt;
* 空路由（Nullrouted）机制：当流量超过阈值，系统会将指向该 IP 的所有数据包丢弃，形成所谓的“黑洞”。此时网站无法打开、&amp;lt;code&amp;gt;SSH&amp;lt;/code&amp;gt; 无法登陆、&amp;lt;code&amp;gt;Ping&amp;lt;/code&amp;gt; 测试也会完全失效。&lt;br /&gt;
* &lt;br /&gt;
* 1800 秒封禁周期：触发空路由后，默认的屏蔽时长通常为 &amp;lt;code&amp;gt;1800 秒&amp;lt;/code&amp;gt;（30 分钟）。如果封禁解除后攻击仍在继续，屏蔽时间会循环延长，导致服务器反复失联。&lt;br /&gt;
&lt;br /&gt;
== 二、 如何确认自己的服务器正处于 DDoS 攻击中？ ==&lt;br /&gt;
&lt;br /&gt;
在排查网络故障时，首先需要区分是软件配置错误还是受到了流量攻击。通过以下数据特征可以快速锁定故障性质。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. 查看系统邮件通知&#039;&#039;&#039; 当 IP 触发空路由时，系统会自动向注册邮箱发送一封包含 &amp;lt;code&amp;gt;is currently under a (D)DoS attack&amp;lt;/code&amp;gt; 的邮件，明确告知 IP 已被暂时屏蔽。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. 分析流量特征曲线&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
在排查过程中，通过图形化的数据回馈能最直观地捕捉攻击痕迹。相比于命令行，KiwiVM 后台的统计图表能更清晰地展现出入站流量的异常脉络。&lt;br /&gt;
&lt;br /&gt;
:* 进入路径：登录 &amp;lt;code&amp;gt;KiwiVM&amp;lt;/code&amp;gt; 管理后台，在左侧 &amp;lt;code&amp;gt;Security &amp;amp; Records&amp;lt;/code&amp;gt; 栏目下点击 &amp;lt;code&amp;gt;Detailed statistics&amp;lt;/code&amp;gt; 页面。&lt;br /&gt;
&lt;br /&gt;
:* 观察指标：在右侧详情页中，重点观察 &amp;lt;code&amp;gt;Network I/O (Bits per second)&amp;lt;/code&amp;gt; 图表。&lt;br /&gt;
&lt;br /&gt;
:* 黑洞特征判定： &lt;br /&gt;
::* 流量尖峰：如果图表中出现如绿色阴影所示的垂直状极高尖峰，说明该时段遭受了大规模入站流量冲击。 &lt;br /&gt;
&lt;br /&gt;
::* 断崖式归零：在尖峰之后，如果流量瞬间掉落并呈水平直线（接近 &amp;lt;code&amp;gt;0 bps&amp;lt;/code&amp;gt;）持续延伸，说明 &amp;lt;code&amp;gt;IP&amp;lt;/code&amp;gt; 已被系统自动放入黑洞（空路由）进行清洗。&lt;br /&gt;
&lt;br /&gt;
[[File:kiwivm-detailed-statistics-ddos-traffic.png|thumb|center|800px|alt=KiwiVM 详细统计页面显示 DDoS 攻击导致的流量断崖|通过 &amp;lt;code&amp;gt;Detailed statistics&amp;lt;/code&amp;gt; 中的流量断层判定 &amp;lt;code&amp;gt;IP&amp;lt;/code&amp;gt; 是否被封禁]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3. 状态矛盾核对&#039;&#039;&#039; 如果控制面板显示服务器状态为 Running 且未执行 重装系统 或主动关闭服务，但外部一切连接手段均失效，基本可以判定为遭受了 DDoS 攻击。&lt;br /&gt;
&lt;br /&gt;
== 三、 应急方案：利用 Cloudflare 建立前端防线 ==&lt;br /&gt;
&lt;br /&gt;
由于搬瓦工普通套餐无法提供高防线路，利用 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 的全球节点接住流量是目前成本最低且效果最好的自救方式。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. 接入与隐藏逻辑&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
通过将域名 &amp;lt;code&amp;gt;DNS&amp;lt;/code&amp;gt; 托管至 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 并点亮橙色云朵图标（代理模式），访客的请求会先打到防护节点而非搬瓦工源站。这种方式能有效隐藏真实 IP，屏蔽掉大量的扫描与流量攻击。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. 防护策略加固&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 开启攻击模式：在攻击期间开启 &amp;lt;code&amp;gt;Under Attack Mode&amp;lt;/code&amp;gt;，强制访客进行 5 秒安全验证。&lt;br /&gt;
* &lt;br /&gt;
* 防火墙规则 (WAF)：在后台针对异常频率的请求或特定国家/地区设置拦截规则。&lt;br /&gt;
* &lt;br /&gt;
* 优化缓存设置：适当提高静态资源的缓存比例，减少回源请求，减轻服务器的 &amp;lt;code&amp;gt;CPU&amp;lt;/code&amp;gt; 与网络压力。&lt;br /&gt;
&lt;br /&gt;
== 四、 利用快照功能实现数据迁移与 IP 更换 ==&lt;br /&gt;
&lt;br /&gt;
如果攻击方持续对特定 IP 进行攻击，即使配置了 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt;，旧 IP 一旦解封仍可能被再次打进黑洞。此时，利用快照功能进行业务迁移是更为彻底的办法。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;数据迁移与环境恢复流程：&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 制作快照：在 KiwiVM 界面进入 &amp;lt;code&amp;gt;Snapshots&amp;lt;/code&amp;gt; 页面，为当前受攻击的服务器做一个全量 备份。&lt;br /&gt;
* &lt;br /&gt;
* 快照还原与迁移：利用&#039;&#039;&#039;搬瓦工的快照&#039;&#039;&#039;功能，可以新建一台同配置的 VPS 并还原该镜像。或者通过面板功能进行&#039;&#039;&#039;更换机房&#039;&#039;&#039;来获取全新的 IP 地址。&lt;br /&gt;
* &lt;br /&gt;
* 解析同步更新：在获取新 IP 后，只需在 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 后台修改 &amp;lt;code&amp;gt;A 记录&amp;lt;/code&amp;gt;，新 IP 会在几秒钟内生效，从而切断旧 IP 的受攻击链路。&lt;br /&gt;
&lt;br /&gt;
== 五、遇DDoS自动断开网卡5分钟 ==&lt;br /&gt;
检测DDoS就自动断网5分钟&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果你的服务器只入 不出的话..那么这个脚本就不适合你了&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
可修改的参数&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 1.触发检查的起步阈值 (MB/s) 这个可以填写你的最大带宽 注意单位是MB&lt;br /&gt;
&lt;br /&gt;
TRIGGER_LIMIT_MB=50&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 2.出站流量比例因子 (0.0 - 1.0) &lt;br /&gt;
&lt;br /&gt;
如果流出的流量比进入的流量*设置的值(默认是0.4)就认为发生了ddos &lt;br /&gt;
&lt;br /&gt;
如果你的服务器只入 不出的话..那么这个脚本就不适合你了&lt;br /&gt;
&lt;br /&gt;
如果 (出站 TX) &amp;lt; (入站 RX * 因子)，则判定为攻击。&lt;br /&gt;
&lt;br /&gt;
SAFE_RATIO=0.4&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 3. 连续确认次数 防止误报&lt;br /&gt;
&lt;br /&gt;
MAX_RETRIES=3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 4. 检查间隔 (秒)&lt;br /&gt;
&lt;br /&gt;
CHECK_INTERVAL=2&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 5. 黑洞时长 (秒) - 300秒 = 5分钟 断网时长..&lt;br /&gt;
&lt;br /&gt;
BLACKHOLE_TIME=300&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 6. 安全模式 (true=只报警不操作, false=执行断网)&lt;br /&gt;
&lt;br /&gt;
SAFE_MODE=false&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 7. 日志显示阈值 (MB/s) 只有高过这个阈值才有显示出来的文本 就是 一些流入和 流出 数据计算的值&lt;br /&gt;
&lt;br /&gt;
LOG_LIMIT_MB=5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bash脚本版本&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 用户配置区域 =================&lt;br /&gt;
&lt;br /&gt;
# 1. 触发检查的起步阈值 (MB/s)&lt;br /&gt;
TRIGGER_LIMIT_MB=50&lt;br /&gt;
&lt;br /&gt;
# 2. 出站流量比例因子 (0.0 - 1.0)&lt;br /&gt;
# 如果 (出站 TX) &amp;lt; (入站 RX * 因子)，则判定为攻击。&lt;br /&gt;
SAFE_RATIO=0.4&lt;br /&gt;
&lt;br /&gt;
# 3. 连续确认次数&lt;br /&gt;
MAX_RETRIES=3&lt;br /&gt;
&lt;br /&gt;
# 4. 检查间隔 (秒)&lt;br /&gt;
CHECK_INTERVAL=2&lt;br /&gt;
&lt;br /&gt;
# 5. 黑洞时长 (秒) - 300秒 = 5分钟&lt;br /&gt;
BLACKHOLE_TIME=300&lt;br /&gt;
&lt;br /&gt;
# 6. 安全模式 (true=只报警不操作, false=执行断网)&lt;br /&gt;
SAFE_MODE=false&lt;br /&gt;
&lt;br /&gt;
# 7. 日志显示阈值 (MB/s)&lt;br /&gt;
LOG_LIMIT_MB=5&lt;br /&gt;
&lt;br /&gt;
# ===============================================&lt;br /&gt;
&lt;br /&gt;
# 自动获取默认网卡&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | awk &#039;{print $5; exit}&#039;)&lt;br /&gt;
OVERLOAD_COUNT=0&lt;br /&gt;
&lt;br /&gt;
# 颜色定义&lt;br /&gt;
RED=&#039;\033[0;31m&#039;&lt;br /&gt;
GREEN=&#039;\033[0;32m&#039;&lt;br /&gt;
YELLOW=&#039;\033[0;33m&#039;&lt;br /&gt;
NC=&#039;\033[0m&#039;&lt;br /&gt;
&lt;br /&gt;
echo -e &amp;quot;${GREEN}=== 智能流量保镖 v3.0 (黑洞版) 启动 ===${NC}&amp;quot;&lt;br /&gt;
echo &amp;quot;监控网卡: $IFACE&amp;quot;&lt;br /&gt;
echo &amp;quot;黑洞时长: $BLACKHOLE_TIME 秒&amp;quot;&lt;br /&gt;
echo &amp;quot;安全模式: $SAFE_MODE&amp;quot;&lt;br /&gt;
echo &amp;quot;========================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
get_bytes() {&lt;br /&gt;
    cat &amp;quot;/sys/class/net/$IFACE/statistics/$1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 初始化读数&lt;br /&gt;
PREV_RX=$(get_bytes &amp;quot;rx_bytes&amp;quot;)&lt;br /&gt;
PREV_TX=$(get_bytes &amp;quot;tx_bytes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
while true; do&lt;br /&gt;
    sleep $CHECK_INTERVAL&lt;br /&gt;
&lt;br /&gt;
    CURR_RX=$(get_bytes &amp;quot;rx_bytes&amp;quot;)&lt;br /&gt;
    CURR_TX=$(get_bytes &amp;quot;tx_bytes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # 使用 awk 计算速度和逻辑&lt;br /&gt;
    read RX_MB TX_MB IS_HIGH IS_ATTACK &amp;lt;&amp;lt;&amp;lt; $(awk -v r1=$PREV_RX -v r2=$CURR_RX \&lt;br /&gt;
                                                  -v t1=$PREV_TX -v t2=$CURR_TX \&lt;br /&gt;
                                                  -v time=$CHECK_INTERVAL \&lt;br /&gt;
                                                  -v limit=$TRIGGER_LIMIT_MB \&lt;br /&gt;
                                                  -v ratio=$SAFE_RATIO &#039;&lt;br /&gt;
    BEGIN {&lt;br /&gt;
        delta_rx = r2 - r1&lt;br /&gt;
        delta_tx = t2 - t1&lt;br /&gt;
        # 防止负数 (重启网卡后可能发生)&lt;br /&gt;
        if (delta_rx &amp;lt; 0) delta_rx = 0&lt;br /&gt;
        if (delta_tx &amp;lt; 0) delta_tx = 0&lt;br /&gt;
        &lt;br /&gt;
        rx_speed = delta_rx / 1024 / 1024 / time&lt;br /&gt;
        tx_speed = delta_tx / 1024 / 1024 / time&lt;br /&gt;
&lt;br /&gt;
        is_high = (rx_speed &amp;gt; limit) ? 1 : 0&lt;br /&gt;
        limit_tx = rx_speed * ratio&lt;br /&gt;
        is_attack = (tx_speed &amp;lt; limit_tx) ? 1 : 0&lt;br /&gt;
&lt;br /&gt;
        printf &amp;quot;%.2f %.2f %d %d&amp;quot;, rx_speed, tx_speed, is_high, is_attack&lt;br /&gt;
    }&#039;)&lt;br /&gt;
&lt;br /&gt;
    # 更新上一轮数据&lt;br /&gt;
    PREV_RX=$CURR_RX&lt;br /&gt;
    PREV_TX=$CURR_TX&lt;br /&gt;
&lt;br /&gt;
    # 打印日志&lt;br /&gt;
    RX_INT=${RX_MB%.*}&lt;br /&gt;
    RX_INT=${RX_INT:-0}&lt;br /&gt;
    if [ &amp;quot;$RX_INT&amp;quot; -ge &amp;quot;$LOG_LIMIT_MB&amp;quot; ]; then&lt;br /&gt;
        echo &amp;quot;[$(date +%T)] RX: $RX_MB MB/s | TX: $TX_MB MB/s&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    # 核心判断逻辑&lt;br /&gt;
    if [ &amp;quot;$IS_HIGH&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$IS_ATTACK&amp;quot; -eq 1 ]; then&lt;br /&gt;
        ((OVERLOAD_COUNT++))&lt;br /&gt;
        echo -e &amp;quot;${RED}[警报 $OVERLOAD_COUNT/$MAX_RETRIES] 流量异常 (RX高 TX低) 疑似攻击！${NC}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$OVERLOAD_COUNT&amp;quot; -ge &amp;quot;$MAX_RETRIES&amp;quot; ]; then&lt;br /&gt;
            echo -e &amp;quot;${RED}!!! 确认攻击，触发黑洞防御 !!!${NC}&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            if [ &amp;quot;$SAFE_MODE&amp;quot; = true ]; then&lt;br /&gt;
                echo -e &amp;quot;${YELLOW}[测试] 安全模式开启，不执行断网。${NC}&amp;quot;&lt;br /&gt;
                OVERLOAD_COUNT=0&lt;br /&gt;
            else&lt;br /&gt;
                # === 执行黑洞 ===&lt;br /&gt;
                echo -e &amp;quot;${RED}正在关闭网卡 [$IFACE]...${NC}&amp;quot;&lt;br /&gt;
                ip link set dev &amp;quot;$IFACE&amp;quot; down&lt;br /&gt;
                &lt;br /&gt;
                echo -e &amp;quot;${YELLOW}网卡已关闭，进入 $BLACKHOLE_TIME 秒静默期...${NC}&amp;quot;&lt;br /&gt;
                sleep &amp;quot;$BLACKHOLE_TIME&amp;quot;&lt;br /&gt;
                &lt;br /&gt;
                echo -e &amp;quot;${GREEN}黑洞结束，正在恢复网卡...${NC}&amp;quot;&lt;br /&gt;
                ip link set dev &amp;quot;$IFACE&amp;quot; up&lt;br /&gt;
                &lt;br /&gt;
                # 等待网络恢复&lt;br /&gt;
                sleep 5&lt;br /&gt;
                &lt;br /&gt;
                # === 关键：重置状态 ===&lt;br /&gt;
                # 网卡重启后计数器会清零，必须重新获取基准值，否则下次计算会出错&lt;br /&gt;
                PREV_RX=$(get_bytes &amp;quot;rx_bytes&amp;quot;)&lt;br /&gt;
                PREV_TX=$(get_bytes &amp;quot;tx_bytes&amp;quot;)&lt;br /&gt;
                OVERLOAD_COUNT=0&lt;br /&gt;
                echo -e &amp;quot;${GREEN}监控已恢复。${NC}&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
    else&lt;br /&gt;
        # 流量正常或恢复&lt;br /&gt;
        if [ &amp;quot;$OVERLOAD_COUNT&amp;quot; -gt 0 ]; then&lt;br /&gt;
            echo -e &amp;quot;${GREEN}流量特征恢复正常，警报解除。${NC}&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
        OVERLOAD_COUNT=0&lt;br /&gt;
    fi&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;bash版本使用&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
一、 前置要求&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;权限&#039;&#039;&#039;：必须以 &#039;&#039;&#039;Root&#039;&#039;&#039; 用户身份运行（脚本需要控制网卡开关）。&lt;br /&gt;
* &#039;&#039;&#039;依赖&#039;&#039;&#039;：脚本依赖 &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;awk&amp;lt;/code&amp;gt; 命令（BandwagonHost 的绝大多数系统已内置，无需额外安装）。&lt;br /&gt;
&lt;br /&gt;
二、 安装脚本&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建存放目录（推荐）&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;mkdir -p /root/scripts&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nano /root/scripts/traffic_guard.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;粘贴代码&#039;&#039;&#039; 将上方的脚本完整复制并粘贴进去，按 &amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; 保存，&amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt; 退出。&lt;br /&gt;
# &#039;&#039;&#039;赋予执行权限&#039;&#039;&#039;  &amp;lt;code&amp;gt;chmod +x /root/scripts/traffic_guard.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
三、 运行方式 &lt;br /&gt;
&lt;br /&gt;
方式 A：Systemd 托管运行（推荐，生产环境标准）&lt;br /&gt;
&lt;br /&gt;
这种方式可以实现&#039;&#039;&#039;开机自启&#039;&#039;&#039;，后台静默运行，且脚本意外退出会自动重启。&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建服务文件&#039;&#039;&#039;  &amp;lt;code&amp;gt;nano /etc/systemd/system/traffic_guard.service&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;填入以下内容&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
[Unit] &lt;br /&gt;
Description=BandwagonHost Traffic Guard Service&lt;br /&gt;
After=network.target&lt;br /&gt;
[Service] &lt;br /&gt;
Type=simple&lt;br /&gt;
# 确保此处的路径与你实际保存的路径一致&lt;br /&gt;
ExecStart=/root/scripts/traffic_guard.sh &lt;br /&gt;
Restart=always&lt;br /&gt;
User=root&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;启动并设置开机自启&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;重载配置文件&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl daemon-reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;设置开机自动启动 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl enable traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;运行此服务 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl start traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.查看运行日志&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki/&amp;gt;#查看实时日志  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;journalctl -u traffic_guard -f&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;方式 B： 临时运行（测试用）&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
如果你不想配置 Systemd，或者想在 SSH 窗口看着它 &amp;lt;code&amp;gt;/root/scripts/traffic_guard.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
go版本&amp;lt;syntaxhighlight lang=&amp;quot;go&amp;quot;&amp;gt;&lt;br /&gt;
package main&lt;br /&gt;
&lt;br /&gt;
import (&lt;br /&gt;
	&amp;quot;fmt&amp;quot;&lt;br /&gt;
	&amp;quot;io/ioutil&amp;quot;&lt;br /&gt;
	&amp;quot;log&amp;quot;&lt;br /&gt;
	&amp;quot;os/exec&amp;quot;&lt;br /&gt;
	&amp;quot;strconv&amp;quot;&lt;br /&gt;
	&amp;quot;strings&amp;quot;&lt;br /&gt;
	&amp;quot;time&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
// ================= 配置区域 =================&lt;br /&gt;
const (&lt;br /&gt;
	TriggerLimitMB  = 50.0  // 入站流量触发阈值 (MB/s)&lt;br /&gt;
	SafeRatio       = 0.4   // 出站/入站 最小安全比例&lt;br /&gt;
	MaxRetries      = 3     // 连续确认次数&lt;br /&gt;
	CheckInterval   = 2     // 检查间隔 (秒)&lt;br /&gt;
	BlackholeTime   = 300   // 黑洞时长 (秒)&lt;br /&gt;
	LogLimitMB      = 5.0   // 日志打印阈值&lt;br /&gt;
	SafeMode        = false // true=仅打印, false=执行操作&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
// ===========================================&lt;br /&gt;
&lt;br /&gt;
var overloadCount = 0&lt;br /&gt;
&lt;br /&gt;
func main() {&lt;br /&gt;
	// 自动获取默认网卡&lt;br /&gt;
	iface, err := getDefaultInterface()&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		log.Fatalf(&amp;quot;无法获取网卡: %v&amp;quot;, err)&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fmt.Printf(&amp;quot;=== Go流量保镖启动 | 网卡: %s | 阈值: %.0fMB/s ===\n&amp;quot;, iface, TriggerLimitMB)&lt;br /&gt;
&lt;br /&gt;
	// 初始化读取&lt;br /&gt;
	prevRx, prevTx := getBytes(iface)&lt;br /&gt;
&lt;br /&gt;
	for {&lt;br /&gt;
		time.Sleep(time.Duration(CheckInterval) * time.Second)&lt;br /&gt;
&lt;br /&gt;
		currRx, currTx := getBytes(iface)&lt;br /&gt;
		&lt;br /&gt;
		// 计算速度 (MB/s)&lt;br /&gt;
		rxSpeed := float64(currRx-prevRx) / 1024 / 1024 / float64(CheckInterval)&lt;br /&gt;
		txSpeed := float64(currTx-prevTx) / 1024 / 1024 / float64(CheckInterval)&lt;br /&gt;
&lt;br /&gt;
		// 防止负数（网卡重置后可能出现）&lt;br /&gt;
		if rxSpeed &amp;lt; 0 { rxSpeed = 0 }&lt;br /&gt;
		if txSpeed &amp;lt; 0 { txSpeed = 0 }&lt;br /&gt;
&lt;br /&gt;
		// 更新基准&lt;br /&gt;
		prevRx = currRx&lt;br /&gt;
		prevTx = currTx&lt;br /&gt;
&lt;br /&gt;
		// 日志输出&lt;br /&gt;
		if rxSpeed &amp;gt; LogLimitMB {&lt;br /&gt;
			log.Printf(&amp;quot;[流量] RX: %.2f MB/s | TX: %.2f MB/s\n&amp;quot;, rxSpeed, txSpeed)&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// 判断逻辑&lt;br /&gt;
		isHighTraffic := rxSpeed &amp;gt; TriggerLimitMB&lt;br /&gt;
		isAttackPattern := txSpeed &amp;lt; (rxSpeed * SafeRatio)&lt;br /&gt;
&lt;br /&gt;
		if isHighTraffic &amp;amp;&amp;amp; isAttackPattern {&lt;br /&gt;
			overloadCount++&lt;br /&gt;
			fmt.Printf(&amp;quot;\033[31m[警报 %d/%d] 异常流量检测! RX: %.2f, TX: %.2f\033[0m\n&amp;quot;, overloadCount, MaxRetries, rxSpeed, txSpeed)&lt;br /&gt;
&lt;br /&gt;
			if overloadCount &amp;gt;= MaxRetries {&lt;br /&gt;
				triggerBlackhole(iface)&lt;br /&gt;
				// 黑洞结束后，重置计数器和基准值&lt;br /&gt;
				overloadCount = 0&lt;br /&gt;
				prevRx, prevTx = getBytes(iface)&lt;br /&gt;
			}&lt;br /&gt;
		} else {&lt;br /&gt;
			if overloadCount &amp;gt; 0 {&lt;br /&gt;
				fmt.Println(&amp;quot;\033[32m流量恢复正常，警报解除。\033[0m&amp;quot;)&lt;br /&gt;
			}&lt;br /&gt;
			overloadCount = 0&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 执行黑洞逻辑&lt;br /&gt;
func triggerBlackhole(iface string) {&lt;br /&gt;
	fmt.Println(&amp;quot;\033[31m!!! 确认攻击，正在执行黑洞防御 !!!\033[0m&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	if SafeMode {&lt;br /&gt;
		fmt.Println(&amp;quot;[安全模式] 模拟执行：关闭网卡...&amp;quot;)&lt;br /&gt;
		time.Sleep(2 * time.Second)&lt;br /&gt;
		fmt.Println(&amp;quot;[安全模式] 模拟执行：开启网卡...&amp;quot;)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 关闭网卡&lt;br /&gt;
	log.Printf(&amp;quot;正在关闭网卡 %s ...&amp;quot;, iface)&lt;br /&gt;
	if err := exec.Command(&amp;quot;ip&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;set&amp;quot;, &amp;quot;dev&amp;quot;, iface, &amp;quot;down&amp;quot;).Run(); err != nil {&lt;br /&gt;
		log.Printf(&amp;quot;关闭网卡失败: %v&amp;quot;, err)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	log.Printf(&amp;quot;网卡已关闭，等待 %d 秒...&amp;quot;, BlackholeTime)&lt;br /&gt;
	time.Sleep(time.Duration(BlackholeTime) * time.Second)&lt;br /&gt;
&lt;br /&gt;
	// 开启网卡&lt;br /&gt;
	log.Printf(&amp;quot;正在恢复网卡 %s ...&amp;quot;, iface)&lt;br /&gt;
	if err := exec.Command(&amp;quot;ip&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;set&amp;quot;, &amp;quot;dev&amp;quot;, iface, &amp;quot;up&amp;quot;).Run(); err != nil {&lt;br /&gt;
		log.Printf(&amp;quot;CRITICAL: 恢复网卡失败，请手动处理! Error: %v&amp;quot;, err)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	// 给一点时间让网络重新协商&lt;br /&gt;
	time.Sleep(5 * time.Second)&lt;br /&gt;
	log.Println(&amp;quot;网络已恢复，重新开始监控。&amp;quot;)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 读取系统文件获取流量字节&lt;br /&gt;
func getBytes(iface string) (uint64, uint64) {&lt;br /&gt;
	rxPath := fmt.Sprintf(&amp;quot;/sys/class/net/%s/statistics/rx_bytes&amp;quot;, iface)&lt;br /&gt;
	txPath := fmt.Sprintf(&amp;quot;/sys/class/net/%s/statistics/tx_bytes&amp;quot;, iface)&lt;br /&gt;
&lt;br /&gt;
	rx, _ := readUint64(rxPath)&lt;br /&gt;
	tx, _ := readUint64(txPath)&lt;br /&gt;
	return rx, tx&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
func readUint64(path string) (uint64, error) {&lt;br /&gt;
	data, err := ioutil.ReadFile(path)&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		return 0, err&lt;br /&gt;
	}&lt;br /&gt;
	return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 简单的获取默认网卡方法&lt;br /&gt;
func getDefaultInterface() (string, error) {&lt;br /&gt;
	out, err := exec.Command(&amp;quot;sh&amp;quot;, &amp;quot;-c&amp;quot;, &amp;quot;ip route get 8.8.8.8 | awk &#039;{print $5; exit}&#039;&amp;quot;).Output()&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		return &amp;quot;&amp;quot;, err&lt;br /&gt;
	}&lt;br /&gt;
	return strings.TrimSpace(string(out)), nil&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;一、环境准备&#039;&#039;&#039;&lt;br /&gt;
在编译运行之前，你需要先在 VPS 上安装 Go 语言环境。&lt;br /&gt;
&#039;&#039;&#039;Debian / Ubuntu:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;apt update &amp;amp;&amp;amp; apt install golang -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CentOS / AlmaLinux:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;yum install golang -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
验证安装是否成功：&lt;br /&gt;
&lt;br /&gt;
go version &lt;br /&gt;
&lt;br /&gt;
输出类似 go version go1.x.x linux/amd64 即为成功&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;二、 编译与安装&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建文件&#039;&#039;&#039; 创建一个名为 &amp;lt;code&amp;gt;traffic_guard.go&amp;lt;/code&amp;gt; 的文件，并将上方的代码完整粘贴进去。Bash  &amp;lt;code&amp;gt;nano traffic_guard.go&amp;lt;/code&amp;gt;  &#039;&#039;(粘贴代码后，按 &amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; 保存，&amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt; 退出)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;修改配置（可选）&#039;&#039;&#039; 代码顶部的 &amp;lt;code&amp;gt;const&amp;lt;/code&amp;gt; 区域是配置项。&lt;br /&gt;
#* &amp;lt;code&amp;gt;TriggerLimitMB&amp;lt;/code&amp;gt;: 触发阈值（默认 50MB/s）。&lt;br /&gt;
#* &amp;lt;code&amp;gt;SafeMode&amp;lt;/code&amp;gt;: 如果设为 &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;，只会报警不会真断网（适合测试）。&lt;br /&gt;
# &#039;&#039;&#039;编译生成可执行文件&#039;&#039;&#039;  &amp;lt;code&amp;gt;go build -o traffic_guard traffic_guard.go&amp;lt;/code&amp;gt;  &#039;&#039;此时你会发现当前目录下多了一个名为 &amp;lt;code&amp;gt;traffic_guard&amp;lt;/code&amp;gt; 的可执行文件。&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;赋予权限并移动&#039;&#039;&#039;  &amp;lt;code&amp;gt;chmod +x traffic_guard  mv traffic_guard /root/traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;三、运行方式&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;方式 A：Systemd 托管运行（强烈推荐）&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
这是最稳健的方式，支持开机自启和进程守护。&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建服务文件&#039;&#039;&#039;  &amp;lt;code&amp;gt;nano /etc/systemd/system/traffic_guard.service&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;填入以下内容&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=BandwagonHost Traffic Guard (Go Version)&lt;br /&gt;
After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
# 确保这里的路径是你实际存放二进制文件的路径&lt;br /&gt;
ExecStart=/root/traffic_guard&lt;br /&gt;
Restart=always&lt;br /&gt;
User=root&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;3.&#039;&#039;&#039;启动并设置开机自启&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;重载配置文件&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl daemon-reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;设置开机自动启动 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl enable traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;运行此服务 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl start traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.查看状态与日志&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;查看运行状态 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl status traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;查看实时日志&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;journalctl -u traffic_guard -f&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;方式 B：临时测试运行&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
如果你只是想看看它能不能正常工作：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;./traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(注意：必须以 Root 身份运行，否则无法控制网卡)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
💡 &#039;&#039;&#039;常见问题 (FAQ)&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Q: 编译时报错 &amp;lt;code&amp;gt;command not found&amp;lt;/code&amp;gt; 怎么办？&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;A:&#039;&#039;&#039; 说明 Go 环境没装好，请重新执行 &amp;lt;code&amp;gt;apt install golang&amp;lt;/code&amp;gt;。如果 VPS 系统太老源里没有 Go，建议使用 Bash 版本脚本。&lt;br /&gt;
* &#039;&#039;&#039;Q: 程序报错 &amp;lt;code&amp;gt;panic: 无法获取网卡&amp;lt;/code&amp;gt;？&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;A:&#039;&#039;&#039; 程序会自动尝试检测默认网卡。如果检测失败，请检查你的 VPS 是否有特殊的网络配置，或者尝试手动修改代码中的 &amp;lt;code&amp;gt;getDefaultInterface&amp;lt;/code&amp;gt; 函数，直接返回字符串 &amp;lt;code&amp;gt;&amp;quot;eth0&amp;quot;&amp;lt;/code&amp;gt;。&lt;br /&gt;
**&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;关键配置说明&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;如何测试脚本是否有效？&#039;&#039;&#039; 建议将脚本中的 &amp;lt;code&amp;gt;SAFE_MODE=false&amp;lt;/code&amp;gt; 改为 &amp;lt;code&amp;gt;SAFE_MODE=true&amp;lt;/code&amp;gt;。 这样当流量超标时，脚本只会输出红色警报文字，而不会真的断网。确认触发逻辑正常后，再改回 &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; 。&lt;br /&gt;
* &#039;&#039;&#039;触发黑洞断网后，我会彻底失联吗？&#039;&#039;&#039;  不会。黑洞防御只是暂时关闭网卡，您可以通过以下三种方式恢复：&lt;br /&gt;
** &#039;&#039;&#039;方法 1：等待自动恢复&#039;&#039;&#039; 耐心等待 &amp;lt;code&amp;gt;BLACKHOLE_TIME&amp;lt;/code&amp;gt;（默认 300秒/5分钟）结束，脚本会自动重新开启网卡，无需任何操作。&lt;br /&gt;
** &#039;&#039;&#039;方法 2：通过 KiwiVM VNC 恢复&#039;&#039;&#039; 登录 KiwiVM 面板，打开 &#039;&#039;&#039;VNC Console&#039;&#039;&#039;（类似于物理显示器，不受网卡关闭影响）。在 VNC 窗口中按 &amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt; 终止脚本，然后输入 &amp;lt;code&amp;gt;ip link set dev eth0 up&amp;lt;/code&amp;gt; 手动恢复网络。&lt;br /&gt;
** &#039;&#039;&#039;方法 3：重启服务器&#039;&#039;&#039; 如果以上方法您都不会操作，可以直接在 KiwiVM 面板点击 &#039;&#039;&#039;Reset&#039;&#039;&#039; 或 &#039;&#039;&#039;Force Stop&#039;&#039;&#039; 后再 &#039;&#039;&#039;Start&#039;&#039;&#039; 重启服务器。重启后网卡会默认恢复连通状态。（注意：这会导致您未保存的数据丢失或服务短暂中断）。&lt;br /&gt;
&lt;br /&gt;
== 六、 长期预案：建立高可用的 VPS 使用环境 ==&lt;br /&gt;
&lt;br /&gt;
防御 DDoS 攻击是一项长期工作。通过完善的预案体系和日常的加固措施，可以将潜在的停机风险降到最低。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;安全预防建议：&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 双机备份方案：建议准备一台速度快的线路（如 香港CN2 GIA 或 日本东京CN2 GIA）作为直连主力机，同时准备一台便宜的KVM常规套餐作为备份服务器。一旦主力机被黑洞，立即切换解析到备份机并开启 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 防御。&lt;br /&gt;
* &lt;br /&gt;
* 系统加固与监控：日常运维中应当修改SSH端口，定期通过 &amp;lt;code&amp;gt;Audit Log&amp;lt;/code&amp;gt; 观察异常操作，并确保开启了&#039;&#039;&#039;搬瓦工自动备份&#039;&#039;&#039;功能以应对极端情况。&lt;br /&gt;
* &lt;br /&gt;
* 资源合理分配：若业务量增长明显，建议及时&#039;&#039;&#039;升级套餐&#039;&#039;&#039;。如果 IP 已被打残无法解封，可咨询&#039;&#039;&#039;搬瓦工客服&#039;&#039;&#039;关于&#039;&#039;&#039;购买IP&#039;&#039;&#039;的具体事宜或申请 退款。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;注意事项&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 当 IP 处于空路由状态时，所有的 &amp;lt;code&amp;gt;SSH&amp;lt;/code&amp;gt; 工具（如 &amp;lt;code&amp;gt;Xshell&amp;lt;/code&amp;gt;、&amp;lt;code&amp;gt;Xftp&amp;lt;/code&amp;gt;）均无法连接，请耐心等待 1800 秒解封。&lt;br /&gt;
* &lt;br /&gt;
* 若服务器因 CPU 占用过高被暂停（Suspended），处理流程会有所不同。请务必确认失联原因属于流量攻击而非资源超载。&lt;br /&gt;
&lt;br /&gt;
[[index.php?title=Category:400 常见问题与故障排查 — Troubleshooting]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E6%90%AC%E7%93%A6%E5%B7%A5_VPS_%E9%81%AD%E9%81%87_DDoS_%E6%94%BB%E5%87%BB%E6%B7%B1%E5%BA%A6%E6%8E%92%E9%9A%9C%E4%B8%8E%E8%87%AA%E6%95%91%E6%89%8B%E5%86%8C&amp;diff=1174</id>
		<title>搬瓦工 VPS 遭遇 DDoS 攻击深度排障与自救手册</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E6%90%AC%E7%93%A6%E5%B7%A5_VPS_%E9%81%AD%E9%81%87_DDoS_%E6%94%BB%E5%87%BB%E6%B7%B1%E5%BA%A6%E6%8E%92%E9%9A%9C%E4%B8%8E%E8%87%AA%E6%95%91%E6%89%8B%E5%86%8C&amp;diff=1174"/>
		<updated>2026-01-25T10:13:06Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​增加一个 自动黑洞5分钟的脚本&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;面对搬瓦工 VPS 突然全网失联且无法 &amp;lt;code&amp;gt;Ping&amp;lt;/code&amp;gt; 通的情况，需要结合实际运维经验，深入剖析 DDoS 攻击下的空路由（Nullrouted）触发机制，并建立一套从精准判定到彻底自救的实战方案。&lt;br /&gt;
&lt;br /&gt;
== 一、 为什么我的搬瓦工服务器会突然“消失”？ ==&lt;br /&gt;
&lt;br /&gt;
搬瓦工绝大多数套餐方案在设计上更偏向于提供高性能的底层架构，而非自带硬件级 DDoS 防御。这意味着当特定 IP 遭遇大规模恶意请求时，机房会采取自动化保护措施以确保整体链路的稳定。&lt;br /&gt;
&lt;br /&gt;
* 空路由（Nullrouted）机制：当流量超过阈值，系统会将指向该 IP 的所有数据包丢弃，形成所谓的“黑洞”。此时网站无法打开、&amp;lt;code&amp;gt;SSH&amp;lt;/code&amp;gt; 无法登陆、&amp;lt;code&amp;gt;Ping&amp;lt;/code&amp;gt; 测试也会完全失效。&lt;br /&gt;
* &lt;br /&gt;
* 1800 秒封禁周期：触发空路由后，默认的屏蔽时长通常为 &amp;lt;code&amp;gt;1800 秒&amp;lt;/code&amp;gt;（30 分钟）。如果封禁解除后攻击仍在继续，屏蔽时间会循环延长，导致服务器反复失联。&lt;br /&gt;
&lt;br /&gt;
== 二、 如何确认自己的服务器正处于 DDoS 攻击中？ ==&lt;br /&gt;
&lt;br /&gt;
在排查网络故障时，首先需要区分是软件配置错误还是受到了流量攻击。通过以下数据特征可以快速锁定故障性质。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. 查看系统邮件通知&#039;&#039;&#039; 当 IP 触发空路由时，系统会自动向注册邮箱发送一封包含 &amp;lt;code&amp;gt;is currently under a (D)DoS attack&amp;lt;/code&amp;gt; 的邮件，明确告知 IP 已被暂时屏蔽。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. 分析流量特征曲线&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
在排查过程中，通过图形化的数据回馈能最直观地捕捉攻击痕迹。相比于命令行，KiwiVM 后台的统计图表能更清晰地展现出入站流量的异常脉络。&lt;br /&gt;
&lt;br /&gt;
:* 进入路径：登录 &amp;lt;code&amp;gt;KiwiVM&amp;lt;/code&amp;gt; 管理后台，在左侧 &amp;lt;code&amp;gt;Security &amp;amp; Records&amp;lt;/code&amp;gt; 栏目下点击 &amp;lt;code&amp;gt;Detailed statistics&amp;lt;/code&amp;gt; 页面。&lt;br /&gt;
&lt;br /&gt;
:* 观察指标：在右侧详情页中，重点观察 &amp;lt;code&amp;gt;Network I/O (Bits per second)&amp;lt;/code&amp;gt; 图表。&lt;br /&gt;
&lt;br /&gt;
:* 黑洞特征判定： &lt;br /&gt;
::* 流量尖峰：如果图表中出现如绿色阴影所示的垂直状极高尖峰，说明该时段遭受了大规模入站流量冲击。 &lt;br /&gt;
&lt;br /&gt;
::* 断崖式归零：在尖峰之后，如果流量瞬间掉落并呈水平直线（接近 &amp;lt;code&amp;gt;0 bps&amp;lt;/code&amp;gt;）持续延伸，说明 &amp;lt;code&amp;gt;IP&amp;lt;/code&amp;gt; 已被系统自动放入黑洞（空路由）进行清洗。&lt;br /&gt;
&lt;br /&gt;
[[File:kiwivm-detailed-statistics-ddos-traffic.png|thumb|center|800px|alt=KiwiVM 详细统计页面显示 DDoS 攻击导致的流量断崖|通过 &amp;lt;code&amp;gt;Detailed statistics&amp;lt;/code&amp;gt; 中的流量断层判定 &amp;lt;code&amp;gt;IP&amp;lt;/code&amp;gt; 是否被封禁]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3. 状态矛盾核对&#039;&#039;&#039; 如果控制面板显示服务器状态为 Running 且未执行 重装系统 或主动关闭服务，但外部一切连接手段均失效，基本可以判定为遭受了 DDoS 攻击。&lt;br /&gt;
&lt;br /&gt;
== 三、 应急方案：利用 Cloudflare 建立前端防线 ==&lt;br /&gt;
&lt;br /&gt;
由于搬瓦工普通套餐无法提供高防线路，利用 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 的全球节点接住流量是目前成本最低且效果最好的自救方式。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. 接入与隐藏逻辑&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
通过将域名 &amp;lt;code&amp;gt;DNS&amp;lt;/code&amp;gt; 托管至 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 并点亮橙色云朵图标（代理模式），访客的请求会先打到防护节点而非搬瓦工源站。这种方式能有效隐藏真实 IP，屏蔽掉大量的扫描与流量攻击。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. 防护策略加固&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 开启攻击模式：在攻击期间开启 &amp;lt;code&amp;gt;Under Attack Mode&amp;lt;/code&amp;gt;，强制访客进行 5 秒安全验证。&lt;br /&gt;
* &lt;br /&gt;
* 防火墙规则 (WAF)：在后台针对异常频率的请求或特定国家/地区设置拦截规则。&lt;br /&gt;
* &lt;br /&gt;
* 优化缓存设置：适当提高静态资源的缓存比例，减少回源请求，减轻服务器的 &amp;lt;code&amp;gt;CPU&amp;lt;/code&amp;gt; 与网络压力。&lt;br /&gt;
&lt;br /&gt;
== 四、 利用快照功能实现数据迁移与 IP 更换 ==&lt;br /&gt;
&lt;br /&gt;
如果攻击方持续对特定 IP 进行攻击，即使配置了 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt;，旧 IP 一旦解封仍可能被再次打进黑洞。此时，利用快照功能进行业务迁移是更为彻底的办法。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;数据迁移与环境恢复流程：&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 制作快照：在 KiwiVM 界面进入 &amp;lt;code&amp;gt;Snapshots&amp;lt;/code&amp;gt; 页面，为当前受攻击的服务器做一个全量 备份。&lt;br /&gt;
* &lt;br /&gt;
* 快照还原与迁移：利用&#039;&#039;&#039;搬瓦工的快照&#039;&#039;&#039;功能，可以新建一台同配置的 VPS 并还原该镜像。或者通过面板功能进行&#039;&#039;&#039;更换机房&#039;&#039;&#039;来获取全新的 IP 地址。&lt;br /&gt;
* &lt;br /&gt;
* 解析同步更新：在获取新 IP 后，只需在 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 后台修改 &amp;lt;code&amp;gt;A 记录&amp;lt;/code&amp;gt;，新 IP 会在几秒钟内生效，从而切断旧 IP 的受攻击链路。&lt;br /&gt;
&lt;br /&gt;
== 五、遇DDoS自动断开网卡5分钟 ==&lt;br /&gt;
检测DDoS就自动断网5分钟&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;如果你的服务器只入 不出的话..那么这个脚本就不适合你了&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
可修改的参数&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 1.触发检查的起步阈值 (MB/s) 这个可以填写你的最大带宽 注意单位是MB&lt;br /&gt;
&lt;br /&gt;
TRIGGER_LIMIT_MB=50&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 2.出站流量比例因子 (0.0 - 1.0) &lt;br /&gt;
&lt;br /&gt;
如果流出的流量比进入的流量*设置的值(默认是0.4)就认为发生了ddos &lt;br /&gt;
&lt;br /&gt;
如果你的服务器只入 不出的话..那么这个脚本就不适合你了&lt;br /&gt;
&lt;br /&gt;
如果 (出站 TX) &amp;lt; (入站 RX * 因子)，则判定为攻击。&lt;br /&gt;
&lt;br /&gt;
SAFE_RATIO=0.4&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 3. 连续确认次数 防止误报&lt;br /&gt;
&lt;br /&gt;
MAX_RETRIES=3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 4. 检查间隔 (秒)&lt;br /&gt;
&lt;br /&gt;
CHECK_INTERVAL=2&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 5. 黑洞时长 (秒) - 300秒 = 5分钟 断网时长..&lt;br /&gt;
&lt;br /&gt;
BLACKHOLE_TIME=300&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 6. 安全模式 (true=只报警不操作, false=执行断网)&lt;br /&gt;
&lt;br /&gt;
SAFE_MODE=false&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; 7. 日志显示阈值 (MB/s) 只有高过这个阈值才有显示出来的文本 就是 一些流入和 流出 数据计算的值&lt;br /&gt;
&lt;br /&gt;
LOG_LIMIT_MB=5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bash脚本版本&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
# ================= 用户配置区域 =================&lt;br /&gt;
&lt;br /&gt;
# 1. 触发检查的起步阈值 (MB/s)&lt;br /&gt;
TRIGGER_LIMIT_MB=50&lt;br /&gt;
&lt;br /&gt;
# 2. 出站流量比例因子 (0.0 - 1.0)&lt;br /&gt;
# 如果 (出站 TX) &amp;lt; (入站 RX * 因子)，则判定为攻击。&lt;br /&gt;
SAFE_RATIO=0.4&lt;br /&gt;
&lt;br /&gt;
# 3. 连续确认次数&lt;br /&gt;
MAX_RETRIES=3&lt;br /&gt;
&lt;br /&gt;
# 4. 检查间隔 (秒)&lt;br /&gt;
CHECK_INTERVAL=2&lt;br /&gt;
&lt;br /&gt;
# 5. 黑洞时长 (秒) - 300秒 = 5分钟&lt;br /&gt;
BLACKHOLE_TIME=300&lt;br /&gt;
&lt;br /&gt;
# 6. 安全模式 (true=只报警不操作, false=执行断网)&lt;br /&gt;
SAFE_MODE=false&lt;br /&gt;
&lt;br /&gt;
# 7. 日志显示阈值 (MB/s)&lt;br /&gt;
LOG_LIMIT_MB=5&lt;br /&gt;
&lt;br /&gt;
# ===============================================&lt;br /&gt;
&lt;br /&gt;
# 自动获取默认网卡&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | awk &#039;{print $5; exit}&#039;)&lt;br /&gt;
OVERLOAD_COUNT=0&lt;br /&gt;
&lt;br /&gt;
# 颜色定义&lt;br /&gt;
RED=&#039;\033[0;31m&#039;&lt;br /&gt;
GREEN=&#039;\033[0;32m&#039;&lt;br /&gt;
YELLOW=&#039;\033[0;33m&#039;&lt;br /&gt;
NC=&#039;\033[0m&#039;&lt;br /&gt;
&lt;br /&gt;
echo -e &amp;quot;${GREEN}=== 智能流量保镖 v3.0 (黑洞版) 启动 ===${NC}&amp;quot;&lt;br /&gt;
echo &amp;quot;监控网卡: $IFACE&amp;quot;&lt;br /&gt;
echo &amp;quot;黑洞时长: $BLACKHOLE_TIME 秒&amp;quot;&lt;br /&gt;
echo &amp;quot;安全模式: $SAFE_MODE&amp;quot;&lt;br /&gt;
echo &amp;quot;========================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
get_bytes() {&lt;br /&gt;
    cat &amp;quot;/sys/class/net/$IFACE/statistics/$1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# 初始化读数&lt;br /&gt;
PREV_RX=$(get_bytes &amp;quot;rx_bytes&amp;quot;)&lt;br /&gt;
PREV_TX=$(get_bytes &amp;quot;tx_bytes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
while true; do&lt;br /&gt;
    sleep $CHECK_INTERVAL&lt;br /&gt;
&lt;br /&gt;
    CURR_RX=$(get_bytes &amp;quot;rx_bytes&amp;quot;)&lt;br /&gt;
    CURR_TX=$(get_bytes &amp;quot;tx_bytes&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # 使用 awk 计算速度和逻辑&lt;br /&gt;
    read RX_MB TX_MB IS_HIGH IS_ATTACK &amp;lt;&amp;lt;&amp;lt; $(awk -v r1=$PREV_RX -v r2=$CURR_RX \&lt;br /&gt;
                                                  -v t1=$PREV_TX -v t2=$CURR_TX \&lt;br /&gt;
                                                  -v time=$CHECK_INTERVAL \&lt;br /&gt;
                                                  -v limit=$TRIGGER_LIMIT_MB \&lt;br /&gt;
                                                  -v ratio=$SAFE_RATIO &#039;&lt;br /&gt;
    BEGIN {&lt;br /&gt;
        delta_rx = r2 - r1&lt;br /&gt;
        delta_tx = t2 - t1&lt;br /&gt;
        # 防止负数 (重启网卡后可能发生)&lt;br /&gt;
        if (delta_rx &amp;lt; 0) delta_rx = 0&lt;br /&gt;
        if (delta_tx &amp;lt; 0) delta_tx = 0&lt;br /&gt;
        &lt;br /&gt;
        rx_speed = delta_rx / 1024 / 1024 / time&lt;br /&gt;
        tx_speed = delta_tx / 1024 / 1024 / time&lt;br /&gt;
&lt;br /&gt;
        is_high = (rx_speed &amp;gt; limit) ? 1 : 0&lt;br /&gt;
        limit_tx = rx_speed * ratio&lt;br /&gt;
        is_attack = (tx_speed &amp;lt; limit_tx) ? 1 : 0&lt;br /&gt;
&lt;br /&gt;
        printf &amp;quot;%.2f %.2f %d %d&amp;quot;, rx_speed, tx_speed, is_high, is_attack&lt;br /&gt;
    }&#039;)&lt;br /&gt;
&lt;br /&gt;
    # 更新上一轮数据&lt;br /&gt;
    PREV_RX=$CURR_RX&lt;br /&gt;
    PREV_TX=$CURR_TX&lt;br /&gt;
&lt;br /&gt;
    # 打印日志&lt;br /&gt;
    RX_INT=${RX_MB%.*}&lt;br /&gt;
    RX_INT=${RX_INT:-0}&lt;br /&gt;
    if [ &amp;quot;$RX_INT&amp;quot; -ge &amp;quot;$LOG_LIMIT_MB&amp;quot; ]; then&lt;br /&gt;
        echo &amp;quot;[$(date +%T)] RX: $RX_MB MB/s | TX: $TX_MB MB/s&amp;quot;&lt;br /&gt;
    fi&lt;br /&gt;
&lt;br /&gt;
    # 核心判断逻辑&lt;br /&gt;
    if [ &amp;quot;$IS_HIGH&amp;quot; -eq 1 ] &amp;amp;&amp;amp; [ &amp;quot;$IS_ATTACK&amp;quot; -eq 1 ]; then&lt;br /&gt;
        ((OVERLOAD_COUNT++))&lt;br /&gt;
        echo -e &amp;quot;${RED}[警报 $OVERLOAD_COUNT/$MAX_RETRIES] 流量异常 (RX高 TX低) 疑似攻击！${NC}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        if [ &amp;quot;$OVERLOAD_COUNT&amp;quot; -ge &amp;quot;$MAX_RETRIES&amp;quot; ]; then&lt;br /&gt;
            echo -e &amp;quot;${RED}!!! 确认攻击，触发黑洞防御 !!!${NC}&amp;quot;&lt;br /&gt;
            &lt;br /&gt;
            if [ &amp;quot;$SAFE_MODE&amp;quot; = true ]; then&lt;br /&gt;
                echo -e &amp;quot;${YELLOW}[测试] 安全模式开启，不执行断网。${NC}&amp;quot;&lt;br /&gt;
                OVERLOAD_COUNT=0&lt;br /&gt;
            else&lt;br /&gt;
                # === 执行黑洞 ===&lt;br /&gt;
                echo -e &amp;quot;${RED}正在关闭网卡 [$IFACE]...${NC}&amp;quot;&lt;br /&gt;
                ip link set dev &amp;quot;$IFACE&amp;quot; down&lt;br /&gt;
                &lt;br /&gt;
                echo -e &amp;quot;${YELLOW}网卡已关闭，进入 $BLACKHOLE_TIME 秒静默期...${NC}&amp;quot;&lt;br /&gt;
                sleep &amp;quot;$BLACKHOLE_TIME&amp;quot;&lt;br /&gt;
                &lt;br /&gt;
                echo -e &amp;quot;${GREEN}黑洞结束，正在恢复网卡...${NC}&amp;quot;&lt;br /&gt;
                ip link set dev &amp;quot;$IFACE&amp;quot; up&lt;br /&gt;
                &lt;br /&gt;
                # 等待网络恢复&lt;br /&gt;
                sleep 5&lt;br /&gt;
                &lt;br /&gt;
                # === 关键：重置状态 ===&lt;br /&gt;
                # 网卡重启后计数器会清零，必须重新获取基准值，否则下次计算会出错&lt;br /&gt;
                PREV_RX=$(get_bytes &amp;quot;rx_bytes&amp;quot;)&lt;br /&gt;
                PREV_TX=$(get_bytes &amp;quot;tx_bytes&amp;quot;)&lt;br /&gt;
                OVERLOAD_COUNT=0&lt;br /&gt;
                echo -e &amp;quot;${GREEN}监控已恢复。${NC}&amp;quot;&lt;br /&gt;
            fi&lt;br /&gt;
        fi&lt;br /&gt;
    else&lt;br /&gt;
        # 流量正常或恢复&lt;br /&gt;
        if [ &amp;quot;$OVERLOAD_COUNT&amp;quot; -gt 0 ]; then&lt;br /&gt;
            echo -e &amp;quot;${GREEN}流量特征恢复正常，警报解除。${NC}&amp;quot;&lt;br /&gt;
        fi&lt;br /&gt;
        OVERLOAD_COUNT=0&lt;br /&gt;
    fi&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;bash版本使用&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
一、 前置要求&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;权限&#039;&#039;&#039;：必须以 &#039;&#039;&#039;Root&#039;&#039;&#039; 用户身份运行（脚本需要控制网卡开关）。&lt;br /&gt;
* &#039;&#039;&#039;依赖&#039;&#039;&#039;：脚本依赖 &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; 和 &amp;lt;code&amp;gt;awk&amp;lt;/code&amp;gt; 命令（BandwagonHost 的绝大多数系统已内置，无需额外安装）。&lt;br /&gt;
&lt;br /&gt;
二、 安装脚本&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建存放目录（推荐）&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;mkdir -p /root/scripts&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nano /root/scripts/traffic_guard.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;粘贴代码&#039;&#039;&#039; 将上方的脚本完整复制并粘贴进去，按 &amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; 保存，&amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt; 退出。&lt;br /&gt;
# &#039;&#039;&#039;赋予执行权限&#039;&#039;&#039;  &amp;lt;code&amp;gt;chmod +x /root/scripts/traffic_guard.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
三、 运行方式 &lt;br /&gt;
&lt;br /&gt;
方式 A：Systemd 托管运行（推荐，生产环境标准）&lt;br /&gt;
&lt;br /&gt;
这种方式可以实现&#039;&#039;&#039;开机自启&#039;&#039;&#039;，后台静默运行，且脚本意外退出会自动重启。&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建服务文件&#039;&#039;&#039;  &amp;lt;code&amp;gt;nano /etc/systemd/system/traffic_guard.service&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;填入以下内容&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
[Unit] Description=BandwagonHost Traffic Guard Service After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service] Type=simple&lt;br /&gt;
&lt;br /&gt;
# 确保此处的路径与你实际保存的路径一致&lt;br /&gt;
&lt;br /&gt;
ExecStart=/root/scripts/traffic_guard.sh Restart=always User=root&lt;br /&gt;
&lt;br /&gt;
[Install] WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;启动并设置开机自启&#039;&#039;&#039;  #重载配置文件&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl daemon-reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;设置开机自动启动 &amp;lt;code&amp;gt;systemctl enable traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;运行此服务 &amp;lt;code&amp;gt;systemctl start traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;查看运行日志&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;# 查看实时日志&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;journalctl -u traffic_guard -f&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
方式 B： 临时运行（测试用）&lt;br /&gt;
&lt;br /&gt;
如果你不想配置 Systemd，或者想在 SSH 窗口看着它 &amp;lt;code&amp;gt;/root/scripts/traffic_guard.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
go版本&amp;lt;syntaxhighlight lang=&amp;quot;go&amp;quot;&amp;gt;&lt;br /&gt;
package main&lt;br /&gt;
&lt;br /&gt;
import (&lt;br /&gt;
	&amp;quot;fmt&amp;quot;&lt;br /&gt;
	&amp;quot;io/ioutil&amp;quot;&lt;br /&gt;
	&amp;quot;log&amp;quot;&lt;br /&gt;
	&amp;quot;os/exec&amp;quot;&lt;br /&gt;
	&amp;quot;strconv&amp;quot;&lt;br /&gt;
	&amp;quot;strings&amp;quot;&lt;br /&gt;
	&amp;quot;time&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
// ================= 配置区域 =================&lt;br /&gt;
const (&lt;br /&gt;
	TriggerLimitMB  = 50.0  // 入站流量触发阈值 (MB/s)&lt;br /&gt;
	SafeRatio       = 0.4   // 出站/入站 最小安全比例&lt;br /&gt;
	MaxRetries      = 3     // 连续确认次数&lt;br /&gt;
	CheckInterval   = 2     // 检查间隔 (秒)&lt;br /&gt;
	BlackholeTime   = 300   // 黑洞时长 (秒)&lt;br /&gt;
	LogLimitMB      = 5.0   // 日志打印阈值&lt;br /&gt;
	SafeMode        = false // true=仅打印, false=执行操作&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
// ===========================================&lt;br /&gt;
&lt;br /&gt;
var overloadCount = 0&lt;br /&gt;
&lt;br /&gt;
func main() {&lt;br /&gt;
	// 自动获取默认网卡&lt;br /&gt;
	iface, err := getDefaultInterface()&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		log.Fatalf(&amp;quot;无法获取网卡: %v&amp;quot;, err)&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	fmt.Printf(&amp;quot;=== Go流量保镖启动 | 网卡: %s | 阈值: %.0fMB/s ===\n&amp;quot;, iface, TriggerLimitMB)&lt;br /&gt;
&lt;br /&gt;
	// 初始化读取&lt;br /&gt;
	prevRx, prevTx := getBytes(iface)&lt;br /&gt;
&lt;br /&gt;
	for {&lt;br /&gt;
		time.Sleep(time.Duration(CheckInterval) * time.Second)&lt;br /&gt;
&lt;br /&gt;
		currRx, currTx := getBytes(iface)&lt;br /&gt;
		&lt;br /&gt;
		// 计算速度 (MB/s)&lt;br /&gt;
		rxSpeed := float64(currRx-prevRx) / 1024 / 1024 / float64(CheckInterval)&lt;br /&gt;
		txSpeed := float64(currTx-prevTx) / 1024 / 1024 / float64(CheckInterval)&lt;br /&gt;
&lt;br /&gt;
		// 防止负数（网卡重置后可能出现）&lt;br /&gt;
		if rxSpeed &amp;lt; 0 { rxSpeed = 0 }&lt;br /&gt;
		if txSpeed &amp;lt; 0 { txSpeed = 0 }&lt;br /&gt;
&lt;br /&gt;
		// 更新基准&lt;br /&gt;
		prevRx = currRx&lt;br /&gt;
		prevTx = currTx&lt;br /&gt;
&lt;br /&gt;
		// 日志输出&lt;br /&gt;
		if rxSpeed &amp;gt; LogLimitMB {&lt;br /&gt;
			log.Printf(&amp;quot;[流量] RX: %.2f MB/s | TX: %.2f MB/s\n&amp;quot;, rxSpeed, txSpeed)&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// 判断逻辑&lt;br /&gt;
		isHighTraffic := rxSpeed &amp;gt; TriggerLimitMB&lt;br /&gt;
		isAttackPattern := txSpeed &amp;lt; (rxSpeed * SafeRatio)&lt;br /&gt;
&lt;br /&gt;
		if isHighTraffic &amp;amp;&amp;amp; isAttackPattern {&lt;br /&gt;
			overloadCount++&lt;br /&gt;
			fmt.Printf(&amp;quot;\033[31m[警报 %d/%d] 异常流量检测! RX: %.2f, TX: %.2f\033[0m\n&amp;quot;, overloadCount, MaxRetries, rxSpeed, txSpeed)&lt;br /&gt;
&lt;br /&gt;
			if overloadCount &amp;gt;= MaxRetries {&lt;br /&gt;
				triggerBlackhole(iface)&lt;br /&gt;
				// 黑洞结束后，重置计数器和基准值&lt;br /&gt;
				overloadCount = 0&lt;br /&gt;
				prevRx, prevTx = getBytes(iface)&lt;br /&gt;
			}&lt;br /&gt;
		} else {&lt;br /&gt;
			if overloadCount &amp;gt; 0 {&lt;br /&gt;
				fmt.Println(&amp;quot;\033[32m流量恢复正常，警报解除。\033[0m&amp;quot;)&lt;br /&gt;
			}&lt;br /&gt;
			overloadCount = 0&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 执行黑洞逻辑&lt;br /&gt;
func triggerBlackhole(iface string) {&lt;br /&gt;
	fmt.Println(&amp;quot;\033[31m!!! 确认攻击，正在执行黑洞防御 !!!\033[0m&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	if SafeMode {&lt;br /&gt;
		fmt.Println(&amp;quot;[安全模式] 模拟执行：关闭网卡...&amp;quot;)&lt;br /&gt;
		time.Sleep(2 * time.Second)&lt;br /&gt;
		fmt.Println(&amp;quot;[安全模式] 模拟执行：开启网卡...&amp;quot;)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 关闭网卡&lt;br /&gt;
	log.Printf(&amp;quot;正在关闭网卡 %s ...&amp;quot;, iface)&lt;br /&gt;
	if err := exec.Command(&amp;quot;ip&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;set&amp;quot;, &amp;quot;dev&amp;quot;, iface, &amp;quot;down&amp;quot;).Run(); err != nil {&lt;br /&gt;
		log.Printf(&amp;quot;关闭网卡失败: %v&amp;quot;, err)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	log.Printf(&amp;quot;网卡已关闭，等待 %d 秒...&amp;quot;, BlackholeTime)&lt;br /&gt;
	time.Sleep(time.Duration(BlackholeTime) * time.Second)&lt;br /&gt;
&lt;br /&gt;
	// 开启网卡&lt;br /&gt;
	log.Printf(&amp;quot;正在恢复网卡 %s ...&amp;quot;, iface)&lt;br /&gt;
	if err := exec.Command(&amp;quot;ip&amp;quot;, &amp;quot;link&amp;quot;, &amp;quot;set&amp;quot;, &amp;quot;dev&amp;quot;, iface, &amp;quot;up&amp;quot;).Run(); err != nil {&lt;br /&gt;
		log.Printf(&amp;quot;CRITICAL: 恢复网卡失败，请手动处理! Error: %v&amp;quot;, err)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	// 给一点时间让网络重新协商&lt;br /&gt;
	time.Sleep(5 * time.Second)&lt;br /&gt;
	log.Println(&amp;quot;网络已恢复，重新开始监控。&amp;quot;)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 读取系统文件获取流量字节&lt;br /&gt;
func getBytes(iface string) (uint64, uint64) {&lt;br /&gt;
	rxPath := fmt.Sprintf(&amp;quot;/sys/class/net/%s/statistics/rx_bytes&amp;quot;, iface)&lt;br /&gt;
	txPath := fmt.Sprintf(&amp;quot;/sys/class/net/%s/statistics/tx_bytes&amp;quot;, iface)&lt;br /&gt;
&lt;br /&gt;
	rx, _ := readUint64(rxPath)&lt;br /&gt;
	tx, _ := readUint64(txPath)&lt;br /&gt;
	return rx, tx&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
func readUint64(path string) (uint64, error) {&lt;br /&gt;
	data, err := ioutil.ReadFile(path)&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		return 0, err&lt;br /&gt;
	}&lt;br /&gt;
	return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// 简单的获取默认网卡方法&lt;br /&gt;
func getDefaultInterface() (string, error) {&lt;br /&gt;
	out, err := exec.Command(&amp;quot;sh&amp;quot;, &amp;quot;-c&amp;quot;, &amp;quot;ip route get 8.8.8.8 | awk &#039;{print $5; exit}&#039;&amp;quot;).Output()&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		return &amp;quot;&amp;quot;, err&lt;br /&gt;
	}&lt;br /&gt;
	return strings.TrimSpace(string(out)), nil&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&#039;&#039;&#039;一、环境准备&#039;&#039;&#039;&lt;br /&gt;
在编译运行之前，你需要先在 VPS 上安装 Go 语言环境。&lt;br /&gt;
&#039;&#039;&#039;Debian / Ubuntu:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;apt update &amp;amp;&amp;amp; apt install golang -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CentOS / AlmaLinux:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;yum install golang -y&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
验证安装是否成功：&lt;br /&gt;
&lt;br /&gt;
go version &lt;br /&gt;
&lt;br /&gt;
输出类似 go version go1.x.x linux/amd64 即为成功&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;二、 编译与安装&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建文件&#039;&#039;&#039; 创建一个名为 &amp;lt;code&amp;gt;traffic_guard.go&amp;lt;/code&amp;gt; 的文件，并将上方的代码完整粘贴进去。Bash  &amp;lt;code&amp;gt;nano traffic_guard.go&amp;lt;/code&amp;gt;  &#039;&#039;(粘贴代码后，按 &amp;lt;code&amp;gt;Ctrl+O&amp;lt;/code&amp;gt; 保存，&amp;lt;code&amp;gt;Ctrl+X&amp;lt;/code&amp;gt; 退出)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;修改配置（可选）&#039;&#039;&#039; 代码顶部的 &amp;lt;code&amp;gt;const&amp;lt;/code&amp;gt; 区域是配置项。&lt;br /&gt;
#* &amp;lt;code&amp;gt;TriggerLimitMB&amp;lt;/code&amp;gt;: 触发阈值（默认 50MB/s）。&lt;br /&gt;
#* &amp;lt;code&amp;gt;SafeMode&amp;lt;/code&amp;gt;: 如果设为 &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;，只会报警不会真断网（适合测试）。&lt;br /&gt;
# &#039;&#039;&#039;编译生成可执行文件&#039;&#039;&#039;  &amp;lt;code&amp;gt;go build -o traffic_guard traffic_guard.go&amp;lt;/code&amp;gt;  &#039;&#039;此时你会发现当前目录下多了一个名为 &amp;lt;code&amp;gt;traffic_guard&amp;lt;/code&amp;gt; 的可执行文件。&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039;赋予权限并移动&#039;&#039;&#039;  &amp;lt;code&amp;gt;chmod +x traffic_guard  mv traffic_guard /root/traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;三、运行方式&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
方式 A：Systemd 托管运行（强烈推荐）&lt;br /&gt;
&lt;br /&gt;
这是最稳健的方式，支持开机自启和进程守护。&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;创建服务文件&#039;&#039;&#039;  &amp;lt;code&amp;gt;nano /etc/systemd/system/traffic_guard.service&amp;lt;/code&amp;gt;&lt;br /&gt;
# &#039;&#039;&#039;填入以下内容&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=BandwagonHost Traffic Guard (Go Version)&lt;br /&gt;
After=network.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
# 确保这里的路径是你实际存放二进制文件的路径&lt;br /&gt;
ExecStart=/root/traffic_guard&lt;br /&gt;
Restart=always&lt;br /&gt;
User=root&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;3.&#039;&#039;&#039;启动并设置开机自启&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;重载配置文件&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl daemon-reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;设置开机自动启动 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl enable traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;运行此服务 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl start traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.查看状态与日志&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;查看运行状态 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;systemctl status traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;查看实时日志&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;journalctl -u traffic_guard -f&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 方式 B：临时测试运行 ===&lt;br /&gt;
如果你只是想看看它能不能正常工作：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;./traffic_guard&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(注意：必须以 Root 身份运行，否则无法控制网卡)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 💡 常见问题 (FAQ) ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Q: 编译时报错 &amp;lt;code&amp;gt;command not found&amp;lt;/code&amp;gt; 怎么办？&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;A:&#039;&#039;&#039; 说明 Go 环境没装好，请重新执行 &amp;lt;code&amp;gt;apt install golang&amp;lt;/code&amp;gt;。如果 VPS 系统太老源里没有 Go，建议使用 Bash 版本脚本。&lt;br /&gt;
* &#039;&#039;&#039;Q: 程序报错 &amp;lt;code&amp;gt;panic: 无法获取网卡&amp;lt;/code&amp;gt;？&#039;&#039;&#039;&lt;br /&gt;
** &#039;&#039;&#039;A:&#039;&#039;&#039; 程序会自动尝试检测默认网卡。如果检测失败，请检查你的 VPS 是否有特殊的网络配置，或者尝试手动修改代码中的 &amp;lt;code&amp;gt;getDefaultInterface&amp;lt;/code&amp;gt; 函数，直接返回字符串 &amp;lt;code&amp;gt;&amp;quot;eth0&amp;quot;&amp;lt;/code&amp;gt;。&lt;br /&gt;
**&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;关键配置说明&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;如何测试脚本是否有效？&#039;&#039;&#039; 建议将脚本中的 &amp;lt;code&amp;gt;SAFE_MODE=false&amp;lt;/code&amp;gt; 改为 &amp;lt;code&amp;gt;SAFE_MODE=true&amp;lt;/code&amp;gt;。 这样当流量超标时，脚本只会输出红色警报文字，而不会真的断网。确认触发逻辑正常后，再改回 &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; 。&lt;br /&gt;
* &#039;&#039;&#039;触发黑洞断网后，我会彻底失联吗？&#039;&#039;&#039;  不会。黑洞防御只是暂时关闭网卡，您可以通过以下三种方式恢复：&lt;br /&gt;
** &#039;&#039;&#039;方法 1：等待自动恢复&#039;&#039;&#039; 耐心等待 &amp;lt;code&amp;gt;BLACKHOLE_TIME&amp;lt;/code&amp;gt;（默认 300秒/5分钟）结束，脚本会自动重新开启网卡，无需任何操作。&lt;br /&gt;
** &#039;&#039;&#039;方法 2：通过 KiwiVM VNC 恢复&#039;&#039;&#039; 登录 KiwiVM 面板，打开 &#039;&#039;&#039;VNC Console&#039;&#039;&#039;（类似于物理显示器，不受网卡关闭影响）。在 VNC 窗口中按 &amp;lt;code&amp;gt;Ctrl+C&amp;lt;/code&amp;gt; 终止脚本，然后输入 &amp;lt;code&amp;gt;ip link set dev eth0 up&amp;lt;/code&amp;gt; 手动恢复网络。&lt;br /&gt;
** &#039;&#039;&#039;方法 3：重启服务器&#039;&#039;&#039; 如果以上方法您都不会操作，可以直接在 KiwiVM 面板点击 &#039;&#039;&#039;Reset&#039;&#039;&#039; 或 &#039;&#039;&#039;Force Stop&#039;&#039;&#039; 后再 &#039;&#039;&#039;Start&#039;&#039;&#039; 重启服务器。重启后网卡会默认恢复连通状态。（注意：这会导致您未保存的数据丢失或服务短暂中断）。&lt;br /&gt;
&lt;br /&gt;
== 六、 长期预案：建立高可用的 VPS 使用环境 ==&lt;br /&gt;
&lt;br /&gt;
防御 DDoS 攻击是一项长期工作。通过完善的预案体系和日常的加固措施，可以将潜在的停机风险降到最低。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;安全预防建议：&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 双机备份方案：建议准备一台速度快的线路（如 香港CN2 GIA 或 日本东京CN2 GIA）作为直连主力机，同时准备一台便宜的KVM常规套餐作为备份服务器。一旦主力机被黑洞，立即切换解析到备份机并开启 &amp;lt;code&amp;gt;Cloudflare&amp;lt;/code&amp;gt; 防御。&lt;br /&gt;
* &lt;br /&gt;
* 系统加固与监控：日常运维中应当修改SSH端口，定期通过 &amp;lt;code&amp;gt;Audit Log&amp;lt;/code&amp;gt; 观察异常操作，并确保开启了&#039;&#039;&#039;搬瓦工自动备份&#039;&#039;&#039;功能以应对极端情况。&lt;br /&gt;
* &lt;br /&gt;
* 资源合理分配：若业务量增长明显，建议及时&#039;&#039;&#039;升级套餐&#039;&#039;&#039;。如果 IP 已被打残无法解封，可咨询&#039;&#039;&#039;搬瓦工客服&#039;&#039;&#039;关于&#039;&#039;&#039;购买IP&#039;&#039;&#039;的具体事宜或申请 退款。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;注意事项&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 当 IP 处于空路由状态时，所有的 &amp;lt;code&amp;gt;SSH&amp;lt;/code&amp;gt; 工具（如 &amp;lt;code&amp;gt;Xshell&amp;lt;/code&amp;gt;、&amp;lt;code&amp;gt;Xftp&amp;lt;/code&amp;gt;）均无法连接，请耐心等待 1800 秒解封。&lt;br /&gt;
* &lt;br /&gt;
* 若服务器因 CPU 占用过高被暂停（Suspended），处理流程会有所不同。请务必确认失联原因属于流量攻击而非资源超载。&lt;br /&gt;
&lt;br /&gt;
[[index.php?title=Category:400 常见问题与故障排查 — Troubleshooting]]&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1173</id>
		<title>使用搬瓦工api操作nftables实现更新白名单ip</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=%E4%BD%BF%E7%94%A8%E6%90%AC%E7%93%A6%E5%B7%A5api%E6%93%8D%E4%BD%9Cnftables%E5%AE%9E%E7%8E%B0%E6%9B%B4%E6%96%B0%E7%99%BD%E5%90%8D%E5%8D%95ip&amp;diff=1173"/>
		<updated>2026-01-25T08:26:10Z</updated>

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

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;apinftiserror&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Apinftisok.png&amp;diff=1171</id>
		<title>File:Apinftisok.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Apinftisok.png&amp;diff=1171"/>
		<updated>2026-01-25T08:22:14Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;apinftisok&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Staratok.png&amp;diff=1170</id>
		<title>File:Staratok.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Staratok.png&amp;diff=1170"/>
		<updated>2026-01-25T08:20:44Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;staratok&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Startsystemctl.png&amp;diff=1169</id>
		<title>File:Startsystemctl.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Startsystemctl.png&amp;diff=1169"/>
		<updated>2026-01-25T08:20:22Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;startsystemctl&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Catsystemctl.png&amp;diff=1168</id>
		<title>File:Catsystemctl.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Catsystemctl.png&amp;diff=1168"/>
		<updated>2026-01-25T08:20:00Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;catsystemctl&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Errorapi.png&amp;diff=1167</id>
		<title>File:Errorapi.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Errorapi.png&amp;diff=1167"/>
		<updated>2026-01-25T08:16:34Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;errorapi&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Checkipok.png&amp;diff=1166</id>
		<title>File:Checkipok.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Checkipok.png&amp;diff=1166"/>
		<updated>2026-01-25T08:12:47Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;checkipok&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Testcheckip.png&amp;diff=1165</id>
		<title>File:Testcheckip.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Testcheckip.png&amp;diff=1165"/>
		<updated>2026-01-25T08:12:21Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;testcheckip&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Lslcheckip.png&amp;diff=1164</id>
		<title>File:Lslcheckip.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Lslcheckip.png&amp;diff=1164"/>
		<updated>2026-01-25T08:09:58Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;lslcheckip&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
	<entry>
		<id>https://md5.pw/index.php?title=File:Catcheckip.png&amp;diff=1163</id>
		<title>File:Catcheckip.png</title>
		<link rel="alternate" type="text/html" href="https://md5.pw/index.php?title=File:Catcheckip.png&amp;diff=1163"/>
		<updated>2026-01-25T08:09:30Z</updated>

		<summary type="html">&lt;p&gt;EliToviyah：​&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;catcheckip&lt;/div&gt;</summary>
		<author><name>EliToviyah</name></author>
	</entry>
</feed>