基于Cloudflare的IPv6动态域名解析DDNS

前置条件

  • 一个域名
  • 域名解析托管到Cloudflare
  • 可以访问IPv6网站
  • Openwrt或者其他系统的路由器(有DDNS功能)/Linux系统服务器

实现步骤

Cloudflare设置

在Cloudflare设置好子域名xx.example.comAAAA记录解析到任意的IPv6地址,比如2400::1,而且关闭代理状态,就是那个云朵点成灰色。

记录从Cloudflare获取的区域 ID(zone id)和Global API Key。

有DDNS功能的路由器设置

手里有一台刷了openwrt的Newifi3 D2,所以以此为例。

添加名为myddns_ipv6(可以自定义)的配置,并且

  • 在查询主机名位置填入xx.example.com
  • IP 地址版本选择IPv6
  • DDNS 服务提供商 [IPv6] 选择cloudflare.com-v4
  • 域名`[email protected]`(必须是这个中间有@的格式);
  • 用户名填入登陆Cloudflare的邮箱;
  • 密码填入的是Global API Key
  • 勾选使用HTTPS;
  • 高级设置中IP 地址来源 [IPv6] 选择URL
  • 用于检测的 URL [IPv6] 默认或者填入http://checkipv6.dyndns.com
  • 事件网络 [IPv6] 选择wan
  • 最后保存并启用。

如果路由器有内置的DDNS,并且DDNS 服务提供商 [IPv6]中有cloudflare.com-v4,就可以完全不用理会其实现原理,填入以上信息即可动态解析。

Linux系统服务器设置

这里是对上述路由器设置的展开。

首先,获取本地IPv6地址

可以通过本地模式,也可以通过网络模式。

本地模式是在命令行执行命令

1
ip addr show $eth_card | grep -v deprecated | grep -A1 'inet6 [^f:]' |grep -v ^-- |sed -nr ':a;N;s#^ +inet6 ([a-f0-9:]+)/.+? scope global .*? valid_lft ([0-9]+sec) .*#\2 \1#p;Ta' | sort -nr | head -n1 | cut -d' ' -f2

即可得到一个IPv6的网址,其中eth0是网卡的名称,可以通过ip addr看到该名称。

网络模式是直接执行curl -6 ip.sb就可以得到网络IPv6地址。其中ip.sb就是一个网址,通过指定的参数就可以获取到IPv6地址。

测试后,比较倾向于网络模式,这样获得的肯定是公网地址。

然后得到Cloudflare一些参数

前面已经得到了区域 ID(zone id)和Global API Key,在这里分别记为zone_idauth_key,另外还需要登陆Cloudflare的邮箱auth_email,域名xx.example.com记为domain_name

然后通过接口

1
2
3
4
curl -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=AAAA&name=$domain_name&content=127.0.0.1&page=1&per_page=20&order=type&direction=desc&match=any" \
-H "X-Auth-Email: $auth_email" \
-H "X-Auth-Key: $auth_key" \
-H "Content-Type: application/json"

这里返回的是所有的AAAA记录,找到xx.example.com对应的ID并记为dns_id

最后,向Cloudflare更新新的解析IP

这里有一种讨巧的办法,本地ipv6.txt用来存储IPv6地址,每次推送Cloudflare成功后,同时更新这个文件中存储的地址。

于是我只需要将前面不管是本地模式还是网络模式获取到的IP地址,和这里存储的IP地址对比,即可得到本地IP和Cloudflare的解析IP是否相同,如果不一样的话,用下面的API更新解析IP。

1
2
3
4
5
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$dns_id"\
-H "X-Auth-Email: $auth_email" \
-H "X-Auth-Key: $auth_key" \
-H "Content-Type: application/json" \
--data '{"type":"AAAA","name":"'"$domain_name"'","content":"'"$New_IP6"'","ttl":1,"proxied":false}'

将以上思路整合为脚本ipv6.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash
auth_email="***@***.com"
auth_key="***"
zone_id="***"
dns_id="***"
domain_name="xx.example.com"

eth_card="eth0"
ip_file="/path/to/file/ipv6.txt"

New_IP6=$(curl -6 ip.sb)
#New_IP6=$(curl -s 'https://api6.ipify.org')
#以上是网络方式获取最新IPv6地址

#New_IP6=`ip -6 addr show dev $eth_card | grep global | awk '{ print $2}' | awk -F "/" '{print $1}' | head -1`
#New_IP6=`ip addr show $eth_card | grep -v deprecated | grep -A1 'inet6 [^f:]' |grep -v ^-- |sed -nr ':a;N;s#^ +inet6 ([a-f0-9:]+)/.+? scope global .*? valid_lft ([0-9]+sec) .*#\2 \1#p;Ta' | sort -nr | head -n1 | cut -d' ' -f2`
#以上是本地方式获取最新IPv6地址

Old_IP6=$(cat $ip_file)

if [ $New_IP6 == $Old_IP6 ]; then
echo "IP has not changed."
exit 0
fi

update_dns=$(curl -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$dns_id"\
-H "X-Auth-Email: $auth_email" \
-H "X-Auth-Key: $auth_key" \
-H "Content-Type: application/json" \
--data '{"type":"AAAA","name":"'"$domain_name"'","content":"'"$New_IP6"'","ttl":1,"proxied":false}')

if [[ $update_dns == *"\"success\":true"* ]]; then
echo "DNS Update to '$New_IP6' Successfully."
echo "$New_IP6" > $ip_file
else
echo "Something wrong. Please check the message:"
echo $update_dns
exit 1
fi

然后将上述脚本每隔10分钟执行一次即可。

能用就行的特殊路由器

另外有一台openwrt的K3路由器,有DDNS功能,但是没有cloudflare.com-v4选项,于是只能想办法能用就行。

参考Linux服务器的脚本,修改dns_id,domain_name,ip_file等指定参数,上传到一个位置比如/home/ftp/(就是举个例子,路由器有些位置不清楚还是不要乱动,怕出错也怕重启丢失)。

然后将DDNS功能中:

  • DDNS 服务提供商 [IPv6] 修改为 自定义
  • 自定义更新脚本修改为上述脚本的存放位置;
  • 用户名、密码、参数等都留空。

然后保存、启用即可。

对于这里我没有更多的研究,这样填可能是有冗余的步骤,但是我的目的暂时是能用就行。

最后

  • IPv6是公网地址,不用在路由器设置端口转发,直接在路由器防火墙中的通信规则中开放指定端口即可;
  • 最好不要使用常用的端口,防止被人猜到后恶意攻击;
  • 必须使用IPv6客户端才能打开IPv6网站;
  • 没有研究Window平台的DDNS,因为我用不到;
  • 就这些吧。

参考资料