Cloudflare API ile Toplu DNS Kaydı Yönetimi
Onlarca subdomain, yüzlerce DNS kaydı ve bunları tek tek Cloudflare panelinden yönetmeye çalışmak… Kim geçirmediki bunu? Özellikle büyük bir göç sırasında ya da toplu güncelleme yaparken Cloudflare’in web arayüzü bir noktada yetersiz kalmaya başlıyor. İşte tam bu noktada Cloudflare API devreye giriyor ve hayatı çok daha kolaylaştırıyor.
Bu yazıda Cloudflare API’yi kullanarak DNS kayıtlarını toplu olarak nasıl yönetebileceğini, gerçek dünya senaryolarıyla birlikte anlatacağım. Bash scriptleri, curl komutları ve biraz Python ile oldukça güçlü bir DNS yönetim altyapısı kurabilirsin.
Cloudflare API’ye Giriş
Cloudflare’in REST API’si son derece kapsamlı ve iyi dokümante edilmiş bir yapıya sahip. DNS yönetimi için temel olarak iki şeye ihtiyacın var:
- API Token: Cloudflare hesabından oluşturduğun, kapsam sınırlı erişim anahtarı
- Zone ID: Yönetmek istediğin alan adının benzersiz kimliği
API Token oluşturmak için Cloudflare panelinde My Profile > API Tokens > Create Token yolunu izle. “Edit zone DNS” şablonunu kullanarak başlamak en sağlıklısı. Bu şablon sana DNS kayıtları üzerinde okuma ve yazma yetkisi veriyor, fazlasını değil.
Zone ID’yi bulmak için ise alan adının Cloudflare dashboard’una girip sağ taraftaki “Overview” sekmesine bakman yeterli. Ya da şu şekilde API üzerinden listeleyebilirsin:
curl -s -X GET "https://api.cloudflare.com/client/v4/zones"
-H "Authorization: Bearer YOUR_API_TOKEN"
-H "Content-Type: application/json" | jq '.result[] | {name: .name, id: .id}'
Bu komutu çalıştırdığında hesabındaki tüm zone’ları ve ID’lerini göreceksin. jq kurulu değilse apt install jq veya yum install jq ile kurabilirsin, bu işleri yaparken jq olmadan yaşamak gerçekten zor.
Sık kullanacağın değişkenleri bir environment dosyasına yazıp kaydetmek iyi bir alışkanlık:
# ~/.cloudflare_env
export CF_API_TOKEN="your_api_token_here"
export CF_ZONE_ID="your_zone_id_here"
export CF_API_BASE="https://api.cloudflare.com/client/v4"
source ~/.cloudflare_env ile yükleyip kullanmaya başlayabilirsin. Tabii bu dosyayı asla Git’e commit etme ve chmod 600 ~/.cloudflare_env ile sadece kendi kullanıcına açık tut.
Mevcut DNS Kayıtlarını Listelemek ve Export Etmek
Toplu işleme başlamadan önce mevcut durumu görmen şart. Şu komutla zone’undaki tüm DNS kayıtlarını listeleyebilirsin:
curl -s -X GET "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?per_page=100"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json" |
jq -r '.result[] | [.type, .name, .content, .ttl, .proxied] | @tsv'
Bu komut sana tab-separated bir çıktı verir. Ama dikkat, Cloudflare varsayılan olarak sayfa başına 100 kayıt döndürüyor. Eğer 100’den fazla kaydın varsa sayfalama yapman gerekiyor. Şu script bunu otomatik olarak halleder:
#!/bin/bash
source ~/.cloudflare_env
page=1
all_records="[]"
while true; do
response=$(curl -s -X GET
"$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?per_page=100&page=$page"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json")
records=$(echo "$response" | jq '.result')
total_pages=$(echo "$response" | jq '.result_info.total_pages')
count=$(echo "$records" | jq 'length')
if [ "$count" -eq 0 ]; then
break
fi
all_records=$(echo "$all_records $records" | jq -s 'add')
echo "Sayfa $page/$total_pages islendi, $count kayit bulundu"
if [ "$page" -ge "$total_pages" ]; then
break
fi
((page++))
done
echo "$all_records" | jq -r '.[] | [.type, .name, .content, .ttl, (.proxied | tostring)] | @csv'
> dns_backup_$(date +%Y%m%d_%H%M%S).csv
echo "Export tamamlandi."
Bu script çıktıyı timestamp’li bir CSV dosyasına kaydediyor. Toplu değişiklik yapmadan önce bu backup’ı almak hayat kurtarıcı olabilir.
Yeni DNS Kaydı Ekleme
Tekli kayıt eklemek basit ama biz toplu işlerden bahsediyoruz. Önce temel ekleme komutunu görelim:
curl -s -X POST "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json"
--data '{
"type": "A",
"name": "test.example.com",
"content": "192.168.1.100",
"ttl": 300,
"proxied": false
}' | jq '.success, .result.id'
Şimdi gerçek dünya senaryosuna geçelim. Diyelim ki 20 farklı müşteri için subdomain oluşturman gerekiyor. Bunları bir CSV dosyasından okuyup toplu ekleyeceksin.
CSV formatın şöyle olsun (records_to_add.csv):
A,customer1.example.com,10.0.1.10,300,false
A,customer2.example.com,10.0.1.11,300,false
CNAME,mail.customer1.example.com,mail.provider.com,3600,false
Bu CSV’yi işleyen script:
#!/bin/bash
source ~/.cloudflare_env
INPUT_FILE="records_to_add.csv"
SUCCESS_COUNT=0
FAIL_COUNT=0
while IFS=',' read -r type name content ttl proxied; do
# Yorum satirlarini ve bos satirlari atla
[[ "$type" =~ ^#.*$ || -z "$type" ]] && continue
echo "Ekleniyor: $type $name -> $content"
response=$(curl -s -X POST "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json"
--data "{
"type": "$type",
"name": "$name",
"content": "$content",
"ttl": $ttl,
"proxied": $proxied
}")
success=$(echo "$response" | jq -r '.success')
if [ "$success" = "true" ]; then
record_id=$(echo "$response" | jq -r '.result.id')
echo " BASARILI - ID: $record_id"
((SUCCESS_COUNT++))
else
error=$(echo "$response" | jq -r '.errors[0].message')
echo " HATA: $error"
((FAIL_COUNT++))
fi
# API rate limit icin kisa bekleme
sleep 0.3
done < "$INPUT_FILE"
echo ""
echo "Islem tamamlandi: $SUCCESS_COUNT basarili, $FAIL_COUNT basarisiz"
Rate limiting konusunda dikkatli olmak lazım. Cloudflare API’si ücretsiz planda dakikada 1200 istek limitine sahip, bu bolca görünüyor ama döngüleri hızlı koşturduğunda sınıra takılabilirsin. sleep 0.3 genellikle güvenli bir tampon.
DNS Kayıtlarını Toplu Güncelleme
Güncelleme işlemi için önce kaydın ID’sini bilmen gerekiyor. Adı ile ID’yi eşleştiren bir yardımcı fonksiyon yazmak çok işe yarıyor:
#!/bin/bash
source ~/.cloudflare_env
# Kayit adina gore ID bul
get_record_id() {
local name=$1
local type=$2
curl -s -X GET
"$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?name=$name&type=$type"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json" |
jq -r '.result[0].id // empty'
}
# Belirli bir IP blogundan gelen tum A kayitlarini yeni IP'ye guncelle
OLD_IP_PREFIX="10.0.1."
NEW_IP_PREFIX="10.0.2."
# Tum A kayitlarini cek
all_a_records=$(curl -s -X GET
"$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?type=A&per_page=100"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json" |
jq -r '.result[] | select(.content | startswith("'"$OLD_IP_PREFIX"'")) | [.id, .name, .content] | @tsv')
while IFS=$'t' read -r record_id name old_ip; do
new_ip="${old_ip/$OLD_IP_PREFIX/$NEW_IP_PREFIX}"
echo "Guncelleniyor: $name | $old_ip -> $new_ip"
response=$(curl -s -X PATCH
"$CF_API_BASE/zones/$CF_ZONE_ID/dns_records/$record_id"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json"
--data "{"content": "$new_ip"}")
if echo "$response" | jq -e '.success' > /dev/null 2>&1; then
echo " OK"
else
echo " HATA: $(echo "$response" | jq -r '.errors[0].message')"
fi
sleep 0.3
done <<< "$all_a_records"
Bu script özellikle sunucu göçlerinde çok işe yarıyor. Tüm bir IP bloğunu yeni bir blokla değiştiriyorsun, tek tek uğraşmak yok.
Python ile Daha Güçlü Toplu Yönetim
Bash ile çok şey yapılabiliyor ama karmaşık mantık için Python çok daha rahat. requests kütüphanesiyle şöyle bir araç yazabilirsin:
#!/usr/bin/env python3
import requests
import csv
import json
import time
import os
from datetime import datetime
class CloudflareDNS:
def __init__(self, api_token, zone_id):
self.base_url = "https://api.cloudflare.com/client/v4"
self.zone_id = zone_id
self.headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
def get_all_records(self, record_type=None):
records = []
page = 1
while True:
params = {"per_page": 100, "page": page}
if record_type:
params["type"] = record_type
resp = requests.get(
f"{self.base_url}/zones/{self.zone_id}/dns_records",
headers=self.headers,
params=params
)
data = resp.json()
if not data.get("success"):
raise Exception(f"API hatasi: {data.get('errors')}")
records.extend(data["result"])
if page >= data["result_info"]["total_pages"]:
break
page += 1
time.sleep(0.2)
return records
def create_record(self, record_type, name, content, ttl=300, proxied=False):
payload = {
"type": record_type,
"name": name,
"content": content,
"ttl": ttl,
"proxied": proxied
}
resp = requests.post(
f"{self.base_url}/zones/{self.zone_id}/dns_records",
headers=self.headers,
json=payload
)
return resp.json()
def delete_record(self, record_id):
resp = requests.delete(
f"{self.base_url}/zones/{self.zone_id}/dns_records/{record_id}",
headers=self.headers
)
return resp.json()
def bulk_import_from_csv(self, filename):
results = {"success": 0, "fail": 0, "errors": []}
with open(filename, "r") as f:
reader = csv.DictReader(f)
for row in reader:
result = self.create_record(
record_type=row["type"],
name=row["name"],
content=row["content"],
ttl=int(row.get("ttl", 300)),
proxied=row.get("proxied", "false").lower() == "true"
)
if result.get("success"):
results["success"] += 1
print(f"OK: {row['type']} {row['name']}")
else:
results["fail"] += 1
error_msg = str(result.get("errors", "Bilinmeyen hata"))
results["errors"].append({"record": row["name"], "error": error_msg})
print(f"HATA: {row['name']} - {error_msg}")
time.sleep(0.3)
return results
if __name__ == "__main__":
cf = CloudflareDNS(
api_token=os.environ["CF_API_TOKEN"],
zone_id=os.environ["CF_ZONE_ID"]
)
# Tum kayitlari backup al
print("Mevcut kayitlar yedekleniyor...")
records = cf.get_all_records()
backup_file = f"dns_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(backup_file, "w") as f:
json.dump(records, f, indent=2)
print(f"Yedek alindi: {backup_file} ({len(records)} kayit)")
# CSV'den toplu import
print("nCSV'den kayitlar import ediliyor...")
results = cf.bulk_import_from_csv("records_to_add.csv")
print(f"nSonuc: {results['success']} basarili, {results['fail']} basarisiz")
Bu Python sınıfını bir modül olarak kullanıp farklı scriptlerde import edebilirsin. Gerçekten işleri bir üst seviyeye taşıyan bir yaklaşım bu.
Gerçek Dünya Senaryosu: Sunucu Göçü Sırasında DNS Yönetimi
Geçen yıl 8 müşterinin DNS kayıtlarını yeni sunuculara taşımak zorunda kaldık. Her müşterinin onlarca subdomaini vardı ve hepsini sırayla güncellemek zorundaydık. Üstelik TTL’leri düşürme, göç, test ve TTL’leri geri yükseltme aşamalarını yönetmek gerekiyordu.
Şöyle bir strateji izledik:
Aşama 1: TTL’leri düşür (değişiklikten 24 saat önce)
Önce tüm A kayıtlarının TTL’ini 300 saniyeye indirdik. Bu, sonraki güncellemelerin hızlı yayılmasını sağlıyor:
#!/bin/bash
source ~/.cloudflare_env
# Tum A kayitlarinin TTL'ini 300'e indir
curl -s "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?type=A&per_page=100"
-H "Authorization: Bearer $CF_API_TOKEN" |
jq -r '.result[] | [.id, .name, .content, (.proxied | tostring)] | @tsv' |
while IFS=$'t' read -r id name content proxied; do
echo "TTL dusruluyor: $name"
curl -s -X PATCH "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records/$id"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json"
--data '{"ttl": 300}' > /dev/null
sleep 0.3
done
echo "Tum A kayitlarinin TTL'i 300 saniyeye indirildi."
Aşama 2: IP’leri güncelle (göç sonrası)
Yeni IP adresleri ile kayıtları güncelle, test et, sorun yoksa TTL’leri tekrar yükselt.
Aşama 3: TTL’leri normalize et
# TTL'leri tekrar 3600'e al
jq -r '.result[] | [.id] | @tsv' <<< "$all_records" |
while read -r id; do
curl -s -X PATCH "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records/$id"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json"
--data '{"ttl": 3600}' > /dev/null
sleep 0.3
done
Duplicate Kayıtları Temizleme
Zamanla DNS kayıtları arasında duplikasyon oluşabiliyor. Özellikle birden fazla kişi yönetim yapıyorsa bu çok sık karşılaşılan bir problem. Şu script duplikeleri tespit edip raporluyor:
#!/bin/bash
source ~/.cloudflare_env
echo "Duplicate DNS kayitlari taranıyor..."
curl -s "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?per_page=100"
-H "Authorization: Bearer $CF_API_TOKEN" |
jq -r '.result[] | [.type, .name, .content] | @tsv' |
sort | uniq -d |
while IFS=$'t' read -r type name content; do
echo "DUPLICATE BULUNDU: $type $name -> $content"
# Bu kayidin tum ID'lerini bul
ids=$(curl -s "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?name=$name&type=$type"
-H "Authorization: Bearer $CF_API_TOKEN" |
jq -r '.result[] | select(.content == "'"$content"'") | .id')
echo " ID'ler: $ids"
echo " Ilki haricindeki duplikeler silinecek. Devam? (y/n)"
read -r confirm
if [ "$confirm" = "y" ]; then
first=true
for id in $ids; do
if $first; then
first=false
continue
fi
curl -s -X DELETE "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records/$id"
-H "Authorization: Bearer $CF_API_TOKEN" > /dev/null
echo " Silindi: $id"
done
fi
done
Cloudflare Proxy Durumunu Toplu Yönetme
Bazen belirli kayıtlar için Cloudflare proxy’sini (turuncu bulut) açıp kapamak gerekiyor. Özellikle mail kayıtları, FTP, SSH gibi servislerin proxy üzerinden geçmemesi gerekiyor. Şu script bu durumu denetliyor:
#!/bin/bash
source ~/.cloudflare_env
# Mail ile ilgili kayitlarda proxy kapali olmali
problematic_records=$(curl -s
"$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?per_page=100"
-H "Authorization: Bearer $CF_API_TOKEN" |
jq -r '.result[] |
select(
(.proxied == true) and
(.name | (startswith("mail.") or startswith("smtp.") or startswith("imap.") or startswith("pop.")))
) | [.id, .name, .type] | @tsv')
if [ -z "$problematic_records" ]; then
echo "Sorunlu kayit bulunamadi. Her sey yolunda."
exit 0
fi
echo "Proxy aktif olmamasi gereken kayitlar:"
echo "$problematic_records" | while IFS=$'t' read -r id name type; do
echo " - $type $name (ID: $id)"
done
echo ""
echo "Bu kayitlarda proxy kapatilsin mi? (y/n)"
read -r confirm
if [ "$confirm" = "y" ]; then
echo "$problematic_records" | while IFS=$'t' read -r id name type; do
curl -s -X PATCH "$CF_API_BASE/zones/$CF_ZONE_ID/dns_records/$id"
-H "Authorization: Bearer $CF_API_TOKEN"
-H "Content-Type: application/json"
--data '{"proxied": false}' > /dev/null
echo "Proxy kapatildi: $name"
sleep 0.3
done
fi
Hata Yönetimi ve Loglama
Production ortamında bu scriptleri çalıştırırken mutlaka iyi bir loglama mekanizması kur. En basit haliyle şöyle yapabilirsin:
# Log fonksiyonu
LOG_FILE="/var/log/cloudflare_dns_$(date +%Y%m%d).log"
log() {
local level=$1
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "$LOG_FILE"
}
log "INFO" "DNS guncelleme islemi baslatildi"
log "ERROR" "Kayit guncellenemedi: $name - $error_message"
log "SUCCESS" "Tum islemler tamamlandi: $SUCCESS_COUNT kayit guncellendi"
API hata kodlarına dikkat etmek de önemli:
- 6003: Geçersiz API Token
- 7003: Zone bulunamadı
- 81057: Kayıt zaten mevcut
- 81044: Geçersiz içerik formatı
- 10000: Authentication hatası
Bu hata kodlarını scriptlerinde yakalayıp uygun şekilde işlemek, sorun çıktığında nerede olduğunu hızlı anlamana yardımcı olur.
Cron ile Otomatik DNS Denetimi
Mevcut DNS kayıtlarının beklenen değerlerde olduğunu periyodik olarak kontrol eden bir monitoring scripti de ekleyebilirsin:
#!/bin/bash
# /opt/scripts/cf_dns_audit.sh
source ~/.cloudflare_env
EXPECTED_FILE="/opt/scripts/expected_dns.tsv"
ALERT_EMAIL="[email protected]"
ISSUES_FOUND=false
while IFS=$'t' read -r expected_type expected_name expected_content; do
actual=$(curl -s
"$CF_API_BASE/zones/$CF_ZONE_ID/dns_records?name=$expected_name&type=$expected_type"
-H "Authorization: Bearer $CF_API_TOKEN" |
jq -r '.result[0].content // "NOT_FOUND"')
if [ "$actual" != "$expected_content" ]; then
echo "UYARI: $expected_name beklenen $expected_content, bulunan $actual"
ISSUES_FOUND=true
fi
done < "$EXPECTED_FILE"
if $ISSUES_FOUND; then
echo "DNS tutarsizliklari tespit edildi, $ALERT_EMAIL adresine bildirim gonderildi"
# mail -s "DNS Audit Uyarisi" $ALERT_EMAIL < /tmp/dns_audit_report.txt
fi
Bunu cron’a ekle:
# Her saat basinda calistir
0 * * * * /opt/scripts/cf_dns_audit.sh >> /var/log/cf_dns_audit.log 2>&1
Sonuç
Cloudflare API’si, DNS yönetimini gerçekten başka bir boyuta taşıyor. Tek seferlik toplu işlemler için curl ile yazılmış basit scriptler yeterli olurken, sürekli bakım ve otomasyon için Python tabanlı araçlar çok daha uygun.
Özellikle şu noktalara dikkat etmeni öneririm:
- Her büyük işlem öncesi backup al. Silinen bir kayıt anında geri gelmiyor, tarihsel kayıtlar da tutulmuyor.
- Rate limiting’e saygı göster.
sleep 0.3gibi küçük gecikmeler seni büyük sorunlardan korur. - API Token’ını minimum yetki prensibine göre oluştur. Sadece DNS düzenleme yetkisi vermek, hesap genelinde tam yetki vermekten çok daha güvenli.
- Scriptleri önce test ortamında dene. Cloudflare’in test zone’ları için ekstra ücret ödemiyorsun, önce orada koştur.
- Değişiklikleri logla. Kimin, ne zaman, ne değiştirdiğini bilmek sorun gidermeyi dramatik biçimde kolaylaştırır.
Bu araçları bir kez kurduğunda, onlarca subdomaini dakikalar içinde yönetmeye başlarsın. Başta biraz zaman alıyor ama sonrasında kazandığın verimlilik buna kesinlikle değiyor.
