利用 CloudFlare API 实现自动 DDNS 功能|支持IPv4|IPv6

小助手读文章 00:00 / 00:00

温馨提示:
本文所述内容具有依赖性,可能因软硬条件不同而与预期有所差异,故请以实际为准,仅供参考。

不知道什么时候起,宽带变成了 N 级 NAT,花生壳、ORAY、NO-IP 等基于动态公网 IP 的 DDNS 服务全线倒地,虽然 FRPNGROK 这些内网穿透依然可以实现类 DDNS 功能,但网络严重依赖于中转服务器带宽,不是非常适合有大流量需求的业务,比如监控、云盘、同步等等。

最近发现本地宽带终于支持 IPv6 了,DDNS 就又可以搞起来了,考虑到安全问题,本文将探讨使用 CloudFlare API 来实现 DDNS 功能。

对了,DDNS 是指动态 DNS(英语:Dynamic DNS)是域名系统(DNS)中的一种自动更新名称服务器(Name Server)内容的技术,根据互联网的域名订立规则,域名必须有固定的 IP 地址,动态 DNS 系统就是为动态 IP 提供一个固定的名称服务器(Name Server),通过即时更新,使外界用户能够连上使用动态 IP 用户的网址。


相关文章:《利用华为云 API 实现自动 DDNS 功能|支持IPv4|IPv6


一、思路

虽然现在上了 IPv6,可以直接与外界通信,但仍然不是静态 IP,即重新拨号或重新联网后,IP 会发生变更,根据 DDNS 原理,可以通过实时监测公网 IP,判断是否发生变更,一旦监测到发生变更,则立即更新 DNS 记录。

二、实现

CloudFlare 以前就有介绍过,是一家功能十分强大的电信服务提供商,今天我们就是通过其 DNS 管理和 API 功能来实现上述思路。

1、SHELL 脚本代码

#!/bin/bash

###############  授权信息(需修改成你自己的) ################ 
# CloudFlare 注册邮箱
auth_email="vircloud.net" 
# CloudFlare Global API Key,下一节会说到
auth_key="vircloud.net"  
# 做 DDNS 的根域名
zone_name="vircloud.net" 
# 做 DDNS 的域名,创建成功后就是通过该域名访问内网资源
record_name="cu.vircloud.net"

######################  修改配置信息 ####################### 
# 域名类型,IPv4 为 A,IPv6 则是 AAAA
record_type="A"
# IPv6 检测服务,本站检测服务仅在大陆提供
#ip=$(curl -s https://ipv6.vircloud.net)
# IPv4 检测服务
ip=$(curl -s https://ipv4.vircloud.net)
# 也可以获取远程主机 IPv4 地址
#ip=`sshpass -p 'password' ssh [email protected] 'curl -s https://ipv4.vircloud.net'`
# IPv4 解析检测
eip=`curl -s -H "accept: application/dns-json" "https://doh.360.cn/resolve?name=$record_name&type=A"| jq -r ".Answer[] | select(.type == 1) | .data"`
# 文件保存地址
#current_dir=$(cd `dirname $0`; pwd)
current_dir="/var/log/ddns-cf"
# 变动前的公网 IP 保存位置
ip_file="$current_dir/ip.txt"
# 域名识别信息保存位置
id_file="$current_dir/cloudflare.ids"
# 监测日志保存位置
log_file="$current_dir/cloudflare.log"

################### 判断日志文件夹是否存在 ##################
if [ ! -d "$current_dir" ]; then
     mkdir -p $current_dir
fi

######################  监测日志格式 ######################## 
log() {
    if [ "$1" ]; then
        echo -e "[$(date)] - $1" >> $log_file
    fi
}
log "Check Initiated"

###################### 简单判断是否是 IP ####################
if [ "$ip" != "${1#*[0-9].[0-9]}" ]; then
     log "IPv4 address detected: $ip"
elif [ "$ip" != "${1#*:[0-9a-fA-F]}" ]; then
     log "IPv6 address detected: $ip"
else
     log "No valid IP address detected"
     log "Check Done"
     exit 0
fi

######################  判断 IP 是否变化 #################### 
if [ -f $ip_file ]; then
    old_ip=$(cat $ip_file)
else
    old_ip=$eip
    echo "$eip" > $ip_file
fi
log "Last recorded IP: $old_ip"
if [ "$ip" == "$old_ip" ]; then
   echo "IP has not changed."
   log "IP has not changed."
   log "Check Done"
   exit 0
else
   echo "IP has changed, updating..."
   log "IP has changed, updating..."
fi

######################  获取域名及授权 ###################### 
if [ -f $id_file ] && [ $(wc -l $id_file | cut -d " " -f 1) == 2 ]; then
    zone_identifier=$(head -1 $id_file)
    if [ ! -n "$zone_identifier" ]; then
        zone_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" | grep -Po '(?<="id":")[^"]*' | head -1 )
        sed -i '1d' $id_file
        sed -i '1i $zone_identifier' $id_file
    fi
    record_identifier=$(tail -1 $id_file)
    if [ ! -n "$zone_identifier" ]; then
        record_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name&type=$record_type" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json"  | grep -Po '(?<="id":")[^"]*')
        sed -i '2d' $id_file
        echo "$record_identifier" >> $id_file
    fi
else
    zone_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" | grep -Po '(?<="id":")[^"]*' | head -1 )
    record_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name&type=$record_type" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json"  | grep -Po '(?<="id":")[^"]*')
    echo "$zone_identifier" > $id_file
    echo "$record_identifier" >> $id_file
fi

######################  更新 DNS 记录 ###################### 
update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"$record_type\",\"name\":\"$record_name\",\"content\":\"$ip\"}")

#########################  更新反馈 ######################### 
if [[ $update == *"\"success\":false"* ]]; then
    result=`echo $update | jq ".errors | .[] | .message"`
    message="API UPDATE FAILED. MESSGAE:\n$result"
    log "$message"
    echo -e "$message"
    exit 1 
elif [[ $update == *"\"error\""* ]]; then
    result=`echo $update | jq ".error"`
    message="ERROR ENCOUNTERED. MESSGAE:\n$result"
    log "$message"
    echo -e "$message"
    exit 1 
elif [ ! -n "$update" ]; then
    message="REQUEST TIMED OUT. CHECK NETWORK CONNECTIVITY."
    log "$message"
    echo -e "$message"
    exit 1 
else
    message="IP has been updated to: $ip"
    echo "$ip" > $ip_file
    log "$message"
    echo "$message"
fi

log "Check Done"

注意,本脚本使用 jq 来解析 CloudFlare 等响应的内容,所以需先安装 jq 命令:

# Ubuntu
apt install jq -y
#CentOS
yum install jq -y

2、获取授权信息

使用 CloudFlare "DDNS 服务" 需要把域名托管到 CloudFlare,此处不做介绍,自行搜索。
DNS 页面,由于我们要达到的是 DDNS 效果,所以 CDN 不要开启,即这朵云保持灰色状态(默认是开启的):

DNS Records.png

其中 Type 是你要设置的 IP 类型,Name 是要做为连接的域名, Value 第一次可以随便设一个,TTL 2 minutes 或者 Automatic 都可以,再次强调 Status 这朵云不要点亮。

个人信息页面 下滑到 API Keys,把 Global API Key 一串字符贴到上述脚本中的 auth_key:

API Keys.png

3、执行结果

~# chmod +x ddns.sh 
~# ./ddns.sh
IP changed to: ****
~# ./ddns.sh 
IP has not changed.

4、定时运行

由于无法得知运营商什么时候会把 IP 变了,所以我们可以设置定时运行脚本来实现实时监控 并更新 IP 的变化。比如通过 crontab 实现:

~# crontab -e
......
0 */1 * * *  /root/ddns.sh >/dev/null 2>&1
......

可以通过 cloudflare.log 来查看历史执行记录。

5、错误分析

问题

~# ./ddns.sh 
API UPDATE FAILED. DUMPING RESULTS:
{"success":false,"errors":[{"code":6007,"message":"Malformed JSON in request body"}],"messages":[],"result":null}

分析

猜测是网络原因引起的,概率出现这个提示,如若出现忽略再执行一次即可。

问题

~# ./ddns.sh 
API UPDATE FAILED. DUMPING RESULTS:
{"success":false,"errors":[{"code":7003,"message":"Could not route to \/zones\/dns_records, perhaps your object identifier is invalid?"},{"code":"7000","message":"No route for that URI"}],"messages":[],"result":null}

分析

再次确认邮箱、域名、KEY 等信息是否正确,不然就是获取 IP 时出现错误(本站 IP 检测服务仅在大陆提供)。

三、总结

自己搭建 DDNS 最大的好处在于可以充分发挥宽带的带宽,不再局限于服务端的限制,其次安全性有了保证,可以不受服务提供者的记录。

其实并不仅仅 CloudFlare 可以实现,其他支持 API 更新 DNS 的 DNS 服务商理论上都支持通过上面的方法来实现 DDNS,而且从网络大环境而言,国内的服务商肯定会比国外的稳定。

文中 IP 检测工具是真实有效的,大家都可以引用,但是请合理使用(建议 30 分钟检测一次即可),因为最近发现服务器负载过大,查了一下日志,好几个 IP 一天几万次请求,显然已经超过正常合理使用范畴,为保证服务可用,目前已经屏蔽这些 IP。

参考文章:

1、 《利用CloudFlare设置Dynamic DNS(DDNS)获取动态IP
2、 《GitHub: benkulbertis/cloudflare-update-record.sh
3、 《CloudFlare API Documentation


ArmxMod for Typecho
个性化、自适应、功能强大的响应式主题

推广

 继续浏览关于 cloudflareipv6ddnsapi 的文章

 本文最后更新于 2023/08/16 13:41:05,可能因经年累月而与现状有所差异

 引用转载请注明: VirCloud's Blog > 运维 > 利用 CloudFlare API 实现自动 DDNS 功能|支持IPv4|IPv6

精选评论

  1. xus
    xus 回复

    Android 8.0.0Chrome 73.0.3683.90来自 香港 的大神

    google amp打开你的网站会触发到防盗链

    1. 欧文斯

      发现了,暂时没想到怎么解决

  2. Kelcoin
    Kelcoin 回复

    Windows 10Chrome 76.0.3809.100来自 浙江 的大神

    支持一次修改多个域名吗

    1. 欧文斯

      重复以下命令就好啦

  3. jack8
    jack8 回复

    Windows 10Chrome 78.0.3904.87来自 浙江 的大神

    谢谢作者的脚本,再次感谢

  4. MDOS
    MDOS 回复

    Windows 10Edge 18.18363来自 香港 的大神

    感谢脚本~
    另外问下有在windows下使用的CF DDNS脚本/程序吗?想要把家里windows服务器的ipv6也做个DDNS

    1. 欧文斯

      暂时没有,可以参照这个思路自己写一个 icon_wink.gif

  5. bit
    bit 回复

    Windows 10Chrome 80.0.3987.149来自 广西 的大神

    好用,IPV6是不是可以改成获取本地网卡的地址?毕竟IPV6都是独立的不需要转发,感觉网址获取有点多此一举icon_question.gif

    1. 欧文斯

      可以的吖,但是 IPV6 地址你确定可以用?

  6. E
    E 回复

    Windows 10Chrome 109.0.0.0来自 火星 的大神

    怎么获取授权 请问

    1. 欧文斯

      2、获取授权信息》一节已经有做说明了,前提是要将域名托管到 CloudFlare,然后按说明操作。