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

Natter打洞自动更新节点及订阅教程
Tom适用于家里没有公网 IPv4、也无法直接通过 IPv6 回家的场景。通过 Natter 自动打洞,并自动更新 RouterOS NAT 和一份固定的 Clash YAML 订阅源文件;这份订阅可以按你的选择发布到 VPS、Cloudflare Pages,或者两者一起发布。
[TOC]
一、这套方案能做什么
这套方案适合家里没有公网 IPv4、也无法直接通过 IPv6 回家的场景,目标是实现下面这 5 件事:
- 内网服务通过
Natter自动打洞到公网 - 主路由
RouterOS自动更新dst-nat规则 - 一份固定的
Clash YAML订阅源文件自动更新打洞后的公网 IP 和端口,也就是server和port - 这份订阅可以发布到
VPS、Cloudflare Pages,或者两者一起发布 - 手机和电脑客户端始终只保留固定订阅地址,不用反复手改节点
整条链路可以理解为:
1 | 外网客户端 -> RouterOS -> Natter 转发机 -> 后端服务机 |
二、你需要准备什么
动手前先准备下面这些东西:
- 一台
Natter 转发机,推荐使用Debian 13虚拟机 - 一台提供回家服务的
后端服务机 - 一台能放订阅源 YAML 的 Linux 主机,最常见就是一台带公网 IP 的
VPS - 一个可以自己控制解析的域名
- 一台
RouterOS主路由 - 一个固定订阅地址用的长
token - 一把给自动同步脚本专用的
SSH key - 如果你准备使用
Cloudflare Pages,还需要一个Cloudflare账号、一个Pages项目,以及一枚可发布 Pages 的API Token - 如果你准备使用
Cloudflare Pages,转发机本机还要能运行node和npx wrangler
先记下下面这些变量,部署时会反复用到:
1 | FORWARDER_IP=10.0.0.11 # Natter 转发机的内网 IP |
为了减少变量名数量,下面继续沿用
VPS_IP这个名字。它本质上指的是“放订阅源 YAML 的那台 Linux 主机”,最常见就是一台 VPS。后面如果你只想给客户端保留 Cloudflare Pages 入口,也仍然需要这台主机作为 YAML 源文件的实际落点。
三、先把拓扑和端口想明白
可以打TCP和UDP的洞,对应可以使用Shadowsocks 和 Hysteria 2 回家,本教程默认只使用以下两个节点:
ss-in-natterhy2-in-natter
步骤 1: 确认后端回家服务已启动,默认 Shadowsocks 入站监听 56001,Hysteria 2 入站监听 56003
在后端服务机上执行:
1 | ss -lntup | grep -E ':56001|:56003' |
至少要能看到:
56001/tcp56003/udp
步骤 2: 在 Natter 转发机上确认能访问后端服务机
1 | timeout 5 bash -lc '</dev/tcp/LAN_SERVICE_IP/56001' && echo tcp-open || echo tcp-closed |
看到 tcp-open 且 ping 正常,说明基础链路没有问题。
56003/udp不像 TCP 那样容易直接一条命令测通,只要后端程序确实在监听、两台机器网络能互通即可。
四、先准备好 Clash 订阅源文件
客户端示例文件使用 Clash / Mihomo 格式。其他如 Stash、sing-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-natterhy2-in-natter
自动同步脚本会按这两个名字精确替换 server 和 port。
务必确保 YAML 中的节点 name 与上面的示例完全一致。
步骤 2: 把示例 YAML 放到订阅源主机的真实路径
YAML 文件路径使用:
1 | /root/natter/clash/clash-natter.yaml |
因为文件已经在 github file 仓库里,所以这里可以直接下载:
1 | mkdir -p /root/natter/clash |
步骤 3: 补全 YAML 里的业务字段
你需要自己填好的内容通常包括:
passwordciphersnialpn- 其他和节点协议有关的字段
自动同步脚本只会改:
serverport
至少 YAML 中 ss-in-natter 和 hy2-in-natter 两个目标节点已经就位,而且其它密码、SNI 等字段已经填好,就说明这一步完成了。
五、给订阅做一个固定地址,并固定下载文件名
这套方案里真正会被脚本改写的是上一节那份 Clash YAML 订阅源文件。
你可以把这份文件按下面 3 种方式发布给客户端:
- 只发布到
VPS - 只发布到
Cloudflare Pages VPS和Cloudflare 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 完成反向代理,当然你也可以换成 caddy、lucky 等工具。
步骤 2: 给域名做好解析和 HTTPS 反向代理
SUB_DOMAIN的A记录指向VPS_IP- 先把证书申请好,例如使用
acme.sh - 建议把证书单独放到
nginx专用目录里,不要直接丢在/root/下
1 | apt install -y curl tar socat wget |
步骤 3: 先让 Nginx 能直接读取这个 YAML 文件
因为我们现在把 YAML 放在:
1 | /root/natter/clash/clash-natter.yaml |
所以要先给 nginx 用户最基本的读取权限:
1 | chmod 755 /root |
步骤 4: 在 VPS 上配置一个最小可用的 Nginx 规则
下面这个示例同时完成两件事:
- 用长
token固定住订阅入口 - 用
Content-Disposition固定住客户端下载后的 YAML 文件名
1 | server { |
步骤 5: 检查订阅地址和下载文件名
1 | nginx -t && systemctl reload nginx |
只要满足下面两点,就说明这一段配置已经生效:
- 返回状态码是
200 - 响应头里能看到类似下面这一行
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 | CF_PAGES_PROJECT=natter-sub |
成功后你会先拿到一个默认的 *.pages.dev 地址。
步骤 7: 准备 Cloudflare API Token
这里不要用全局 API Key,直接创建一枚专门给 Pages 发布使用的 API Token 即可。
如果你使用自定义 Token,权限至少要包含:
AccountCloudflare PagesEdit
成功后你会得到一串只显示一次的 Token,把它安全记下来,后面写进 pages.env。
步骤 8: 在 Pages 项目里绑定 CF_SUB_DOMAIN
把 CF_SUB_DOMAIN 加到这个 Pages 项目里,然后按 Cloudflare 面板提示完成 DNS 绑定。
成功后你会看到这个域名已经归属于该 Pages 项目。
步骤 9: 在转发机上先确认 node 和 wrangler 能运行
生产环境实际使用的是:
node v22.23.1npx wrangler@4.107.0
你不一定非要完全一样,但至少要保证下面两条命令能正常执行:
1 | node -v |
只要两条命令都能正常输出版本号,就说明转发机具备了 Pages 发布能力。
这里用的是
Cloudflare API Token + Wrangler,不是直接手写调用 Cloudflare REST API;真正触发发布的动作是npx wrangler pages deploy ...。
(4)方案 C:VPS 和 Cloudflare Pages 两边一起发布
如果你希望同时保留两条入口,也完全可以这样做:
https://SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yamlhttps://CF_SUB_DOMAIN/sub/RANDOM_LONG_TOKEN.yaml
这样做的好处是:
- 你可以自己长期观察两条入口的可用性
- 客户端可以只保留其中一条
- 以后要切换入口时,不需要重新生成节点
六、在本地 Natter 转发机上部署 env 版 Natter 自动同步方案
(1)安装 natter.py
步骤 1: 在 Natter 转发机上准备程序目录
1 | mkdir -p /opt/natter |
看到 --help 输出,说明 natter.py 已经准备好了。
(2)上传 natter-bundle
步骤 2: 把 natter-bundle 整个目录放到转发机上
例如放到:
1 | /root/natter-bundle |
步骤 3: 先修改两个 Natter service 模板
编辑这两个文件:
1 | /root/natter-bundle/systemd/natter-tcp-56001.service |
把里面的示例地址改成你的真实环境:
-i FORWARDER_IP-t LAN_SERVICE_IP- 保留
56001和56003
修改完成后,核心命令应该类似这样:
1 | ExecStart=/usr/bin/python3 /opt/natter/natter.py -v -U -i FORWARDER_IP -m socket -t LAN_SERVICE_IP -p 56001 |
(3)安装到系统目录
步骤 4: 执行安装脚本
1 | cd /root/natter-bundle |
这一步会安装:
/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 | mkdir -p /root/.ssh |
把输出的公钥追加到 VPS 的:
1 | /root/.ssh/authorized_keys |
步骤 6: 加入 VPS host key 并测试免密登录
1 | ssh-keyscan -p VPS_SSH_PORT VPS_IP >> /root/.ssh/known_hosts |
能直接登录成功,就说明自动同步所需的 SSH 条件已经满足。
步骤 7: 如果 VPS 已经支持某把私钥登录,直接复用这把私钥
不一定非要重新生成。最简单的两种做法是:
- 如果转发机本机已经有一把能登录 VPS 的私钥,直接把
SSH_KEY改成那把私钥的实际路径 - 如果这把私钥只在你自己的管理电脑上,可以把它安全复制到转发机上,再改名成下面这条统一路径
例如在你的管理电脑上执行:
1 | scp ~/.ssh/已有可用私钥 root@FORWARDER_IP:/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 | POLL_SECONDS=15 |
注意这几个点:
- 把上面的占位符全部替换成你自己的真实值
- YAML 路径要和你在 VPS 上真正放置的文件路径完全一致
改完后再次检查一遍,确认只有两个节点、只有一个要同步的 Clash YAML 文件。
(6)如果你选择使用 Cloudflare Pages,再配置 /etc/natter-sync/pages.env
如果你最后选择的是下面两种发布方式之一,就需要这一段配置:
- 只发布到
Cloudflare Pages VPS和Cloudflare Pages两边一起发布
真正的工作方式是:
- 脚本先按
REMOTE_YAMLS_JSON通过SSH改写VPS上的真实 YAML - 再从这个远端 YAML 里拉一份回来
- 最后调用
Wrangler发布到Cloudflare Pages
所以这里依然离不开:
SSH_KEYSSH_HOSTSSH_PORTREMOTE_YAMLS_JSON
步骤 9: 先从示例复制出 Pages 配置文件
1 | cp /etc/natter-sync/pages.env.example /etc/natter-sync/pages.env |
示例内容如下:
1 | CF_API_TOKEN=your_cloudflare_api_token |
这些字段分别表示:
CF_API_TOKEN
Cloudflare Pages 发布用的 API TokenCF_ACCOUNT_ID
Cloudflare 账号的 Account IDCF_PAGES_PROJECT
Pages 项目名CF_PAGES_BRANCH
Pages 发布分支CF_PAGES_DOMAIN
这个 Pages 订阅最终使用的自定义域名,建议填CF_SUB_DOMAINCF_PAGES_SUB_TOKEN
Pages 订阅路径中的长 token,通常直接复用前面的RANDOM_LONG_TOKENCF_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 里填好了这些字段,并且前面的 SSH 和 REMOTE_YAMLS_JSON 已经可用,就说明 Cloudflare Pages 这个发布选项也已经准备好了。
七、配置 RouterOS 自动 NAT
自动同步脚本和 RouterOS 的连接方式是:
- 通过
HTTP Basic Auth - 调用
RouterOS REST API - 脚本实际访问的是:
http://ROUTER_IP/rest/ip/firewall/nathttp://ROUTER_IP/rest/ip/address
也就是说,natter_sync.py 不是走 SSH 去改 RouterOS,而是直接调用 RouterOS REST API。
根据 MikroTik 官方文档,RouterOS REST API 需要
www-ssl或www服务可用;从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 | /ip/service/enable www |
如果你更习惯在 WinBox 里操作,也可以进入 IP -> Services,把 www 打开,并把允许来源限制到 FORWARDER_IP/32。
(2)创建一个专门给同步脚本用的账号
步骤 2: 创建一个只给脚本使用的用户组和用户
1 | /user/group/add name=natter-sync policy=read,write,rest-api |
根据 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-56001natter-56003
步骤 5: 规则先按下面原则准备好
to-addresses指向FORWARDER_IPto-ports先写成对应端口- TCP 规则给
56001 - UDP 规则给
56003
如果你是通过 WinBox 或 WebFig 操作,只要 comment、协议、目标地址这些基础信息先建好即可。
步骤 6: 让脚本自动接管后续更新
脚本运行后会根据当前打洞状态自动更新:
dst-portto-ports
如果 RouterOS 的 WAN 拿到的是私网或 CGNAT 地址,脚本会优先回写 local_port;如果是公网地址,会优先回写公网映射端口。
看到对应 comment 的两条 NAT 规则已经存在,并且 ROUTER_IP、ROS_USER、ROS_PASS、ROS_TARGET_IP 都已经填进 env 文件,就说明 RouterOS 这块准备完了。
八、启动服务、验证结果、排查问题
(1)启动服务
步骤 1: 重新加载 systemd
1 | systemctl daemon-reload |
步骤 2: 启动并设置开机自启
1 | systemctl enable --now natter-tcp-56001.service |
(2)检查服务状态
步骤 3: 查看运行状态
1 | systemctl status natter-tcp-56001.service |
看到绿色 active (running),说明服务已经启动成功。
(3)检查同步结果
步骤 4: 看同步日志和状态文件
1 | journalctl -u natter-sync.service -n 50 --no-pager -o cat |
如果同步正常,你会在日志里看到类似 synced: 的结果,state.json 里也会出现最新的公网 server、公网 port 和本地 local_port。
如果你选择了 Cloudflare Pages 这个发布方式,还应该能在日志或 state.json 中看到类似下面这些字段:
pages.statuspages.sub_pathpages.custom_domainpages.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 这条发布方式也已经跑通:
- 返回状态码是
200 - 响应头里能看到
Content-Disposition state.json里的pages.status是updated
步骤 7: 按需使用健康检查脚本
1 | cd /root/natter-bundle |
这个脚本会顺手帮你看:
natter-tcp-56001.servicenatter-udp-56003.servicenatter-sync.service- 同步状态文件和最近日志
(4)常见问题
问题 1: missing YAML entries
说明远端 YAML 里缺少脚本要找的节点名。
检查 ss-in-natter 和 hy2-in-natter 有没有写错。
问题 2: SSH 登录失败
优先检查:
/root/.ssh/id_ed25519_natter_sync- VPS 上的
authorized_keys SSH_HOSTSSH_PORT
问题 3: RouterOS 没有自动更新
优先检查:
ROUTER_IPROS_USERROS_PASS- RouterOS 的
www服务是否已启用 - 脚本账号是否有
rest-api权限 - NAT 规则的
comment
问题 4: 订阅地址返回 404
优先检查:
SUB_DOMAIN是否解析到 VPSnginx配置里的路径是否写对alias后面的 YAML 路径是否和你的真实文件路径一致/root/natter/clash/clash-natter.yaml是否真的存在
问题 5: Cloudflare Pages 没有自动更新
优先检查:
/etc/natter-sync/pages.env是否真的存在CF_API_TOKENCF_ACCOUNT_IDCF_PAGES_PROJECTCF_PAGES_BRANCHCF_PAGES_SOURCE_YAMLnode -vnpx --yes wrangler@4.107.0 --version
如果这里有一项不通,Pages 这条发布方式就不会成功发布。
问题 6: VPS 订阅更新了,但 Pages 订阅没变
优先检查:
state.json里的pages.statusjournalctl -u natter-sync.serviceCF_SUB_DOMAIN是否已经正确绑定到 Pages 项目
特别注意:
Cloudflare Pages这条发布方式使用的仍然是同一份订阅源 YAML- 所以排查时先确认这份 YAML 是否已经被同步脚本改对
- 再确认
Pages发布动作本身是否成功
当你完成上面所有检查后,如果:
- 3 个服务都是
active state.json里有最新映射- 订阅地址可以正常打开
那么这套 Natter + RouterOS + Clash YAML 自动同步 方案就已经跑通了;如果你同时把 Cloudflare Pages 也检查通过,那就等于你已经拥有了两种可用的订阅发布方式。





