OpenWrt Cloudflare DDNS

2022-11-27 ⏳3.7分钟(1.5千字)

最近更新路由器系统,把 DDNS 搞坏了。路由系统是 OpenWrt,DDNS 服务使用 Cloudflare。上次配置就折腾了好久,忘记记录了。这次又折腾了一遍。趁此机会整理成文,分享给大家。

首先,我们需要创建接口令牌(token),用于调用 Cloudflare API。这一步需要在 Cloudflare 管理后台完成。

Cloudflare API Token

点击右上角用户图标,在弹出菜单中点击 My Profile 打开新页面。在左边栏中点击 API Tokens,再点击右边的 Create Token。然后从 API token templates 选 Edit zone DNS,点击右边的 Use Template。接着在 Zone Resources 点击最后的下拉菜单,选择你要修改的域名。最后点击 Continue To summary,系统会显示对应的域名信息。点击 Create Token 完成创建。此时系统会显示新创建的令牌。注意,只会显示一次,请务必保存好。如果弄丢了,只能创建新令牌。

有了令牌,接下来我们安装所需的软件包。

OpenWrt DDNS Packages

我们可以在 OpenWrt 后台 System -> Software 页面安装,也可以直接使用 opkg 安装。所需要的软件包有:

luci-app-ddns 用于支持在管理后台界面配置 DDNS,ddns-scripts-cloudflare用于调用 Cloudflare API。DDNS 功能还依赖其他一些工具包,OpenWrt 会自动安装。

安装之后刷新页面,顶栏会出现 Services 菜单,里面有 Dynamic DNS 服务。点击打开 DDNS 配置页面。

配置 Cloudflare DDNS

点击左下角 Add new services… 按钮,需要指定以下字段:

字段 取值 说明
Name 配置名 可以随便填,比如 cf4
IP Address Version IP地址类型 可以选 IPv4 和 IPv6,一般选 IPv4
DDNS Service provider DDNS 服务商 本文需要选 cloudflare.com-v4

点击创建服务,会弹出新的配置菜单。字段有点多,必填字段如下:

字段 取值 说明
Lookup Hostname 验证 IP 映射所用的域名 其实就是 DDNS 使用的域名
Domain DDNS 使用的域名 子域名需要使用 @ 连接,比如 ddns@example.net
Username API 认证用户名 固定取值为 Bearer
Password API 认证令牌 就是前面申请的令牌
Use HTTP Secure 使用 HTTPS 调用接口 必须勾选
Path to CA… /etc/ssl/certs CA证书文件路径

其他字段保持不变就行。最后点击 Save 保存。

排查问题

创建好配置后,还需要点击最下面的 Save & Apply 按钮。最后可以点击配置右侧的 Reload 按钮。

但实际操作后会发现,DDNS 并没有生效。我从网上没有找到解决办法,只能自己调试一番。

首先看日志。点击配置上的 Edit 按钮,切换到 Log File Viewer 标签,再点击 Read / Reread log file,这时就能看到日志。最可疑的一行如下:

WARN : No update_url found/defined or no update_script found/defined! - TERMINATE

说是没找到 update_url 或者 update_script。但从哪里找呢?不好说,只能想办法看代码。但我不知道这条消息是由哪个脚本输出的。于是只能逐个排查。

我们前面说过,DDNS 依赖好几个包,OpenWrt 会自动安装。其中最主要的是 ddns-scripts 这个包,它可以说是整个 DDNS 的基础框架。我们看一下它有多少文件:

opkg files ddns-scripts
Package ddns-scripts (2.8.2-12) is installed on root and has the following files:
/usr/lib/ddns/dynamic_dns_updater.sh
/usr/bin/ddns
/etc/init.d/ddns
/etc/hotplug.d/iface/95-ddns
/etc/config/ddns
/usr/lib/ddns/dynamic_dns_functions.sh
/usr/share/ddns/version
/usr/lib/ddns/dynamic_dns_lucihelper.sh

比较可疑的是dynamic_dns_updater.sh,搜索脚本内容:

grep 'No update_url found' /usr/lib/ddns/dynamic_dns_updater.sh
[ -z "$update_url" -a -z "$update_script" ] && write_log 14 "No update_url found/defined or no update_script found/defined!"

运气比较好,居然找到了报错代码。打开文件,报错代码的前一行是获取 update_url 或者 update_script 信息:

[ -n "$service_name" ] && get_service_data update_url update_script UPD_ANSWER

这里调用了get_service_data函数。我们需要找到这个函数的源码。dynamic_dns_functions.sh 比较可疑,搜一下:

grep get_service_data /usr/lib/ddns/dynamic_dns_functions.sh
get_service_data() {
    [ $# -ne 3 ] && write_log 12 "Error calling 'get_service_data()' - wrong number of parameters"

果然在此,核心代码片段如下:

get_service_data() {
    [ $# -ne 3 ] && write_log 12 "Error calling 'get_service_data()' - wrong number of parameters"
    __FILE="/etc/ddns/services"
    [ $use_ipv6 -ne 0 ] && __FILE="/etc/ddns/services_ipv6"
    mkfifo pipe_$$
    sed '/^#/d; /^[ \t]*$/d; s/\"//g' $__FILE >pipe_$$ &
    while read __SERVICE __DATA __ANSWER; do
        if [ "$__SERVICE" = "$service_name" ]; then
            __URL=$(echo "$__DATA" | grep "^http")
            [ -z "$__URL" ] && __SCRIPT="/usr/lib/ddns/$__DATA"

它从 /etc/ddns/services 逐行读取内容,然后通过read __SERVICE __DATA __ANSWER 提取每个字段。从__URL的赋值语句可以判定,services文件中第二列如果以 http 开头,就表示 URL,否则表示脚本路径。而且这个路径对应 /usr/lib/ddns/ 目录下的脚本文件。

ls -1 /usr/lib/ddns/
dynamic_dns_functions.sh
dynamic_dns_lucihelper.sh
dynamic_dns_updater.sh
update_cloudflare_com_v4.sh

所以我们需要在 /etc/ddns/services 中添加一行:

"cloudflare.com-v4"     "update_cloudflare_com_v4.sh"

保存后在 OpenWrt 后台重新加载 DDNS 配置并点击 Reload 按钮,至此完成 DDNS 全部配置。这时可以通过日志看到系统已经正常调用 Cloudflare API 了。最多等待10分钟,DNS更新就会生效。

IPv6 配置

OpenWrt DDNS 支持绑定 IPv6 地址。我们需要添加单独的配置,地址类型选 IPv6。其他配置跟 IPv4 一模一样。唯一需要注意的是 IPv6 需要额外指定网卡。也就是在配置的 Advanced Settings 标签中,把 Network 指定为 wan6,不然会因为无法获取 IPv6 地址而报错。

GL-AX1800

我当前使用 GL-AX1800,由广联智通(GL.iNet)生产,算是国内比较早支持 WiFi 6 的路由器。但是 GL-AX1800 用的是定制过的 OpenWrt,经常遇到一些杂七杂八的问题。

比如前一阵子升级系统后 DDNS 就出问题了。看起来是 DDNS 程序没有运行,日志也查不到。无奈之下,我尝试查看 DDNS 服务脚本源代码。

脚本路径为 /etc/rc.d/{K10,S95}ddns。这里的K表示Kill,用来关闭服务;S表示Start,用来启动服务。

仔细看脚本代码,发现一个start函数:

start() {
        enable=`uci -q get ddns.glddns.enabled`
        if [ "$enable" = 1 ];then
                $PROG -- start
        fi
}

再仔细看enable变量的取值命令uci -q get ddns.glddns.enabled。注意里面的glddns,这是 GL-AX1800 定制的变量,标准 OpenWrt 里没有这个变量。手工执行输出空,说明没有设置,自然也就不会执行后面的\$PROG -- start了。

所以我们需要在/etc/config/ddns中添加如下配置:

config ddns 'glddns'
        option enabled 1

重启后恢复正常。

总结

OpenWrt 虽然支持 DDNS 功能,但针对 Cloudflare 的默认配置还有很多问题。大家在使用的时候还需要一番折腾。不过用 OpenWrt 的用户谁又不在折腾呢?与诸君共勉😄

参考链接: