MENU

【歪门邪道】使用Cloudflare作为ddns更新IPv6域名DNS

November 1, 2024 • 瞎折腾

上一篇文章无意中发现了我的公网IPv6地址是能够联通的,并且实际联机体验较为不错。但由于没钱买商宽,所以没有静态IP,而每次IP变动都会带来麻烦,因此本篇将探索如何使用Cloudflare API来更新域名DNS,四舍五入成为一个dDNS。

IPv6公网地址虽然好,但毕竟是动态的。虽然Minecraft本身对于服务器的地址没什么要求,但一些mod,例如小地图,它使用服务器的地址来区分数据。也就是说,当IP变动时,即便服务器还是原来的服务器,但对于小地图mod来说,这就是一个新的服务器,因此之前加载的地图缓存和路经点也就全没了。

前些日子刚好想起来我还有个域名托管在Cloudflare,主要是利用它的tunnel服务来将内网或一些临时性的服务分享到公网上(一方面是懒得更新DNS,另一方面是懒得搞SSL证书)。我原本也想用tunnel服务将Minecraft服务器映射出去,但简单测试后发现延迟波动比较大,而且延迟普遍有点高,不适合联机。这时我突然意识到,Cloudflare提供了完备的API,即便官方没有提供ddns客户端,我也可以通过调用API的方式更新域名的DNS记录。

想来想去,由于我用的是Linux系统,因此实现的最佳方式便是bash,恰好Cloudflare的API文档也提供了多种多样的调用例子,其中就包含了Shell/curl:https://developers.cloudflare.com/api/operations/zones-get

要访问Cloudflare API,第一步就是要获取访问密钥,也就是所谓的API Token,这一步在账户设置里就可以找到。关键点在于创建的Token不光要能Edit Zone设置,还要能Read,不然后面第一步获取id就失败了。我一开始以为Edit蕴含了Read权限,所以是我调用的方式不对,结果发现是我token的权限没给。

api_email="your login email address"
api_key="your api token"

在Cloudflare中,网页中显示的website,实际上是DNS Zone。要操作一个DNS Zone,我们不能直接用名字,我们要用它的ID。根据文档,我们可以直接按照Zone的名字(也就是域名)进行筛选:

zone_name="example.com"
zone_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" \
            -H "X-Auth-Email: $api_email" \
            -H "Authorization: Bearer $api_key" \
            -H "Content-Type: application/json" | jq -r .result[0].id )
echo Zone $zone_name id is: $zone_id

这里我们用jq这个工具从返回的Json中获取我们想要的结果。

拿到Zone ID之后,若要修改一个记录(Record),我们同样需要ID。根据文档,我们使用记录的完整域名和zone id进行查询:

record_name="ddns.example.com"
record_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=AAAA&name=$record_name" \
            -H "X-Auth-Email: $api_email" \
            -H "Authorization: Bearer $api_key" \
            -H "Content-Type: application/json"  | jq -r .result[0].id )
echo Record $record_name id is: $record_id

同样地,使用jq来解析Json。

拿到了这两个ID我们就可以更新DNS记录了。这里要先在网页面板上创建好一个AAAA记录(用于IPv6),至于实际地址可以随便写,比如::1。关于TTL,因为IP可能会随时变动,因此直接设置为最小值,以便IP更新后朋友那边可以直接获取到最新的记录。那么,IPv6怎么获取呢?经过一番awk的现学现卖,我得到了如下命令:

v6Addr=$(ip -6 addr show scope global | grep /128 | awk -F '[ \t]+|/' '{print $3}' | grep -v ^fd7a)

在上一篇文章中可以得知,我的NUC是能够获得一个/128的公网IP。我先通过ip addr命令的变体找出所有公网IPv6地址,然后寻找后缀是/128的地址,使用awk命令提取出IP,随后排除掉来自tailscale的fd7a开头的地址。最后将字符串形式的IP存入到v6Addr这个变量中。

根据文档,我们可以使用PATCH动作来只更新我们需要的字段:

result_json=$(curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$record_id" \
            -H "X-Auth-Email: $api_email" \
            -H "Authorization: Bearer $api_key" \
            -H "Content-Type: application/json" \
            --data "{\"type\":\"AAAA\",\"content\":\"$v6Addr\"}")
# check the result and print
if [[ "$(echo $result_json | jq .success)" == "true" ]]; then
    echo Done!
else
    echo -e "Failed to update DDNS. CF API result: $result_json"
    exit 1
fi

这里我们确保更新的是AAAA记录,并且记录的内容是我们获取到的公网IP。最后同样使用jq判断一下返回的数据是否包含成功。成功则直接退出,若失败则将结果打印到控制台中。

至此,我的朋友和我联机时就再也不用把小地图和路经点复制来复制去了。

完整版脚本

这个脚本非常原始,中间命令没有错误检测,并且只能设置IPv6,并且IP只能从本地网卡获得。使用前请务必确认你理解这个脚本的全部内容,使用此脚本的风险和后果由你自行承担。

#!/bin/bash
# basic info
api_email="your login email address"
api_key="your api token"
zone_name="example.com"
record_name="ddns.example.com"
# the command to pull ipv6 address
v6Addr=$(ip -6 addr show scope global | grep /128 | awk -F '[ \t]+|/' '{print $3}' | grep -v ^fd7a)
echo Using IPv6 address: $v6Addr
# Call CF API to get zone id and record id
zone_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" \
            -H "X-Auth-Email: $api_email" \
            -H "Authorization: Bearer $api_key" \
            -H "Content-Type: application/json" | jq -r .result[0].id )
echo Zone $zone_name id is: $zone_id
# record id
record_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=AAAA&name=$record_name" \
            -H "X-Auth-Email: $api_email" \
            -H "Authorization: Bearer $api_key" \
            -H "Content-Type: application/json"  | jq -r .result[0].id )
echo Record $record_name id is: $record_id
# Update the record
result_json=$(curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$record_id" \
            -H "X-Auth-Email: $api_email" \
            -H "Authorization: Bearer $api_key" \
            -H "Content-Type: application/json" \
            --data "{\"type\":\"AAAA\",\"content\":\"$v6Addr\"}")
# check the result and print
if [[ "$(echo $result_json | jq .success)" == "true" ]]; then
    echo Done!
else
    echo -e "Failed to update DDNS. CF API result: $result_json"
    exit 1
fi

-全文完-


知识共享许可协议
【歪门邪道】使用Cloudflare作为ddns更新IPv6域名DNS天空 Blond 采用 知识共享 署名 - 非商业性使用 - 相同方式共享 4.0 国际 许可协议进行许可。
本许可协议授权之外的使用权限可以从 https://skyblond.info/about.html 处获得。

Archives QR Code
QR Code for this page
Tipping QR Code