mcpField
C
cloudflare-dns

cloudflare-dns

github.com/julianobarbosaクラウド・DevOps14185 回インストール2 日前に更新
リポジトリ

cloudflare-dns について

REST API を使い、Azure でホストするワークロード向けに Cloudflare DNS を管理するスキルです。DNS レコード、プロキシ設定、ゾーン設定に加え、Kubernetes External-DNS 連携やトラブルシューティングを扱います。

主な機能

  1. 01複数の DNS レコード種別を管理
  2. 02レコードのプロキシ状態を設定
  3. 03ゾーンとゾーン設定を確認
  4. 04External-DNS 連携例を提示
  5. 05BIND 形式のレコードを入出力
  6. 06DNS と API の問題を調査

活用シーン

  1. 01Azure ホストアプリの DNS レコードを管理
  2. 02Kubernetes 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 スキル

Azure との連携に重点を置き、REST API 経由で Cloudflare DNS のあらゆる操作を行います。

概要

このスキルでは、Azure でホストされるワークロード向けの Cloudflare DNS 管理について説明します。内容は次のとおりです。

  • API トークンの設定とセキュリティ
  • DNS レコードの管理(A、AAAA、CNAME、TXT、MX)
  • プロキシ設定(オレンジ/グレーの雲)
  • Kubernetes 向けの External-DNS 連携
  • トラブルシューティングとモニタリング

認証

API トークン(推奨)

Global API Key を使用する代わりに、スコープを限定した API トークンを作成します。

必要な権限:

権限アクセス目的
Zone > Zone読み取りゾーンの一覧表示
Zone > DNS編集DNS レコードの管理

トークンの作成:

  1. Cloudflare ダッシュボード > プロフィール > 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はいパフォーマンス、セキュリティ
静的 Web サイトはいキャッシュ、最適化
WebSocketはい設定によりサポート
プロキシを無効にする場合(グレーの雲)
用途プロキシ理由
メールサーバー(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 シークレット
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 レコードが返すのは、お客様の IP ではなく Cloudflare の 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 を共有する 2 つのクラスターは、所有権を示す TXT レコードを奪い合い、互いの A レコードを通知なく上書きします。必ず一意のクラスター識別子を使用してください。
  • policy: sync は External-DNS が作成していないレコードを削除します: 手動で作成した A レコードが管理対象のホスト名パターンに一致する場合、同期モードでは調整中に削除されます。本番環境では upsert-only を使用してください。
  • トークン検証エンドポイントは、権限がゼロでも 200 を返します: /user/tokens/verify で確認できるのはトークンの存在であり、Zone:Read または DNS:Edit の権限ではありません。実際にゾーンを一覧表示して、権限が正しくスコープ設定されていることを確認してください。

コメント