Cloudflare 作为世界上最大的互联网慈善组织,为开发者提供了极其完整的 API 支持。本文关注于 Cloudflare 的 DNS API 部分。
申请 API 令牌(Token)
这是 API 使用过程中唯一图形化的部分了。我们要在 用户 API 令牌 创建一个令牌。对于我们要操作的 DNS,可以选择编辑区域 DNS 模板,然后填入我们要操作的二级域名(和 IP 地址白名单)。为了最小权限原理,建议填写白名单。
然后就生成了我们的 API Token!注意保存以及保存的安全性,因为离开页面后再也见不到它啦。如果弄丢了只能重新生成部署了。
建议在对应服务器上明文储存,反正有 ssh 帮你拦着风险。
然后就可以尝试验证一下 Token。
curl "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer <API_TOKEN>"
正常情况下应当返回
{
"result": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "active"
},
"success": true,
"errors": [],
"messages": [
{
"code": 10000,
"message": "This API Token is valid and active",
"type": null
}
]
}
DNS Record API
Cloudflare 的 API 采用 RESTful 风格,默认基于 JSON (Content-Type: application/json) 传递数据。
在 DNS 记录方面,Cloudflare 提供了 9 个 API:
列举 Zone
API: GET
https://api.cloudflare.com/client/v4/zones
对于 DNS 来说,Cloudflare 里有一个参数叫做 Zone ID(zone_identifier
)。一个 Zone 对应着一个你账号内的二级域名,我们需要事前确定这个 Zone ID。
另一个可能会用到的是 Record ID(identifier
)。一个 Record ID 对应着一条域名记录。对于同一个子域名的不同记录对应着不同 Record ID,即使他们都是 A 记录。
获取前者的方法是 GET curl "https://api.cloudflare.com/client/v4/zones" -H "Authorization: Bearer ${CF_Token}"
。
示例结果
{
"result": [
{
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "DOMAIN1",
"status": "active",
"paused": false,
"type": "full",
"owner": { "id": null, "type": "user", "email": null },
"account": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "ACCOUNT NAME"
},
"permissions": ["#dns_records:edit", "#dns_records:read", "#zone:read"],
"plan": {...}
},
{
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "DOMAIN2",
"status": "active",
"paused": false,
"type": "full",
...
"owner": { "id": null, "type": "user", "email": null },
"account": {
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "ACCOUNT NAME"
},
"permissions": ["#dns_records:edit", "#dns_records:read", "#zone:read"],
"plan": {...}
},
...
],
"result_info": {
"page": 1,
"per_page": 20,
"total_pages": 5726,
"count": 20,
"total_count": 114514
},
"success": true,
"errors": [],
"messages": []
}
我们可以通过 curl "https://api.cloudflare.com/client/v4/zones" -H "Authorization: Bearer ${CF_Token}" | jq '.result[] | if .name == "${Goal_Domain}" then .id else null end'
轻松筛选出目标的域名 id。下面默认设置了一个 CF_Zone
的环境变量,存储目标域名的 Zone ID。
如果你有很多很多域名,可能会分页,那么可以通过 https://api.cloudflare.com/client/v4/zones?page=xx
来逐页获取,或是增大 per_page
参数。
列举 DNS 记录
API: GET
https://api.cloudflare.com/client/v4/zones/{zone_identifier}/dns_records
这里就用上了上一步获得的 Zone ID。我们直接 curl https://api.cloudflare.com/client/v4/zones/${CF_Zone}/dns_records -H "Authorization: Bearer ${CF_Token}"
。
示例结果
{
"result": [
{
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1",
"zone_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"zone_name": "DOMAIN",
"name": "SUB1.DOMAIN",
"type": "A",
"content": "1.14.5.14",
"proxiable": true,
"proxied": false,
"ttl": 1,
"locked": false,
"meta": {
"auto_added": false,
"managed_by_apps": false,
"managed_by_argo_tunnel": false,
"source": "primary"
},
"comment": null,
"tags": [],
"created_on": "2023-01-01T00:00:00.000000Z",
"modified_on": "2023-01-01T00:00:00.000000Z"
},
{
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2",
"zone_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"zone_name": "DOMAIN",
"name": "SUB2.DOMAIN",
"type": "AAAA",
"content": "1919::810",
"proxiable": true,
"proxied": true,
"ttl": 1,
"locked": false,
"meta": {
"auto_added": false,
"managed_by_apps": false,
"managed_by_argo_tunnel": false,
"source": "primary"
},
"comment": null,
"tags": [],
"created_on": "2023-01-01T00:00:00.000000Z",
"modified_on": "2023-01-01T00:00:00.000000Z"
}
],
"success": true,
"errors": [],
"messages": [],
"result_info": {
"page": 1,
"per_page": 100,
"count": 100,
"total_count": 1919810,
"total_pages": 1920
}
}
我们可以在此之中找到我们需要的 Record id(即每一项中的 .id
)备用。
新建 DNS 记录
API: POST
https://api.cloudflare.com/client/v4/zones/{zone_identifier}/dns_records
话不多说,直接上代码。
curl -X POST https://api.cloudflare.com/client/v4/zones/${CF_Zone}/dns_records\
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ${CF_Token}' \
--data '{
"content": "1.14.5.14",
"name": "SUBDOMAIN",
"proxied": false,
"type": "A",
"comment": "COMMENT",
"tags": [
"TAG"
],
"ttl": 3600
}'
示例返回结果如下
{
"errors": [],
"messages": [],
"result": {
// 重复一遍你的 data
"created_on": "2023-01-01T00:00:00.000000Z",
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"locked": false,
"meta": {
"auto_added": true,
"source": "primary"
},
"modified_on": "2023-01-01T00:00:00.000000Z",
"zone_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"zone_name": "DOMAIN"
},
"success": true
}
这里的 .result.id
是这个记录的 Record ID,可以保存以后用。
删除记录
API: DELETE
https://api.cloudflare.com/client/v4/zones/{zone_identifier}/dns_records/{identifier}
其中的 identifier
就是 Record ID。你可以通过 curl -X DELETE -H 'Authorization: Bearer ${CF_Token}' https://api.cloudflare.com/client/v4/zones/${CF_Zone}/dns_records/${CF_Record}
来轻松删除一条记录。
更新记录
API: PUT
https://api.cloudflare.com/client/v4/zones/{zone_identifier}/dns_records/{identifier}
内容、用法和 创建记录 一模一样,返回值也一模一样。唯一的不同就是调用链接加上了 Record ID,方法变成了 PUT。
实用
来撸点脚本吧。
IPv6 DDNS 脚本
一键 DDNS 脚本(每小时刷新),依赖 jq。
# 配置变量
API='https://ddnsip.cn/'
name='myddns'
CF_Zone='...'
CF_Token='...'
# 新建记录
IP=$(curl $API -6 -s)
Record_ID=$(curl -s -X POST https://api.cloudflare.com/client/v4/zones/${CF_Zone}/dns_records\
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${CF_Token}" \
--data "{
\"content\": \"${IP}\",
\"name\": \"${name}\",
\"proxied\": false,
\"type\": \"AAAA\",
\"comment\": \"DDNS initialized on $(date "+%Y/%m/%d %H:%M:%S")\",
\"ttl\": 1800
}" | jq -j '.result.id')
# 更新
sudo tee /etc/cron.hourly/ddns6.sh > /dev/null <<EOF
#!/bin/bash
IP=\$(curl $API -6 -s)
curl -s -X PUT https://api.cloudflare.com/client/v4/zones/${CF_Zone}/dns_records/${Record_ID}\
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${CF_Token}" \
--data "{
\"content\": \"\${IP}\",
\"name\": \"${name}\",
\"proxied\": false,
\"type\": \"AAAA\",
\"comment\": \"DDNS updated on \$(date "+%Y/%m/%d %H:%M:%S")\",
\"ttl\": 1800
}" >/dev/null
EOF
如果你有公网 v4 也可以把上面的 6 全改成 4,AAAA 改成 A,v4 ddns 脚本就新鲜出炉啦!