关于 cloudflare-dns
这是一个面向 Cloudflare DNS 和 Azure 工作负载的技能,通过 REST API 管理 DNS 记录、代理设置和区域配置。它还涵盖 Kubernetes External-DNS 集成、故障排查及令牌安全建议。
主要功能
- 01管理多种 DNS 记录
- 02配置记录代理状态
- 03查看区域与区域设置
- 04提供 External-DNS 集成示例
- 05导入导出 BIND 格式记录
- 06排查 DNS 与 API 问题
适用场景
- 01维护 Azure 托管应用的 DNS 记录
- 02为 Kubernetes External-DNS 配置 Cloudflare
- 03检查代理状态并排查解析问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ julianobarbosa/ claude-code-skills --skill cloudflare-dns安装后在 Claude Code 中输入/cloudflare-dns确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api-reference.mdazure-integration.mdscripts/cloudflare-dns.sh详细介绍
Cloudflare DNS 技能
通过 REST API 完成 Cloudflare DNS 操作,重点关注 Azure 集成。
概述
此技能涵盖 Azure 托管工作负载的 Cloudflare DNS 管理,包括:
- API 令牌配置与安全
- DNS 记录管理(A、AAAA、CNAME、TXT、MX)
- 代理设置(橙色/灰色云朵)
- Kubernetes 的 External-DNS 集成
- 故障排除与监控
身份验证
API 令牌(推荐)
创建具有指定权限的 API 令牌,而不要使用 Global API Key:
所需权限:
| 权限 | 访问权限 | 用途 |
|---|---|---|
| Zone > Zone | 读取 | 列出区域 |
| Zone > DNS | 编辑 | 管理 DNS 记录 |
创建令牌:
- Cloudflare Dashboard > My Profile > API Tokens
- Create Token > Custom token
- 添加上述权限
- Zone Resources:仅限指定区域
- (可选)通过 IP 过滤增强安全性
环境设置:
# Export for API calls
export CF_API_TOKEN="your-api-token"
export CF_ZONE_ID="your-zone-id"
# Get zone ID
curl -s -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[] | {name, id}'
令牌验证
# Verify token is valid
curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer $CF_API_TOKEN"
快速参考
列出 DNS 记录
# All records
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[] | {name, type, content, proxied}'
# Filter by type
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?type=A" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[]'
# Search by name
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=app.example.com" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[]'
创建 DNS 记录
# A Record (proxied)
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "A",
"name": "app",
"content": "20.185.100.50",
"ttl": 1,
"proxied": true
}'
# A Record (DNS-only)
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "A",
"name": "mail",
"content": "20.185.100.51",
"ttl": 3600,
"proxied": false
}'
# CNAME Record
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "CNAME",
"name": "www",
"content": "app.example.com",
"ttl": 1,
"proxied": true
}'
# TXT Record
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "TXT",
"name": "_dmarc",
"content": "v=DMARC1; p=quarantine; rua=mailto:[email protected]",
"ttl": 3600
}'
# MX Record
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "MX",
"name": "@",
"content": "mail.example.com",
"priority": 10,
"ttl": 3600
}'
更新 DNS 记录
# Get record ID first
RECORD_ID=$(curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=app.example.com&type=A" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq -r '.result[0].id')
# Update record
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "A",
"name": "app",
"content": "20.185.100.60",
"ttl": 1,
"proxied": true
}'
# Patch (partial update)
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"proxied": false}'
删除 DNS 记录
# Get record ID
RECORD_ID=$(curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=old.example.com" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq -r '.result[0].id')
# Delete
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $CF_API_TOKEN"
代理设置(橙色/灰色云朵)
何时启用代理(橙色云朵)
| 用例 | 代理 | 原因 |
|---|---|---|
| Web 应用 | 是 | CDN、DDoS 防护 |
| REST API | 是 | 性能、安全性 |
| 静态网站 | 是 | 缓存、优化 |
| WebSockets | 是 | 配置后支持 |
何时禁用代理(灰色云朵)
| 用例 | 代理 | 原因 |
|---|---|---|
| 邮件服务器(MX) | 否 | 不支持 SMTP |
| SSH 访问 | 否 | 非 HTTP 协议 |
| FTP 服务器 | 否 | 非 HTTP 协议 |
| 自定义 TCP/UDP | 否 | 仅代理 HTTP/HTTPS |
| VPN 端点 | 否 | 需要直接连接 |
通过 API 切换代理
# Enable proxy
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"proxied": true}'
# Disable proxy
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"proxied": false}'
External-DNS 集成
Kubernetes Secret
kubectl create namespace external-dns
kubectl create secret generic cloudflare-api-token \
--namespace external-dns \
--from-literal=cloudflare_api_token="$CF_API_TOKEN"
Helm 值(kubernetes-sigs/external-dns)
fullnameOverride: external-dns
provider:
name: cloudflare
env:
- name: CF_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-api-token
key: cloudflare_api_token
extraArgs:
cloudflare-proxied: true
cloudflare-dns-records-per-page: 5000
sources:
- service
- ingress
domainFilters:
- example.com
txtOwnerId: "aks-cluster-name" # MUST be unique per cluster
txtPrefix: "_externaldns."
policy: upsert-only # Production: NEVER use sync
interval: "5m"
logLevel: info
logFormat: json
resources:
requests:
memory: "64Mi"
cpu: "25m"
limits:
memory: "128Mi"
serviceMonitor:
enabled: true
interval: 30s
Ingress 注解
metadata:
annotations:
# Hostname for External-DNS
external-dns.alpha.kubernetes.io/hostname: "app.example.com"
# Custom TTL
external-dns.alpha.kubernetes.io/ttl: "300"
# Override proxy setting
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
# Multiple hostnames
external-dns.alpha.kubernetes.io/hostname: "app.example.com,www.example.com"
区域管理
列出区域
curl -s "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[] | {name, id, status, plan: .plan.name}'
获取区域详情
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result'
区域设置
# Get all settings
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/settings" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[] | {id, value}'
# Get specific setting
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/settings/ssl" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result'
# Update SSL mode
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/settings/ssl" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": "full"}'
导出/导入 DNS 记录
导出(BIND 格式)
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/export" \
-H "Authorization: Bearer $CF_API_TOKEN" > dns-backup-$(date +%Y%m%d).txt
导入(BIND 格式)
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/import" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-F "[email protected]"
故障排除
DNS 验证
# Query Cloudflare DNS (1.1.1.1)
dig @1.1.1.1 app.example.com A
dig @1.1.1.1 app.example.com AAAA
# Check if proxied (returns Cloudflare IP)
dig +short app.example.com
# Proxied: 104.x.x.x or 172.64.x.x
# DNS-only: Your actual IP
# Check TXT records (External-DNS ownership)
dig @1.1.1.1 TXT _externaldns.app.example.com
# Full trace
dig +trace app.example.com
# Check nameservers
dig NS example.com +short
常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 401 Unauthorized | 令牌无效 | 重新生成 API 令牌 |
| 403 Forbidden | 权限不足 | 添加 Zone:Read、DNS:Edit 权限 |
| 429 Rate Limited | 请求过多 | 增加间隔,使用分页 |
| Record exists | 重复 | 删除或更新现有记录 |
External-DNS 日志
# Watch logs
kubectl logs -n external-dns deployment/external-dns -f
# Check for Cloudflare errors
kubectl logs -n external-dns deployment/external-dns | grep -i cloudflare
# Check sync status
kubectl logs -n external-dns deployment/external-dns | grep -i "All records are already up to date"
安全最佳实践
API 令牌安全
- 限定令牌范围 - 使用指定区域,而非“All zones”
- IP 过滤 - 尽可能限制为已知 IP
- 定期轮换 - 生产环境每 90 天轮换一次
- 安全存储 - 使用 Kubernetes Secrets 或 Azure Key Vault
- 审计使用情况 - 检查 Cloudflare 审计日志
令牌轮换
# 1. Create new token in Cloudflare dashboard
# 2. Update Kubernetes secret
kubectl create secret generic cloudflare-api-token \
--namespace external-dns \
--from-literal=cloudflare_api_token="NEW_TOKEN" \
--dry-run=client -o yaml | kubectl apply -f -
# 3. Restart External-DNS
kubectl rollout restart deployment external-dns -n external-dns
# 4. Verify
kubectl logs -n external-dns deployment/external-dns | head -20
# 5. Revoke old token in Cloudflare dashboard
速率限制
Cloudflare API 限制:
- 每 5 分钟 1,200 个请求(每个账户)
- 每 5 分钟 100 个请求(每个区域,适用于某些端点)
缓解措施:
# External-DNS optimizations
extraArgs:
cloudflare-dns-records-per-page: 5000 # Max pagination
zone-id-filter: "specific-zone-id" # Reduce API calls
interval: "10m" # Less frequent polling
Azure 集成
使用 Cloudflare DNS-01 的 cert-manager
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-cloudflare
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-cloudflare-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
selector:
dnsZones:
- example.com
AKS Ingress 配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cloudflare
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
参考资料
references/api-reference.md- 完整的 Cloudflare DNS API 文档references/azure-integration.md- Azure 专用模式与配置scripts/cloudflare-dns.sh- 用于常见操作的辅助脚本- Cloudflare API 文档
- External-DNS Cloudflare 教程
注意事项
- 速率限制按 API 令牌计算,而非按区域计算: 令牌 X 上频繁运行的 External-DNS 循环会耗尽该令牌所涉及所有区域的 1,200/5min 配额。将变更频繁的区域拆分到单独的令牌中,以隔离影响范围。
- 代理 A 记录返回的是 Cloudflare IP,而不是你的 IP:
dig +short app.example.com显示104.x.x.x是正常的,并非故障。必须通过 Host 标头或直接访问 Azure 源站 IP 来测试源站是否可达。 - 启用代理时 TTL 会被忽略: 为橙色云朵记录设置 TTL 看似成功,但 Cloudflare 会将其覆盖为“Auto”(=1)。如果确实需要指定 TTL(例如 DNS-01 证书流程),请先禁用代理。
- External-DNS 的
txtOwnerId冲突会导致跨集群记录损坏: 两个集群共用同一个txtOwnerId时,会争抢所有权 TXT 记录,并静默覆盖对方的 A 记录。务必使用唯一的集群标识符。 policy: sync会删除 External-DNS 未创建的记录: 如果手动 A 记录与受管理的主机名模式匹配,sync 模式会在协调期间将其删除。生产环境必须使用upsert-only。- 即使没有任何权限,令牌验证端点也会返回 200:
/user/tokens/verify只能确认令牌存在,不能确认其拥有 Zone:Read 或 DNS:Edit 权限。请尝试列出实际区域,以确认权限范围配置正确。
评论
支持 Markdown