mcpField
C
cloudflare-dns

cloudflare-dns

github.com/julianobarbosa云与 DevOps14185 次安装2 天前更新
代码仓库

关于 cloudflare-dns

这是一个面向 Cloudflare DNS 和 Azure 工作负载的技能,通过 REST API 管理 DNS 记录、代理设置和区域配置。它还涵盖 Kubernetes External-DNS 集成、故障排查及令牌安全建议。

主要功能

  1. 01管理多种 DNS 记录
  2. 02配置记录代理状态
  3. 03查看区域与区域设置
  4. 04提供 External-DNS 集成示例
  5. 05导入导出 BIND 格式记录
  6. 06排查 DNS 与 API 问题

适用场景

  1. 01维护 Azure 托管应用的 DNS 记录
  2. 02为 Kubernetes External-DNS 配置 Cloudflare
  3. 03检查代理状态并排查解析问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/julianobarbosa/claude-code-skills --skill cloudflare-dns

安装后在 Claude Code 中输入/cloudflare-dns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-reference.md
azure-integration.md
scripts/
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 记录

创建令牌:

  1. Cloudflare Dashboard > My Profile > API Tokens
  2. Create Token > Custom token
  3. 添加上述权限
  4. Zone Resources:仅限指定区域
  5. (可选)通过 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 令牌安全
  1. 限定令牌范围 - 使用指定区域,而非“All zones”
  2. IP 过滤 - 尽可能限制为已知 IP
  3. 定期轮换 - 生产环境每 90 天轮换一次
  4. 安全存储 - 使用 Kubernetes Secrets 或 Azure Key Vault
  5. 审计使用情况 - 检查 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

参考资料


注意事项

  • 速率限制按 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