Natter打洞自动更新节点及订阅教程

适用于家里没有公网 IPv4、也无法直接通过 IPv6 回家的场景。通过 Natter 自动打洞,并自动更新 RouterOS NAT 和一份固定的 Clash YAML 订阅源文件;这份订阅可以按你的选择发布到 VPS、Cloudflare Pages,或者两者一起发布。

[TOC]

一、这套方案能做什么

这套方案适合家里没有公网 IPv4、也无法直接通过 IPv6 回家的场景,目标是实现下面这 5 件事:

  1. 内网服务通过 Natter 自动打洞到公网
  2. 主路由 RouterOS 自动更新 dst-nat 规则
  3. 一份固定的 Clash YAML 订阅源文件自动更新打洞后的公网 IP 和端口,也就是 serverport
  4. 这份订阅可以发布到 VPSCloudflare Pages,或者两者一起发布
  5. 手机和电脑客户端始终只保留固定订阅地址,不用反复手改节点

整条链路可以理解为:

1
外网客户端 -> RouterOS -> Natter 转发机 -> 后端服务机

二、你需要准备什么

动手前先准备下面这些东西:

  • 一台 Natter 转发机,推荐使用 Debian 13 虚拟机
  • 一台提供回家服务的 后端服务机
  • 一台能放订阅源 YAML 的 Linux 主机,最常见就是一台带公网 IP 的 VPS
  • 一个可以自己控制解析的域名
  • 一台 RouterOS 主路由
  • 一个固定订阅地址用的长 token
  • 一把给自动同步脚本专用的 SSH key
  • 如果你准备使用 Cloudflare Pages,还需要一个 Cloudflare 账号、一个 Pages 项目,以及一枚可发布 Pages 的 API Token
  • 如果你准备使用 Cloudflare Pages,转发机本机还要能运行 nodenpx wrangler

先记下下面这些变量,部署时会反复用到:

1
2
3
4
5
6
7
8
9
10
11
FORWARDER_IP=10.0.0.11                                # Natter 转发机的内网 IP
LAN_SERVICE_IP=10.0.0.6 # 后端回家服务机的内网 IP
ROUTER_IP=10.0.0.1 # RouterOS 主路由管理地址
VPS_IP=203.0.113.10 # 订阅源 YAML 所在主机的 IP,最常见就是一台 VPS
VPS_SSH_PORT=22 # 订阅源 YAML 所在主机的 SSH 端口
SUB_DOMAIN=sub.example.com # 对外提供订阅的域名
RANDOM_LONG_TOKEN=9f3b7f2c2a5b4d6e8c1f0a9b7d3e5c11 # 固定订阅链接里的长 token,替换成自己的随机值
CF_SUB_DOMAIN=sub-cf.example.com # 可选:Cloudflare Pages 订阅域名,建议单独使用一个子域名
CF_PAGES_PROJECT=natter-sub # 可选:Cloudflare Pages 项目名
CF_PAGES_BRANCH=main # 可选:Cloudflare Pages 发布分支
CF_PAGES_DOWNLOAD_NAME=clash-natter.yaml # 可选:Cloudflare Pages 下载时显示的文件名

为了减少变量名数量,下面继续沿用 VPS_IP 这个名字。它本质上指的是“放订阅源 YAML 的那台 Linux 主机”,最常见就是一台 VPS。后面如果你只想给客户端保留 Cloudflare Pages 入口,也仍然需要这台主机作为 YAML 源文件的实际落点。


三、先把拓扑和端口想明白

可以打TCP和UDP的洞,对应可以使用ShadowsocksHysteria 2 回家,本教程默认只使用以下两个节点:

  • ss-in-natter
  • hy2-in-natter

步骤 1: 确认后端回家服务已启动,默认 Shadowsocks 入站监听 56001Hysteria 2 入站监听 56003

在后端服务机上执行:

1
ss -lntup | grep -E ':56001|:56003'

至少要能看到:

  • 56001/tcp
  • 56003/udp

步骤 2: 在 Natter 转发机上确认能访问后端服务机

1
2
timeout 5 bash -lc '</dev/tcp/LAN_SERVICE_IP/56001' && echo tcp-open || echo tcp-closed
ping -c 2 LAN_SERVICE_IP

看到 tcp-openping 正常,说明基础链路没有问题。

56003/udp 不像 TCP 那样容易直接一条命令测通,只要后端程序确实在监听、两台机器网络能互通即可。


四、先准备好 Clash 订阅源文件

客户端示例文件使用 Clash / Mihomo 格式。其他如 Stashsing-box 也可以使用,但需要自行准备对应文件并调整自动更新脚本。
仓库路径如下:

1
https://github.com/jasonxtt/file/tree/main/natter/natter-bundle

示例文件直链:

1
https://raw.githubusercontent.com/jasonxtt/file/main/natter/natter-bundle/examples/clash-natter-example.yaml

步骤 1: 先看清楚这个示例文件里要保留的节点名

YAML 里只保留下面两个节点名:

  • ss-in-natter
  • hy2-in-natter

自动同步脚本会按这两个名字精确替换 serverport
务必确保 YAML 中的节点 name 与上面的示例完全一致。

步骤 2: 把示例 YAML 放到订阅源主机的真实路径

YAML 文件路径使用:

1
/root/natter/clash/clash-natter.yaml

因为文件已经在 github file 仓库里,所以这里可以直接下载:

1
2
mkdir -p /root/natter/clash
wget -O /root/natter/clash/clash-natter.yaml https://raw.githubusercontent.com/jasonxtt/file/main/natter/natter-bundle/examples/clash-natter-example.yaml

步骤 3: 补全 YAML 里的业务字段

你需要自己填好的内容通常包括:

  • password
  • cipher
  • sni
  • alpn
  • 其他和节点协议有关的字段

自动同步脚本只会改:

  • server
  • port

至少 YAML 中 ss-in-natterhy2-in-natter 两个目标节点已经就位,而且其它密码、SNI 等字段已经填好,就说明这一步完成了。


五、给订阅做一个固定地址,并固定下载文件名

这套方案里真正会被脚本改写的是上一节那份 Clash YAML 订阅源文件。
你可以把这份文件按下面 3 种方式发布给客户端:

  1. 只发布到 VPS
  2. 只发布到 Cloudflare Pages
  3. VPSCloudflare Pages 两边同时发布

推荐做法是先统一把固定订阅路径定下来,例如:

1
https://SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml

这样以后 Natter 打出来的公网地址和端口变了,也只需要同步脚本去改 YAML 内容,客户端不用重新导入订阅。

订阅路径使用一个足够长、足够随机的 token 来隐藏真实订阅路径。

步骤 1: 先生成一个长 token

推荐直接在 VPS 上执行下面这条命令:

1
openssl rand -hex 24

这条命令会生成一串 48 位十六进制随机字符串,例如:

1
4b1b5e0d7d1a6f3aef2c13b84c6e7f1aa1029384756bcdef

把这串值记下来,后面把它填进 RANDOM_LONG_TOKEN 即可。

教程使用 acme.sh + nginx 完成反向代理,当然你也可以换成 caddylucky 等工具。

步骤 2: 给域名做好解析和 HTTPS 反向代理

  • SUB_DOMAINA 记录指向 VPS_IP
  • 先把证书申请好,例如使用 acme.sh
  • 建议把证书单独放到 nginx 专用目录里,不要直接丢在 /root/
1
2
3
4
5
6
7
8
9
10
apt install -y curl tar socat wget
mkdir -p /etc/nginx/ssl/SUB_DOMAIN
curl https://get.acme.sh | sh
systemctl stop nginx
~/.acme.sh/acme.sh --register-account -m 你的邮箱地址
~/.acme.sh/acme.sh --issue -d SUB_DOMAIN --standalone
~/.acme.sh/acme.sh --installcert -d SUB_DOMAIN --key-file /etc/nginx/ssl/SUB_DOMAIN/private.key --fullchain-file /etc/nginx/ssl/SUB_DOMAIN/fullchain.crt
~/.acme.sh/acme.sh --upgrade --auto-upgrade
chmod 600 /etc/nginx/ssl/SUB_DOMAIN/private.key
chmod 644 /etc/nginx/ssl/SUB_DOMAIN/fullchain.crt

步骤 3: 先让 Nginx 能直接读取这个 YAML 文件

因为我们现在把 YAML 放在:

1
/root/natter/clash/clash-natter.yaml

所以要先给 nginx 用户最基本的读取权限:

1
2
3
4
chmod 755 /root
chmod 755 /root/natter
chmod 755 /root/natter/clash
chmod 644 /root/natter/clash/clash-natter.yaml

步骤 4: 在 VPS 上配置一个最小可用的 Nginx 规则

下面这个示例同时完成两件事:

  1. 用长 token 固定住订阅入口
  2. Content-Disposition 固定住客户端下载后的 YAML 文件名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 80;
server_name SUB_DOMAIN;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name SUB_DOMAIN;

ssl_certificate /etc/nginx/ssl/SUB_DOMAIN/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/SUB_DOMAIN/private.key;

location = /sub/RANDOM_LONG_TOKEN.yaml {
default_type application/yaml;
add_header Content-Disposition 'attachment; filename="clash-natter.yaml"' always;
alias /root/natter/clash/clash-natter.yaml;
}

location / {
return 404;
}
}

步骤 5: 检查订阅地址和下载文件名

1
2
nginx -t && systemctl reload nginx
curl -I https://SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml

只要满足下面两点,就说明这一段配置已经生效:

  1. 返回状态码是 200
  2. 响应头里能看到类似下面这一行
1
Content-Disposition: attachment; filename="clash-natter.yaml"

这样以后客户端导入的是固定链接,下载显示的也是固定文件名,不会变成一串难认的随机字符。

(2)方案 A:发布到 VPS

如果你只想保留一个最传统的入口,直接让客户端访问:

1
https://SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml

按前面已经完成的 Nginx 配置,这条路径就会直接把订阅源 YAML 提供给客户端。

(3)方案 B:发布到 Cloudflare Pages

如果你更想让客户端走 Cloudflare Pages,可以单独再准备一个子域名,例如:

1
CF_SUB_DOMAIN=sub-cf.example.com

然后把客户端入口定成:

1
https://CF_SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml

这种做法的含义是:

  • 订阅源 YAML 仍然由同步脚本自动改写
  • 客户端最终访问的是 Cloudflare Pages 提供的固定地址

步骤 6: 在 Cloudflare 创建一个 Pages 项目

项目名示例:

1
2
CF_PAGES_PROJECT=natter-sub
CF_PAGES_BRANCH=main

成功后你会先拿到一个默认的 *.pages.dev 地址。

步骤 7: 准备 Cloudflare API Token

这里不要用全局 API Key,直接创建一枚专门给 Pages 发布使用的 API Token 即可。

如果你使用自定义 Token,权限至少要包含:

  • Account
  • Cloudflare Pages
  • Edit

成功后你会得到一串只显示一次的 Token,把它安全记下来,后面写进 pages.env

步骤 8: 在 Pages 项目里绑定 CF_SUB_DOMAIN

CF_SUB_DOMAIN 加到这个 Pages 项目里,然后按 Cloudflare 面板提示完成 DNS 绑定。

成功后你会看到这个域名已经归属于该 Pages 项目。

步骤 9: 在转发机上先确认 nodewrangler 能运行

生产环境实际使用的是:

  • node v22.23.1
  • npx wrangler@4.107.0

你不一定非要完全一样,但至少要保证下面两条命令能正常执行:

1
2
node -v
npx --yes wrangler@4.107.0 --version

只要两条命令都能正常输出版本号,就说明转发机具备了 Pages 发布能力。

这里用的是 Cloudflare API Token + Wrangler,不是直接手写调用 Cloudflare REST API;真正触发发布的动作是 npx wrangler pages deploy ...

(4)方案 C:VPS 和 Cloudflare Pages 两边一起发布

如果你希望同时保留两条入口,也完全可以这样做:

  • https://SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml
  • https://CF_SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml

这样做的好处是:

  • 你可以自己长期观察两条入口的可用性
  • 客户端可以只保留其中一条
  • 以后要切换入口时,不需要重新生成节点

六、在本地 Natter 转发机上部署 env 版 Natter 自动同步方案

(1)安装 natter.py

步骤 1: 在 Natter 转发机上准备程序目录

1
2
3
4
mkdir -p /opt/natter
curl -fsSL https://raw.githubusercontent.com/MikeWang000000/Natter/master/natter.py -o /opt/natter/natter.py
chmod 755 /opt/natter/natter.py
python3 /opt/natter/natter.py --help

看到 --help 输出,说明 natter.py 已经准备好了。

(2)上传 natter-bundle

步骤 2:natter-bundle 整个目录放到转发机上

例如放到:

1
/root/natter-bundle

步骤 3: 先修改两个 Natter service 模板

编辑这两个文件:

1
2
/root/natter-bundle/systemd/natter-tcp-56001.service
/root/natter-bundle/systemd/natter-udp-56003.service

把里面的示例地址改成你的真实环境:

  • -i FORWARDER_IP
  • -t LAN_SERVICE_IP
  • 保留 5600156003

修改完成后,核心命令应该类似这样:

1
2
ExecStart=/usr/bin/python3 /opt/natter/natter.py -v -U -i FORWARDER_IP -m socket -t LAN_SERVICE_IP -p 56001
ExecStart=/usr/bin/python3 /opt/natter/natter.py -v -u -U -i FORWARDER_IP -m socket -t LAN_SERVICE_IP -p 56003

(3)安装到系统目录

步骤 4: 执行安装脚本

1
2
3
cd /root/natter-bundle
chmod +x scripts/install.sh scripts/healthcheck.sh
./scripts/install.sh

这一步会安装:

  • /opt/natter/natter_sync.py
  • /etc/natter-sync/natter-sync.env
  • /etc/natter-sync/pages.env.example
  • /etc/systemd/system/natter-sync.service
  • /etc/systemd/system/natter-*.service

如果屏幕上能看到 installed: 相关提示,就说明安装成功了。

(4)为 VPS 配置密钥登录

如果你的 VPS 早就已经支持某把私钥登录,直接看 步骤 7 复用已有 SSH key 即可。

步骤 5: 在转发机上生成专用密钥

1
2
3
4
mkdir -p /root/.ssh
chmod 700 /root/.ssh
ssh-keygen -t ed25519 -N "" -f /root/.ssh/id_ed25519_natter_sync
cat /root/.ssh/id_ed25519_natter_sync.pub

把输出的公钥追加到 VPS 的:

1
/root/.ssh/authorized_keys

步骤 6: 加入 VPS host key 并测试免密登录

1
2
3
ssh-keyscan -p VPS_SSH_PORT VPS_IP >> /root/.ssh/known_hosts
chmod 600 /root/.ssh/known_hosts
ssh -i /root/.ssh/id_ed25519_natter_sync -p VPS_SSH_PORT root@VPS_IP

能直接登录成功,就说明自动同步所需的 SSH 条件已经满足。

步骤 7: 如果 VPS 已经支持某把私钥登录,直接复用这把私钥

不一定非要重新生成。最简单的两种做法是:

  1. 如果转发机本机已经有一把能登录 VPS 的私钥,直接把 SSH_KEY 改成那把私钥的实际路径
  2. 如果这把私钥只在你自己的管理电脑上,可以把它安全复制到转发机上,再改名成下面这条统一路径

例如在你的管理电脑上执行:

1
2
scp ~/.ssh/已有可用私钥 root@FORWARDER_IP:/root/.ssh/id_ed25519_natter_sync
ssh root@FORWARDER_IP 'chmod 600 /root/.ssh/id_ed25519_natter_sync'

然后再测试:

1
ssh -i /root/.ssh/id_ed25519_natter_sync -p VPS_SSH_PORT root@VPS_IP

能直接登录就说明这把旧私钥也可以给同步脚本复用。

(5)编辑 /etc/natter-sync/natter-sync.env

步骤 8: 打开 env 文件

1
nano /etc/natter-sync/natter-sync.env

这里直接把占位值替换成你自己的真实值即可。

默认模板内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POLL_SECONDS=15
TCP_PUBLISH_MIN_UPTIME_SECONDS=12
STATE_PATH=/var/lib/natter-sync/state.json

SSH_KEY=/root/.ssh/id_ed25519_natter_sync
SSH_HOST=VPS_IP
SSH_PORT=VPS_SSH_PORT
SSH_USER=root

ROUTER_IP=ROUTER_IP
ROS_USER=router_api_user
ROS_PASS=router_api_pass
ROS_TARGET_IP=FORWARDER_IP

TARGETS_JSON={"ss-in-natter":{"service":"natter-tcp-56001.service","proto":"tcp","ros_comment":"natter-56001"},"hy2-in-natter":{"service":"natter-udp-56003.service","proto":"udp","ros_comment":"natter-56003"}}
REMOTE_YAMLS_JSON={"clash":{"path":"/root/natter/clash/clash-natter.yaml","format":"clash","targets":["ss-in-natter","hy2-in-natter"]}}

注意这几个点:

  • 把上面的占位符全部替换成你自己的真实值
  • YAML 路径要和你在 VPS 上真正放置的文件路径完全一致

改完后再次检查一遍,确认只有两个节点、只有一个要同步的 Clash YAML 文件。

(6)如果你选择使用 Cloudflare Pages,再配置 /etc/natter-sync/pages.env

如果你最后选择的是下面两种发布方式之一,就需要这一段配置:

  1. 只发布到 Cloudflare Pages
  2. VPSCloudflare Pages 两边一起发布

真正的工作方式是:

  1. 脚本先按 REMOTE_YAMLS_JSON 通过 SSH 改写 VPS 上的真实 YAML
  2. 再从这个远端 YAML 里拉一份回来
  3. 最后调用 Wrangler 发布到 Cloudflare Pages

所以这里依然离不开:

  • SSH_KEY
  • SSH_HOST
  • SSH_PORT
  • REMOTE_YAMLS_JSON

步骤 9: 先从示例复制出 Pages 配置文件

1
2
cp /etc/natter-sync/pages.env.example /etc/natter-sync/pages.env
nano /etc/natter-sync/pages.env

示例内容如下:

1
2
3
4
5
6
7
8
CF_API_TOKEN=your_cloudflare_api_token
CF_ACCOUNT_ID=your_cloudflare_account_id
CF_PAGES_PROJECT=natter-sub
CF_PAGES_BRANCH=main
CF_PAGES_DOMAIN=CF_SUB_DOMAIN
CF_PAGES_SUB_TOKEN=RANDOM_LONG_TOKEN
CF_PAGES_DOWNLOAD_NAME=clash-natter.yaml
CF_PAGES_SOURCE_YAML=clash

这些字段分别表示:

  • CF_API_TOKEN
    Cloudflare Pages 发布用的 API Token
  • CF_ACCOUNT_ID
    Cloudflare 账号的 Account ID
  • CF_PAGES_PROJECT
    Pages 项目名
  • CF_PAGES_BRANCH
    Pages 发布分支
  • CF_PAGES_DOMAIN
    这个 Pages 订阅最终使用的自定义域名,建议填 CF_SUB_DOMAIN
  • CF_PAGES_SUB_TOKEN
    Pages 订阅路径中的长 token,通常直接复用前面的 RANDOM_LONG_TOKEN
  • CF_PAGES_DOWNLOAD_NAME
    客户端下载时显示的固定文件名
  • CF_PAGES_SOURCE_YAML
    指定 Pages 这条线要发布哪一份已经更新好的远端 YAML;本教程使用 clash

步骤 10: 明确 CF_PAGES_SOURCE_YAML=clash 的含义

这里不是让你在转发机本地再单独维护第二份 YAML。

它的真实意思是:

  • 先按 REMOTE_YAMLS_JSON 更新 VPS 上的 clash 文件
  • 再把这份已经更新好的 clash 文件发布到 Cloudflare Pages

只要你在 pages.env 里填好了这些字段,并且前面的 SSHREMOTE_YAMLS_JSON 已经可用,就说明 Cloudflare Pages 这个发布选项也已经准备好了。


七、配置 RouterOS 自动 NAT

自动同步脚本和 RouterOS 的连接方式是:

  • 通过 HTTP Basic Auth
  • 调用 RouterOS REST API
  • 脚本实际访问的是:
    • http://ROUTER_IP/rest/ip/firewall/nat
    • http://ROUTER_IP/rest/ip/address

也就是说,natter_sync.py 不是走 SSH 去改 RouterOS,而是直接调用 RouterOS REST API

根据 MikroTik 官方文档,RouterOS REST API 需要 www-sslwww 服务可用;从 RouterOS v7.9 起可通过 www 提供 http://<router_ip>/rest 入口。当前这份脚本使用的是 http://ROUTER_IP/rest/...,所以要保证 www 服务已开启,并且只建议在你自己的内网环境中使用。MikroTik REST API

(1)先把 RouterOS REST API 打开

步骤 1: 启用 www 服务,并把来源限制到 Natter 转发机

可以在 RouterOS 终端执行:

1
2
/ip/service/enable www
/ip/service/set www address=FORWARDER_IP/32

如果你更习惯在 WinBox 里操作,也可以进入 IP -> Services,把 www 打开,并把允许来源限制到 FORWARDER_IP/32

(2)创建一个专门给同步脚本用的账号

步骤 2: 创建一个只给脚本使用的用户组和用户

1
2
/user/group/add name=natter-sync policy=read,write,rest-api
/user/add name=router_api_user group=natter-sync password=改成强密码

根据 MikroTik 官方文档,RouterOS 用户组里可以单独授予 rest-api 权限;建议不要直接用你平时登录路由器的管理员账号给脚本使用。MikroTik User Groups

(3)先验证 API 通不通

步骤 3: 在 Natter 转发机上测试 REST API

1
curl -sS -u router_api_user:你的密码 http://ROUTER_IP/rest/ip/firewall/nat

如果返回的是 JSON 数组,就说明:

  • www 服务已经打开
  • 用户名密码没问题
  • rest-api 权限也已经生效

(4)再创建要被脚本接管的 NAT 规则

自动同步脚本不会替你创建 NAT 规则,它只会根据 ros_comment 去更新已经存在的规则。

这里的关键点一定要记住:脚本是靠 comment 去定位 NAT 规则的,不是按端口号盲改,也不是按你手工看到的顺序改。

也就是说:

  • natter-56001 这条规则会由脚本接管
  • natter-56003 这条规则会由脚本接管
  • 这两个 comment 一旦和 TARGETS_JSON 里写的不一致,脚本就找不到对应规则

所以后续不要随手改这两个 NAT 规则的 comment

步骤 4: 先在 RouterOS IPv4 部分创建好两条 dst-nat

建议 comment 固定使用:

  • natter-56001
  • natter-56003

步骤 5: 规则先按下面原则准备好

  • to-addresses 指向 FORWARDER_IP
  • to-ports 先写成对应端口
  • TCP 规则给 56001
  • UDP 规则给 56003

如果你是通过 WinBoxWebFig 操作,只要 comment、协议、目标地址这些基础信息先建好即可。

步骤 6: 让脚本自动接管后续更新

脚本运行后会根据当前打洞状态自动更新:

  • dst-port
  • to-ports

如果 RouterOS 的 WAN 拿到的是私网或 CGNAT 地址,脚本会优先回写 local_port;如果是公网地址,会优先回写公网映射端口。

看到对应 comment 的两条 NAT 规则已经存在,并且 ROUTER_IPROS_USERROS_PASSROS_TARGET_IP 都已经填进 env 文件,就说明 RouterOS 这块准备完了。


八、启动服务、验证结果、排查问题

(1)启动服务

步骤 1: 重新加载 systemd

1
systemctl daemon-reload

步骤 2: 启动并设置开机自启

1
2
3
systemctl enable --now natter-tcp-56001.service
systemctl enable --now natter-udp-56003.service
systemctl enable --now natter-sync.service

(2)检查服务状态

步骤 3: 查看运行状态

1
2
3
systemctl status natter-tcp-56001.service
systemctl status natter-udp-56003.service
systemctl status natter-sync.service

看到绿色 active (running),说明服务已经启动成功。

(3)检查同步结果

步骤 4: 看同步日志和状态文件

1
2
journalctl -u natter-sync.service -n 50 --no-pager -o cat
cat /var/lib/natter-sync/state.json

如果同步正常,你会在日志里看到类似 synced: 的结果,state.json 里也会出现最新的公网 server、公网 port 和本地 local_port

如果你选择了 Cloudflare Pages 这个发布方式,还应该能在日志或 state.json 中看到类似下面这些字段:

  • pages.status
  • pages.sub_path
  • pages.custom_domain
  • pages.deployment_url

步骤 5: 再检查固定订阅地址

1
curl -I https://SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml

如果订阅地址能正常返回,并且响应头里带有 Content-Disposition,说明客户端入口和固定下载文件名都没有问题。

步骤 6: 如果你选择了 Cloudflare Pages 这个发布方式,再检查 Pages 入口

1
curl -I https://CF_SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml

只要满足下面 3 点,就说明 Pages 这条发布方式也已经跑通:

  1. 返回状态码是 200
  2. 响应头里能看到 Content-Disposition
  3. state.json 里的 pages.statusupdated

步骤 7: 按需使用健康检查脚本

1
2
cd /root/natter-bundle
./scripts/healthcheck.sh

这个脚本会顺手帮你看:

  • natter-tcp-56001.service
  • natter-udp-56003.service
  • natter-sync.service
  • 同步状态文件和最近日志

(4)常见问题

问题 1: missing YAML entries

说明远端 YAML 里缺少脚本要找的节点名。
检查 ss-in-natterhy2-in-natter 有没有写错。

问题 2: SSH 登录失败

优先检查:

  • /root/.ssh/id_ed25519_natter_sync
  • VPS 上的 authorized_keys
  • SSH_HOST
  • SSH_PORT

问题 3: RouterOS 没有自动更新

优先检查:

  • ROUTER_IP
  • ROS_USER
  • ROS_PASS
  • RouterOS 的 www 服务是否已启用
  • 脚本账号是否有 rest-api 权限
  • NAT 规则的 comment

问题 4: 订阅地址返回 404

优先检查:

  • SUB_DOMAIN 是否解析到 VPS
  • nginx 配置里的路径是否写对
  • alias 后面的 YAML 路径是否和你的真实文件路径一致
  • /root/natter/clash/clash-natter.yaml 是否真的存在

问题 5: Cloudflare Pages 没有自动更新

优先检查:

  • /etc/natter-sync/pages.env 是否真的存在
  • CF_API_TOKEN
  • CF_ACCOUNT_ID
  • CF_PAGES_PROJECT
  • CF_PAGES_BRANCH
  • CF_PAGES_SOURCE_YAML
  • node -v
  • npx --yes wrangler@4.107.0 --version

如果这里有一项不通,Pages 这条发布方式就不会成功发布。

问题 6: VPS 订阅更新了,但 Pages 订阅没变

优先检查:

  • state.json 里的 pages.status
  • journalctl -u natter-sync.service
  • CF_SUB_DOMAIN 是否已经正确绑定到 Pages 项目

特别注意:

  • Cloudflare Pages 这条发布方式使用的仍然是同一份订阅源 YAML
  • 所以排查时先确认这份 YAML 是否已经被同步脚本改对
  • 再确认 Pages 发布动作本身是否成功

当你完成上面所有检查后,如果:

  • 3 个服务都是 active
  • state.json 里有最新映射
  • 订阅地址可以正常打开

那么这套 Natter + RouterOS + Clash YAML 自动同步 方案就已经跑通了;如果你同时把 Cloudflare Pages 也检查通过,那就等于你已经拥有了两种可用的订阅发布方式。