cloudflare-dns について
REST API を使い、Azure でホストするワークロード向けに Cloudflare DNS を管理するスキルです。DNS レコード、プロキシ設定、ゾーン設定に加え、Kubernetes External-DNS 連携やトラブルシューティングを扱います。
主な機能
- 01複数の DNS レコード種別を管理
- 02レコードのプロキシ状態を設定
- 03ゾーンとゾーン設定を確認
- 04External-DNS 連携例を提示
- 05BIND 形式のレコードを入出力
- 06DNS と API の問題を調査
活用シーン
- 01Azure ホストアプリの DNS レコードを管理
- 02Kubernetes 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 スキル
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 レコードの管理 |
トークンの作成:
- Cloudflare ダッシュボード > プロフィール > 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 | はい | パフォーマンス、セキュリティ |
| 静的 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 トークンのセキュリティ
- トークンのスコープを限定する - 「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 レコードが返すのは、お客様の 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 の権限ではありません。実際にゾーンを一覧表示して、権限が正しくスコープ設定されていることを確認してください。
コメント
Markdown 対応