利用 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. hulk
    hulk 回复

    Windows 10Chrome 114.0.0.0来自 台湾 的大神

    大佬,脚本不可通篇复制的么

    1. 欧文斯

      不建议直接复制,根据实际情况参考