从零开始把家里的动态 IP 接到自己的域名上,大约需要十分钟。 大部分步骤在控制台里会自动生成好脚本,这里讲的是原理、凭证怎么拿,以及出问题了怎么查。
路由器每隔几分钟把自己当前的公网 IP 发给我们,我们替它完成云厂商接口的签名,然后更新你的解析记录。
你的路由器 ──①上报当前 IP──▶ ddns.masktt.com
│
②和上次比对
│ IP 没变 → 直接返回,不往下走
│ IP 变了 → 继续
▼
③签名并调用接口
│
▼
阿里云 / DNSPod / Cloudflare
(你自己的账号)
为什么路由器不能自己去调? 阿里云要求把请求参数排序后算 HMAC-SHA1 签名,再做 percent-encode。RouterOS 的脚本语言里没有这些函数,实现不出来。这就是中转服务存在的意义。
第 ② 步很关键。 路由器通常 5 分钟上报一次,但家宽的公网 IP 可能几天才变一次。我们先在本地比对,IP 没变就直接返回,完全不碰云厂商接口 —— 这样你的 API 配额不会被白白消耗掉,也不会因为调用太频繁被限流。
masktt-ddns。AliyunDNSFullAccess。如果你想再收紧一点,可以不授 AliyunDNSFullAccess,而是自定义一个只能操作单个域名的权限策略:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"alidns:DescribeDomainRecords",
"alidns:DescribeSubDomainRecords",
"alidns:AddDomainRecord",
"alidns:UpdateDomainRecord"
],
"Resource": "*"
}
]
}
ID,Token 的形式,中间是英文逗号,不要有空格。ID : 123456
Token : abcdef0123456789abcdef0123456789
填写时 : 123456,abcdef0123456789abcdef0123456789
DNSPod 支持分线路解析(电信 / 联通 / 移动 / 教育网)。如果你的域名在不同线路配了不同记录,在控制台里选对线路,我们只会更新那一条,不影响其它线路。
Include → Specific zone → 你的域名。
这一步很重要,不选就是账号下所有域名都能改。Cloudflare 的记录有「已代理」(橙云)和「仅 DNS」(灰云)两种状态。做家宽回源时,通常要选「仅 DNS」。
控制台里那个「更新后保持橙云代理开启」的勾选框,默认不勾就是仅 DNS。
适用于 RouterOS v6 和 v7。在控制台生成脚本后:
masktt-ddns,把生成的脚本粘进 Source。/tool fetch 需要相应权限,缺了会报
not enough permissions,而且在 Scheduler 里是静默失败的,日志里什么都看不到。
新建时把 read write test ftp policy
都勾上最省事。注意 Scheduler 条目本身也有一份独立的 Policy,同样要勾。| 检查项 | 怎么确认 / 出问题的表现 |
|---|---|
| 脚本 Policy | 见上。报 not enough permissions,或 Scheduler 到点了但日志里毫无动静。 |
| 路由器能解析域名 | IP → DNS 里要有可用的 DNS 服务器。没有的话 fetch 连域名都解析不了,
日志里是请求失败。在 Terminal 里 /ping ddns.masktt.com 通了就说明没问题。 |
| 固件别太老 | 脚本用了 as-value 取返回内容。如果执行时报
as-value 不是有效参数,说明固件太旧,升级到较新的 v6 或 v7 即可。 |
这一步选错了解析会直接废掉,是实际用起来最容易出错的地方。控制台「高级选项 → IP 来源」里的两个选项, 对应的就是这两种接入方式:
| 你的网络 | 该选哪个 | 为什么 |
|---|---|---|
| 光猫拨号 路由器 WAN 口是 192.168.x / 10.x |
由服务端识别 | 路由器自己看不到公网 IP,它 WAN 上只有光猫分的内网地址。 这种情况千万别选读接口,会把内网地址推上去,解析就废了。 |
| 路由器拨号 有 pppoe-out1 这类接口 |
读 PPPoE / WAN 接口 | 直接读拨号接口上的地址,不依赖服务端看到的源 IP。多拨、或出口走了代理 / VPN 导致服务端看到的不是宽带 IP 时,必须用这个。 |
选「读 PPPoE / WAN 接口」后,生成的脚本会多出读地址这一段,大致是这样:
# 下面这行改成你的 PPPoE / WAN 接口名
:local wanIf "pppoe-out1"
:local addr ""
:do { :set addr [/ip address get [find interface=$wanIf] address] } on-error={ :set addr "" }
:if ([:len $addr] > 0) do={
:local slash [:find $addr "/"]
:if ([:typeof $slash] = "num") do={ :set addr [:pick $addr 0 $slash] }
}
# 拨号没起来时接口上没有地址,这时候什么都不要推
:if ([:len $addr] = 0) do={
:log warning ("MaskTT DDNS: 接口 " . $wanIf . " 上没有地址,跳过本次")
} else={
# …… 这里是上报请求 ……
}
接口名要改成你实际的那个。在 Terminal 里敲 /interface print 看一眼,
PPPoE 拨号的一般叫 pppoe-out1,静态 IP 或 DHCP 的 WAN 口可能是 ether1。
#,
写成 :local wanIf "pppoe-out1" # 接口名 这样会直接报
expected end of command,脚本一行都跑不了。要加说明就单独起一行。/system scheduler add name=masktt-ddns interval=5m \
on-event="/system script run masktt-ddns" comment="MaskTT DDNS"
在 Log 里搜 MaskTT,正常会看到:
MaskTT DDNS: 解析已是最新 / 更新成功
如果看到 请求失败,多半是路由器的 DNS 有问题 —— 检查 IP → DNS 里有没有配可用的 DNS 服务器。
info 级别,RouterOS 默认有 topics=info action=memory 这条规则,
但不少人为了让 Log 窗口清爽会把它删掉 —— 删掉之后 :log info 就没有去处,
而 error / warning 还在,所以只看得到报错。先确认一下规则还在不在:
/system logging print
列表里如果没有 topics=info action=memory,加一条只收脚本日志的规则就行。
这样既能看到成功提示,又不会被系统其它 info 刷屏:
/system logging add topics=script,info action=memory
topics=script,info 只匹配「同时带 script 和 info 两个标签」的消息,
也就是脚本自己打的那几条,不会把系统日志一起放进来。
嫌麻烦就不用管路由器日志 —— 控制台的推送记录里每一次上报都有, 包括时间、写入的 IP 和结果,比路由器日志全。
check-certificate=no。RouterOS 默认没有内置 CA 证书库,不加这个参数请求会失败。想更严格的话,可以到 System → Certificates 导入 CA 证书后去掉该参数。/usr/bin/masktt-ddns.sh。chmod +x /usr/bin/masktt-ddns.shsh /usr/bin/masktt-ddns.sh*/5 * * * * /usr/bin/masktt-ddns.sh
编辑完记得重启 cron:/etc/init.d/cron restart
查看运行情况:logread | grep masktt-ddns
curl,执行 opkg update && opkg install curl ca-bundle 装一下。ca-bundle 也要装,否则 HTTPS 会因为验不了证书而失败。群晖不用装任何脚本,用系统自带的 DDNS 客户端就行。
home.example.com),
用户名和密码按控制台的提示填。不同厂商的用户名 / 密码对应关系:
| 解析商 | 用户名填 | 密码填 |
|---|---|---|
| 阿里云 | AccessKey ID | AccessKey Secret |
| DNSPod | Token 的 ID(逗号前) | Token 本体(逗号后) |
| Cloudflare | 固定填 token | API Token |
我们兼容 DynDNS v2 标准协议,所以大部分路由器和软路由内置的 DDNS 客户端都能直接用,不需要装脚本。
| 字段 | 填什么 |
|---|---|
| 服务器 / Server | ddns.masktt.com |
| 协议 / Service | Custom、DynDNS 或「自定义」 |
| 请求路径 / Path | /nic/<解析商>/update?hostname=…&myip=…&u=… |
| 用户名 / 密码 | 见上面群晖那张表 |
| 主机名 / Hostname | 完整域名,如 home.example.com |
| HTTPS | 开启 |
解析商代号分别是 aliyun、dnspod、cloudflare。完整地址在控制台的「通用 DDNS」标签页里会直接生成好,复制粘贴即可。
返回 good 或 nochg 表示成功,badauth 表示用户名密码不对。
C:\Scripts\masktt-ddns.ps1。如果提示脚本被策略阻止,先执行一次:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
和 OpenWrt 一样,用生成的 shell 脚本 + cron 即可。也可以直接用 systemd timer:
[Unit]
Description=MaskTT DDNS
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/masktt-ddns.sh
[Unit]
Description=MaskTT DDNS 每 5 分钟执行
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now masktt-ddns.timer
systemctl list-timers | grep masktt
如果你想自己写客户端,接口很简单。三家厂商各有一个入口,GET 和 POST 都支持。
https://ddns.masktt.com/aliddns/ 阿里云
https://ddns.masktt.com/dnspod/ DNSPod
https://ddns.masktt.com/cloudflare/ Cloudflare
https://ddns.masktt.com/update/ 统一入口(用 provider 参数指定厂商)
| 参数 | 必填 | 说明 |
|---|---|---|
| u | 是 | 你的上报标识,在账号设置里能看到 |
| domain | 是 | 主域名,如 example.com |
| record | 是 | 主机记录,如 home;根域名填 @ |
| id | 阿里云必填 | AccessKey ID |
| secret | 阿里云必填 | AccessKey Secret |
| token | DNSPod / CF 必填 | DNSPod 是 ID,Token;Cloudflare 是 API Token |
| type | 否 | A(默认)或 AAAA |
| ip | 否 | 不传则自动取你连过来的源 IP。推荐不传 |
| line | 否 | 仅 DNSPod:default ctc cucc cmcc cernet |
| proxied | 否 | 仅 Cloudflare:传 1 表示保持橙云代理 |
| format | 否 | 传 json 可拿到详细的失败原因 |
# 最简形式,IP 由服务端自动识别
curl "https://ddns.masktt.com/aliddns/?id=LTAI...&secret=...&domain=example.com&record=home&u=u..."
# 想看详细结果就加 format=json
curl "https://ddns.masktt.com/aliddns/?...&format=json"
{"success":true,"result":"updated","ip":"1.2.3.4","message":"1.2.3.3 → 1.2.3.4"}
默认返回一个数字,和旧版保持一致,方便老脚本直接迁移:
| 返回 | 含义 |
|---|---|
| 0 | 成功。包含「已更新」「已创建」和「IP 未变化无需更新」三种情况 |
| 1 | 失败。加 &format=json 可以看到具体原因 |
| 2 | 参数不完整或格式不对 |
加上 format=json 后,result 字段的取值:
| result | 含义 |
|---|---|
| updated | 解析记录已更新 |
| created | 记录原本不存在,已自动创建 |
| nochg | IP 没变,未调用云厂商接口 |
| fail | 调用云厂商接口失败,message 里是对方返回的原话 |
| badauth | u 参数无效,或账号未验证 / 已停用 |
| badparam | 参数有误 |
| ratelimit | 上报太频繁被限流 |
| cooldown | 连续失败次数过多,已暂停尝试 |
控制台 → 推送日志里有每一次上报的记录:时间、来源 IP、结果、失败原因。失败时会写明云厂商返回的原话,照着改就行,不用猜。
在请求地址后面加 &format=json 再跑一次,会直接告诉你原因。常见的几种:
| 提示 | 怎么办 |
|---|---|
| AccessKey ID 不存在 | ID 填错了,或者这个 RAM 子账号被删了 |
| AccessKey Secret 不正确 | Secret 填错,或复制时带了空格 |
| RAM 子账号没有 DNS 权限 | 回阿里云给它授 AliyunDNSFullAccess |
| 该域名不在这个账号下 | 域名的解析不在这家,或者填的是别的账号的凭证 |
| Token 不存在 | DNSPod Token 被删了,重新建一个 |
| Token 格式不对 | 要写成 ID,Token,中间英文逗号 |
| API Token 没有操作这个域名的权限 | Cloudflare 的 Zone Resources 没选对域名 |
这不是错误。 你的公网 IP 确实没变,所以我们没有去调云厂商接口 —— 这正是设计意图,避免浪费你的 API 配额。
想确认解析是不是对的,在电脑上执行:
nslookup home.example.com
# 或
dig +short home.example.com
如果不传 ip 参数,我们取的是你设备连过来时用的那个 IP。设备走 IPv4 连过来,我们就只能看到 IPv4,没法给你设 AAAA。
两个办法:
这就不是 DDNS 的问题了,按顺序查:
10.x、100.64–100.127.x、192.168.x 开头,
那是运营商给的内网地址,需要打电话让运营商开公网 IP。正常的 DDNS 用不到那么高的频率。我们的限制是单设备每分钟 30 次、单个域名每分钟 10 次。 如果撞到了,检查是不是定时任务设得太密,或者同一个脚本被重复添加了多次。
同一个域名连续失败 20 次后,我们会暂停尝试 10 分钟。 这是为了防止有人拿本站批量验证偷来的云厂商密钥。修正凭证后等一会儿会自动恢复,不用联系我们。
中转服务绕不开一件事:你的云厂商凭证要经过我们的服务器,因为签名是在我们这边做的。所以我们尽量把能做的都做了:
你也可以主动降低风险:
u 只是身份标识,不是密钥。它的作用是把推送记录归到你的账号名下,好让你能在控制台查日志。
别人就算拿到你的 u,也改不了你的解析 —— 改解析需要的是云厂商凭证,那个只在你自己的设备上。真要换,在账号设置里能重置(重置后所有设备的脚本都要重新生成)。