之前就写过关于动态域名解析的文章,使用的是阿里云的API。最近将域名转移到了Cloudflare上,顺便记录一下如何使用Cloudflare的API进行动态域名解析。
缘起
之前的文章里提到过,由于工作原因需要经常使用ssh
登录家里的和办公室的个人电脑,但是这些电脑基本都没有固定的IP地址,所以当IP地址发生变动之后,往往不能及时连接到电脑上。就考虑使用域名代替IP地址来访问这些电脑,并且购买了一两个便宜的域名。很长一段时间都在用阿里云的域名解析服务。之前关于在阿里云上进行动态域名解析的文章可以参考“个人网站的建立过程(一):购买个人域名并配置动态域名解析”。
后来我把域名转移到了Cloudflare上,主要是因为Cloudflare的CDN服务和DNS解析速度都比阿里云快。现在记录一下如何使用Cloudflare的API进行动态域名解析。
前提
-
拥有自己的域名
- 购买的域名可以在Cloudflare上进行解析。
- 如果没有自己的域名,可以在Namecheap上购买一个便宜的域名。
-
拥有Cloudflare账号
- 如果没有Cloudflare账号,可以在Cloudflare上注册一个账号。
- 注册完成后,登录Cloudflare账号,添加自己的域名,并将域名的DNS服务器指向Cloudflare提供的DNS服务器。
域名解析到服务器IP地址
- 购买的域名需要解析到服务器的IP地址才能直接通过域名访问服务器。
- 如果服务器具有固定IP,只需要在Cloudflare上将域名和服务器绑定即可。
- 如果服务器没有固定IP,则需要将域名动态解析到服务器的IP地址。
有固定IP——直接绑定
-
获取IP地址
-
可以前往IPv6测试网站查看网络是否支持IPv6:
-
如果是类Unix系统,可以通过如下命令获取IPv4地址:
1
curl ipinfo.io/ip
通过如下命令获取IPv6地址:
1
curl ipv6.icanhazip.com
-
-
将域名与IP地址绑定
- 登录Cloudflare账号,点击左上角的菜单,在菜单里找到DNS选项:
- 点击Add Record按钮,添加一条A记录(IPv4)或AAAA记录(IPv6),将域名解析到服务器的IP地址:
- 填入需要解析的域名和对应的IP地址(IP地址正确与否不重要,如果错误的话,待会儿运行本脚本后会把它改正;但域名一定要是你在之前“有固定IP——直接绑定”第2步中创建的域名)。
- 如果你有多个域名需要解析,可以仿照上面的例子自行添加域名,IPv4和IPv6都支持。再次强调,本文件中需要解析的域名需要是之前在“有固定IP——直接绑定”第2步中在Cloudflare上已经创建的域名。
- 登录Cloudflare账号,点击左上角的菜单,在菜单里找到DNS选项:
-
测试是否绑定成功 绑定完成后可能需要10分钟的时间等待Cloudflare DNS服务器完成更新(一般提交之后立刻就会更新)。然后可以查看是否绑定成功。
- 建议在另一个网络环境通过“ping”命令测试
如果可以在公网ping通,则绑定成功。
1
ping surface.jinli.cyou
- 建议在另一个网络环境通过“ping”命令测试
-
完成! 绑定成功之后就可以通过域名来访问服务器了,例如如果需要使用ssh远程登录服务器,就可以直接ssh域名:
1
ssh [email protected] -X
也可以用服务器搭建NAS(Network Attached Storage)服务等,然后通过域名访问。
无固定IP——绑定之后再动态解析
如果电脑或服务器没有固定IP地址,则需要先按照上面的步骤在Cloudflare上绑定当前的IP地址。然后监控本机IP地址,一旦发生改变,就通过Cloudflare提供的API上传到Cloudflare DNS服务器,修改原来绑定的IP地址。
-
DDNS脚本
- 我使用的DDNS(Dynamic Domain Name System)脚本是一个让ChatGPT生成的bash脚本:
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
#!/bin/bash # Config API_TOKEN="your_cloudflare_api_token" ZONE_ID="your_cloudflare_zone_id" RECORDS=("host1.example.com:A" "host1.example.com:AAAA" "host2.example.com:A" "host2.example.com:AAAA") # Add as needed # Get current IPv4 and IPv6 addresses IPV4=$(curl -s https://ipv4.icanhazip.com) IPV6=$(ip -6 addr show scope global | grep inet6 | awk '{print $2}' | cut -d/ -f1 | head -n 1) if [ -z "$IPV4" ]; then echo "No IPv4 address found." else echo "Detected IPv4: $IPV4" fi if [ -z "$IPV6" ]; then echo "No IPv6 address found." else echo "Detected IPv6: $IPV6" fi # Function to update DNS record update_record() { local name="$1" local type="$2" local ip="$3" echo "Processing $type record for $name" # Get record info RECORD=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=$type&name=$name" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json") RECORD_ID=$(echo "$RECORD" | jq -r '.result[0].id') CURRENT_IP=$(echo "$RECORD" | jq -r '.result[0].content') if [ "$CURRENT_IP" == "$ip" ]; then echo " - $type is already up to date: $ip" return fi # Update the DNS record UPDATE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ --data "{\"type\":\"$type\",\"name\":\"$name\",\"content\":\"$ip\",\"ttl\":120,\"proxied\":false}") SUCCESS=$(echo "$UPDATE" | jq -r '.success') if [ "$SUCCESS" == "true" ]; then echo " - Updated $type record to $ip" else echo " - Failed to update $type record: $UPDATE" fi } # Loop through all record names and update the specified type (A or AAAA) for record in "${RECORDS[@]}"; do # Split the record into name and type IFS=':' read -r name type <<< "$record" if [ "$type" == "A" ] && [ -n "$IPV4" ]; then update_record "$name" "A" "$IPV4" elif [ "$type" == "AAAA" ] && [ -n "$IPV6" ]; then update_record "$name" "AAAA" "$IPV6" else echo " - Skipping $type record for $name due to missing IP address." fi done
- 这个脚本要求用户指定想要解析的域名和解析类型(A或AAAA),格式为
domain_name:record_type
,例如host1.example.com:A
。如果有多个域名需要解析,可以在脚本中添加多个记录,格式为host1.example.com:A
、host2.example.com:AAAA
等。 - 该脚本会自动检测当前的IPv4和IPv6地址,并将其更新到Cloudflare DNS服务器上。脚本中使用了
jq
命令来解析JSON格式的数据,因此需要安装jq
命令:1
sudo apt install jq
- 该脚本使用了Cloudflare的API进行动态域名解析,因此需要在Cloudflare上生成API Token和Zone ID。下面简单介绍一下如何在Cloudflare上获取API Token和Zone ID。将Zone_ID填入上面脚本中的
ZONE_ID
变量中。
-
获取API Token和Zone ID
- 登录Cloudflare账号,点击你的域名,在右侧有一列菜单。菜单靠下的部分有Zone ID和Account ID。在这两个ID下面就是“Get your API token"的链接,点击它。
- 在User API Token的页面,选择“Create Token”。这里需要选择创建API Token的类型,这里选择“Edit Zone DNS”类型就行,即这个Token只能修改DNS记录。
- 然后会让你设置Token的权限,这里根据需要设置就行:
- 设置完成后,点击“Continue to summary”,然后点击“Create Token”按钮。创建完成后,会显示Token的值,页面也会提示你可以在目标电脑上运行一个curl命令来测试Token是否有效。测试有效之后把API Token复制下来填入上面脚本中的
API_TOKEN
变量中。
注意: 这个API Token和上面的Zone_ID都是非常重要的,千万不要公开它们。如果泄露了API Token,应该立即登录Cloudflare账号删除该Token。Zone_ID和API Token是你在Cloudflare上进行操作的凭证,泄露后可能会导致你的域名被恶意修改。
- 登录Cloudflare账号,点击你的域名,在右侧有一列菜单。菜单靠下的部分有Zone ID和Account ID。在这两个ID下面就是“Get your API token"的链接,点击它。
-
测试运行DDNS脚本
在修改完上面的脚本之后,保存为
ddns.sh
文件。然后-
确保安装了
curl
和jq
命令:1
sudo apt install curl jq
-
赋予脚本可执行权限:
1
chmod +x ddns.sh
-
运行脚本:
1
./ddns.sh
-
如果脚本运行成功,会输出类似下面的结果:
1 2 3 4 5 6 7 8 9 10
Detected IPv4: 111.111.111.111 Detected IPv6: 12aa3:4567:89ab:cdef Processing A record for host1.example.com - Updated A record to 111.111.111.111 Processing AAAA record for host1.example.com - Updated AAAA record to 12aa3:4567:89ab:cdef Processing A record for host2.example.com - Updated A record to 111.111.111.111 Processing AAAA record for host2.example.com - Updated AAAA record to 12aa3:4567:89ab:cdef
-
-
设置定时任务
-
Linux 在Linux系统中,设置
crontab
定时任务非常简单。我们可以在命令行输入以下命令:shell crontab -e
此命令会打开一个包含crontab
定时任务的配置文件,在文件末尾加入一行shell 42 * * * * cd ~/.config/ddns && ./ddns.sh
即可。上述命令表示每小时的第42分钟运行ddns.sh
脚本。你可以根据需要修改时间。
- 之后可以使用如下命令查看
crontab
定时任务是否在运行:1
crontab -l
- macOS
在macOS上由于其系统安全性的考虑,在使用
crontab
命令前,还需要经过一些特殊的设置。
1. 为`cron`命令添加访问全盘文件的权限:
- 打开“系统偏好设置”->“安全性与隐私”->“隐私”选项卡,点击左下角的锁图标解锁设置。
- 在左侧列表中选择“完全磁盘访问”,然后点击右侧的加号按钮,添加`/usr/sbin/cron`程序。注意,在点击加号按钮后会弹出一个Finder窗口,让你选择要添加的程序。但`cron`程序默认是隐藏的,在Finder窗口中无法直接找到。你可以在Finder中按下`Command + Shift + G`组合键,输入`/usr/sbin/`,然后选择`cron`程序添加即可。
- 关闭“系统偏好设置”窗口,确保权限已打开。
2. 这个脚本需要用到的`curl`、`jq`、`yq`和`ifconfig`这些命令,但是`cron`运行脚本的时候并不会加载用户的环境变量,所以需要在脚本中指定这些命令的完整路径。可以通过如下命令查看这些命令的完整路径:
```shell
which curl jq yq ifconfig
```
然后将这些命令的完整路径填入脚本中,例如:
```bash
export PATH="$PATH:/usr/local/bin:/usr/bin:/opt/local/bin:/sbin"
```
3. 为此,我专门写了一个脚本作为调用`ddns.sh`的中介脚本,命名为`cron_ddns.sh`,内容如下:
```bash
#!/bin/bash
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting cron job for Cloudflare DDNS update..."
export PATH="$PATH:/usr/local/bin:/usr/bin:/opt/local/bin:/sbin"
echo "PATH is $PATH"
cd /Users/lijin/.config/ddns
echo "Current directory is $(pwd)"
echo "Running script cloudflare.sh"
/bin/bash ddns.sh
```
4. 赋予脚本可执行权限:
```shell
chmod +x cron_ddns.sh
```
5. 然后在`crontab`中添加定时任务:
```shell
crontab -e
```
在文件末尾添加一行:
```shell
42 * * * * /Users/lijin/.config/ddns/cron_ddns.sh >> /tmp/ddns.log 2>&1
```