接入文档

从零开始把家里的动态 IP 接到自己的域名上,大约需要十分钟。 大部分步骤在控制台里会自动生成好脚本,这里讲的是原理、凭证怎么拿,以及出问题了怎么查。

三步接入

  1. 在解析商那里拿一把「只能改 DNS」的钥匙。 阿里云是 RAM 子账号的 AccessKey,DNSPod 是 API Token,Cloudflare 是 API Token。 具体步骤看下面「获取凭证」。
  2. 到控制台填域名和凭证,页面会实时生成脚本。 凭证只在你浏览器里拼进脚本,不会提交到我们的服务器。
  3. 把脚本粘到路由器或 NAS 上,设个定时任务。 建议 5 分钟一次。之后就不用管了。
你需要自己有一个域名,并且它的解析托管在阿里云、DNSPod 或 Cloudflare 其中一家。我们不提供域名。

工作原理

路由器每隔几分钟把自己当前的公网 IP 发给我们,我们替它完成云厂商接口的签名,然后更新你的解析记录。

流程
你的路由器  ──①上报当前 IP──▶  ddns.masktt.com
                                      │
                                      ②和上次比对
                                      │  IP 没变 → 直接返回,不往下走
                                      │  IP 变了 → 继续
                                      ▼
                                   ③签名并调用接口
                                      │
                                      ▼
                             阿里云 / DNSPod / Cloudflare
                                (你自己的账号)

为什么路由器不能自己去调? 阿里云要求把请求参数排序后算 HMAC-SHA1 签名,再做 percent-encode。RouterOS 的脚本语言里没有这些函数,实现不出来。这就是中转服务存在的意义。

第 ② 步很关键。 路由器通常 5 分钟上报一次,但家宽的公网 IP 可能几天才变一次。我们先在本地比对,IP 没变就直接返回,完全不碰云厂商接口 —— 这样你的 API 配额不会被白白消耗掉,也不会因为调用太频繁被限流。

获取凭证 · 阿里云 DNS

不要用主账号的 AccessKey。 那把钥匙能操作你阿里云账号里的一切,包括买服务器、删数据。一定要建一个只有 DNS 权限的 RAM 子账号。
  1. 登录阿里云控制台,搜索并进入 访问控制 RAM。
  2. 左侧 身份管理 → 用户 → 创建用户。登录名随便填,比如 masktt-ddns。
  3. 访问方式勾选 「使用永久 AccessKey 访问」,不要勾控制台登录。
  4. 创建完会显示 AccessKey ID 和 AccessKey Secret。 Secret 只显示这一次,立刻复制保存。
  5. 回到用户列表,点这个用户 → 权限管理 → 新增授权, 搜索并勾选 AliyunDNSFullAccess。

如果你想再收紧一点,可以不授 AliyunDNSFullAccess,而是自定义一个只能操作单个域名的权限策略:

自定义权限策略(可选,更安全)
{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "alidns:DescribeDomainRecords",
        "alidns:DescribeSubDomainRecords",
        "alidns:AddDomainRecord",
        "alidns:UpdateDomainRecord"
      ],
      "Resource": "*"
    }
  ]
}

获取凭证 · DNSPod

  1. 登录 DNSPod 控制台(腾讯云用户也可以从腾讯云进 DNSPod)。
  2. 右上角头像 → 密钥管理 → DNSPod Token → 创建密钥。
  3. 会得到一个 ID(一串数字)和一个 Token(一串字母数字)。
  4. 填进控制台时要写成 ID,Token 的形式,中间是英文逗号,不要有空格。
格式示例
ID     : 123456
Token  : abcdef0123456789abcdef0123456789

填写时 : 123456,abcdef0123456789abcdef0123456789

DNSPod 支持分线路解析(电信 / 联通 / 移动 / 教育网)。如果你的域名在不同线路配了不同记录,在控制台里选对线路,我们只会更新那一条,不影响其它线路。

获取凭证 · Cloudflare

不要用 Global API Key。 那个能操作你 Cloudflare 账号下所有域名。请用 API Token,并且限定到单个域名。
  1. 登录 Cloudflare,右上角头像 → My Profile → API Tokens。
  2. Create Token → 找到模板 Edit zone DNS → Use template。
  3. Zone Resources 选 Include → Specific zone → 你的域名。 这一步很重要,不选就是账号下所有域名都能改。
  4. Continue → Create Token,复制那串 Token。只显示一次。

关于橙云代理

Cloudflare 的记录有「已代理」(橙云)和「仅 DNS」(灰云)两种状态。做家宽回源时,通常要选「仅 DNS」。

控制台里那个「更新后保持橙云代理开启」的勾选框,默认不勾就是仅 DNS。

设备配置 · RouterOS

适用于 RouterOS v6 和 v7。在控制台生成脚本后:

  1. System → Scripts → Add,Name 填 masktt-ddns,把生成的脚本粘进 Source。
  2. 把 Policy 全部勾上(见下面的说明),保存。
  3. 打开 Terminal,执行控制台一并给出的 Scheduler 命令。
脚本权限不勾全,fetch 跑不起来。 这是 RouterOS 上最常见的坑:新建脚本时 Policy 默认并不会全勾,而 /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 接口地址的部分
# 下面这行改成你的 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。

改脚本时不要在命令后面加注释。 RouterOS 只认行首的 #, 写成 :local wanIf "pppoe-out1"  # 接口名 这样会直接报 expected end of command,脚本一行都跑不了。要加说明就单独起一行。
拿不准就先用「由服务端识别」。 它对两种接入方式都有效 —— 光猫拨号时它是唯一正确的做法, 路由器拨号时它拿到的也是同一个公网 IP。只有在多拨、或出口被代理改写的场景下才必须改成读接口。
定时任务
/system scheduler add name=masktt-ddns interval=5m \
  on-event="/system script run masktt-ddns" comment="MaskTT DDNS"

怎么确认它在跑

在 Log 里搜 MaskTT,正常会看到:

Log
MaskTT DDNS: 解析已是最新 / 更新成功

如果看到 请求失败,多半是路由器的 DNS 有问题 —— 检查 IP → DNS 里有没有配可用的 DNS 服务器。

一条日志都没有,但解析确实更新了? 那是成功日志被路由器的日志规则丢掉了。 成功走的是 info 级别,RouterOS 默认有 topics=info action=memory 这条规则, 但不少人为了让 Log 窗口清爽会把它删掉 —— 删掉之后 :log info 就没有去处, 而 error / warning 还在,所以只看得到报错。

先确认一下规则还在不在:

Terminal
/system logging print

列表里如果没有 topics=info action=memory,加一条只收脚本日志的规则就行。 这样既能看到成功提示,又不会被系统其它 info 刷屏:

Terminal
/system logging add topics=script,info action=memory

topics=script,info 只匹配「同时带 script 和 info 两个标签」的消息, 也就是脚本自己打的那几条,不会把系统日志一起放进来。

嫌麻烦就不用管路由器日志 —— 控制台的推送记录里每一次上报都有, 包括时间、写入的 IP 和结果,比路由器日志全。

脚本里用了 check-certificate=no。RouterOS 默认没有内置 CA 证书库,不加这个参数请求会失败。想更严格的话,可以到 System → Certificates 导入 CA 证书后去掉该参数。

设备配置 · OpenWrt / iStoreOS

  1. SSH 登录路由器,把控制台生成的脚本存为 /usr/bin/masktt-ddns.sh。
  2. 加执行权限:chmod +x /usr/bin/masktt-ddns.sh
  3. 先手动跑一次试试:sh /usr/bin/masktt-ddns.sh
  4. 确认没问题后加进定时任务。
crontab -e
*/5 * * * * /usr/bin/masktt-ddns.sh

编辑完记得重启 cron:/etc/init.d/cron restart

查看运行情况:logread | grep masktt-ddns

精简版 OpenWrt 可能没装 curl,执行 opkg update && opkg install curl ca-bundle 装一下。ca-bundle 也要装,否则 HTTPS 会因为验不了证书而失败。

设备配置 · 群晖 NAS

群晖不用装任何脚本,用系统自带的 DDNS 客户端就行。

  1. 控制面板 → 外部访问 → DDNS,点 自定义提供商。
  2. 新增,服务提供商名称随便填,查询 URL 用控制台「群晖」标签页给出的那一串。
  3. 回到 DDNS 列表 新增,服务提供商选刚才建的那个。
  4. 主机名称填完整域名(比如 home.example.com), 用户名和密码按控制台的提示填。
  5. 点 测试连接,显示「正常」就保存。

不同厂商的用户名 / 密码对应关系:

解析商用户名填密码填
阿里云AccessKey IDAccessKey Secret
DNSPodToken 的 ID(逗号前)Token 本体(逗号后)
Cloudflare固定填 tokenAPI Token

设备配置 · 华硕 / 爱快 / pfSense 等

我们兼容 DynDNS v2 标准协议,所以大部分路由器和软路由内置的 DDNS 客户端都能直接用,不需要装脚本。

字段填什么
服务器 / Serverddns.masktt.com
协议 / ServiceCustom、DynDNS 或「自定义」
请求路径 / Path/nic/<解析商>/update?hostname=…&myip=…&u=…
用户名 / 密码见上面群晖那张表
主机名 / Hostname完整域名,如 home.example.com
HTTPS开启

解析商代号分别是 aliyun、dnspod、cloudflare。完整地址在控制台的「通用 DDNS」标签页里会直接生成好,复制粘贴即可。

返回 good 或 nochg 表示成功,badauth 表示用户名密码不对。

设备配置 · Windows

  1. 把控制台生成的 PowerShell 脚本存成 C:\Scripts\masktt-ddns.ps1。
  2. 以管理员身份打开 PowerShell,执行控制台给出的注册计划任务命令。

如果提示脚本被策略阻止,先执行一次:

PowerShell(管理员)
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

设备配置 · Linux

和 OpenWrt 一样,用生成的 shell 脚本 + cron 即可。也可以直接用 systemd timer:

/etc/systemd/system/masktt-ddns.service
[Unit]
Description=MaskTT DDNS
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/masktt-ddns.sh
/etc/systemd/system/masktt-ddns.timer
[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
tokenDNSPod / CF 必填DNSPod 是 ID,Token;Cloudflare 是 API Token
type否A(默认)或 AAAA
ip否不传则自动取你连过来的源 IP。推荐不传
line否仅 DNSPod:default ctc cucc cmcc cernet
proxied否仅 Cloudflare:传 1 表示保持橙云代理
format否传 json 可拿到详细的失败原因

调用示例

curl
# 最简形式,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记录原本不存在,已自动创建
nochgIP 没变,未调用云厂商接口
fail调用云厂商接口失败,message 里是对方返回的原话
badauthu 参数无效,或账号未验证 / 已停用
badparam参数有误
ratelimit上报太频繁被限流
cooldown连续失败次数过多,已暂停尝试

故障排查

第一步:去看日志

控制台 → 推送日志里有每一次上报的记录:时间、来源 IP、结果、失败原因。失败时会写明云厂商返回的原话,照着改就行,不用猜。

返回 1,但不知道为什么

在请求地址后面加 &format=json 再跑一次,会直接告诉你原因。常见的几种:

提示怎么办
AccessKey ID 不存在ID 填错了,或者这个 RAM 子账号被删了
AccessKey Secret 不正确Secret 填错,或复制时带了空格
RAM 子账号没有 DNS 权限回阿里云给它授 AliyunDNSFullAccess
该域名不在这个账号下域名的解析不在这家,或者填的是别的账号的凭证
Token 不存在DNSPod Token 被删了,重新建一个
Token 格式不对要写成 ID,Token,中间英文逗号
API Token 没有操作这个域名的权限Cloudflare 的 Zone Resources 没选对域名

日志里一直是「IP 未变」,解析对吗?

这不是错误。 你的公网 IP 确实没变,所以我们没有去调云厂商接口 —— 这正是设计意图,避免浪费你的 API 配额。

想确认解析是不是对的,在电脑上执行:

验证解析
nslookup home.example.com
# 或
dig +short home.example.com

AAAA 记录拿不到 IPv6

如果不传 ip 参数,我们取的是你设备连过来时用的那个 IP。设备走 IPv4 连过来,我们就只能看到 IPv4,没法给你设 AAAA。

两个办法:

域名解析对了,但外面连不上

这就不是 DDNS 的问题了,按顺序查:

  1. 你家真的有公网 IP 吗? 在路由器上看 WAN 口 IP,如果是 10.x、100.64–100.127.x、192.168.x 开头, 那是运营商给的内网地址,需要打电话让运营商开公网 IP。
  2. 端口转发配了吗? 路由器上要把外部端口映射到内网设备。
  3. 运营商封端口了吗? 国内家宽普遍封 80 和 443,换成 8080、8443 这类高位端口试试。
  4. Cloudflare 开了橙云吗? 开了的话非 Web 端口连不上,改成「仅 DNS」。

提示被限流

正常的 DDNS 用不到那么高的频率。我们的限制是单设备每分钟 30 次、单个域名每分钟 10 次。 如果撞到了,检查是不是定时任务设得太密,或者同一个脚本被重复添加了多次。

连续失败后被暂停

同一个域名连续失败 20 次后,我们会暂停尝试 10 分钟。 这是为了防止有人拿本站批量验证偷来的云厂商密钥。修正凭证后等一会儿会自动恢复,不用联系我们。

关于密钥安全

中转服务绕不开一件事:你的云厂商凭证要经过我们的服务器,因为签名是在我们这边做的。所以我们尽量把能做的都做了:

你也可以主动降低风险:

控制台生成脚本时凭证不经过服务器。 那个页面是纯前端拼接的,你填的 AccessKey 只在浏览器内存里。可以打开浏览器开发者工具的网络面板自己验证。

u 参数是什么?会泄露吗?

u 只是身份标识,不是密钥。它的作用是把推送记录归到你的账号名下,好让你能在控制台查日志。

别人就算拿到你的 u,也改不了你的解析 —— 改解析需要的是云厂商凭证,那个只在你自己的设备上。真要换,在账号设置里能重置(重置后所有设备的脚本都要重新生成)。