Nejakú dobu už používam GitHub Actions na automatické zálohovanie DNS záznamov Cloudflare ako JSON súborov. Je to jednoduchý, ale efektívny spôsob, ako sledovať zmeny DNS a mať záchranné lano pre prípad, že niečo pôjde zle. Nedávno som sa rozhodol toto nastavenie vylepšiť tak, aby zálohoval DNS záznamy aj vo formáte zónového súboru BIND9 popri JSON zálohách.
Problém s výlučne JSON zálohou #
Hoci sú JSON zálohy z Cloudflare API komplexné a obsahujú všetky metadáta (ako stav proxy, ID záznamov atď.), majú jedno výrazné obmedzenie: vendor lock-in. Ak by som niekedy potreboval rýchlo migrovať z Cloudflare k inému poskytovateľovi DNS alebo nastaviť vlastný DNS server, formát JSON nie je priamo použiteľný.
Väčšina DNS serverov očakáva zónové súbory v štandardnom formáte BIND9, ktorý je univerzálnym formátom zónových súborov DNS od počiatkov internetu. Zálohy v tomto formáte znamenajú, že môžem obnoviť DNS záznamy prakticky na akomkoľvek DNS serveri bez nutnosti konverzie.
Objavenie skrytého BIND9 exportu Cloudflare #
Pri skúmaní stratégií zálohovania DNS som objavil, že Cloudflare v skutočnosti poskytuje priamy BIND9 exportný endpoint, o ktorom som nevedel:
GET /zones/{zone_id}/dns_records/export
Tento endpoint vracia správny BIND9 zónový súbor namiesto JSON, kompletný so SOA záznamami, správnym formátovaním a všetkými štandardnými typmi DNS záznamov. V ich dokumentácii nie je nijak výrazne uvedený, ale existuje a funguje perfektne.
Pridanie BIND9 exportu #
Vylepšenie pôvodného skriptu bolo prekvapivo jednoduché. Stačilo pridať jeden ďalší API volanie na načítanie BIND9 exportu. Tu je môj aktualizovaný workflow:
name: Cloudflare DNS Backup
on:
workflow_dispatch:
schedule:
- cron: "40 0 * * 0"
jobs:
backup:
runs-on: ubuntu-22.04
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- run: |
# Create backup directory
mkdir -p dns-backups
# Get list of zones (domains)
zones=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" | jq -r '.result[].id')
# Loop through zones and backup DNS records
for zone in $zones; do
# Get zone name
zone_name=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" | jq -r '.result.name')
echo "Backing up DNS records for $zone_name"
# Get DNS records and save JSON
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" | jq > "dns-backups/$zone_name.json"
# Get DNS records as BIND9 zone file and normalize timestamps
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone/dns_records/export" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | \
sed 's/;; Exported: [0-9-]* [0-9:]*/;; Exported: [TIMESTAMP_REMOVED]/' | \
sed 's/\([a-zA-Z0-9.-]*\)\s*3600\s*IN\s*SOA\s*\([a-zA-Z0-9.-]*\)\s*\([a-zA-Z0-9.-]*\)\s*[0-9]* \(.*\)/\1\t3600\tIN\tSOA\t\2 \3 [SERIAL_REMOVED] \4/' \
> "dns-backups/$zone_name.zone"
done
- uses: stefanzweifel/git-auto-commit-action@v5
Riešenie problému s časovými pečiatkami #
Jeden problém, ktorý som objavil, bol, že BIND9 export Cloudflare obsahuje časové pečiatky a inkrementálne sériové čísla, ktoré sa menia pri každom exporte, aj keď sa skutočné DNS záznamy nezmenili. To by spôsobovalo zbytočné commity pri každom spustení workflowu.
Zónové súbory vyzerali takto:
;;
;; Domain: example.com.
-;; Exported: 2025-08-14 04:50:19
+;; Exported: 2025-08-14 08:38:48
;;
-example.com 3600 IN SOA ns1.cloudflare.com. dns.cloudflare.com. 2050681361 10000 2400 604800 3600
+example.com 3600 IN SOA ns1.cloudflare.com. dns.cloudflare.com. 2050682732 10000 2400 604800 3600
Na vyriešenie tohto problému som pridal spracovanie pomocou sed, ktoré:
- Nahradí časovú pečiatku exportu reťazcom
[TIMESTAMP_REMOVED] - Nahradí sériové číslo SOA reťazcom
[SERIAL_REMOVED]
Tým sa zabezpečí, že zónové súbory zobrazujú zmeny iba vtedy, keď sa skutočne zmenia DNS záznamy, nie len preto, že záloha prebehla v iný čas.
Príprava zónových súborov na núdzový import #
Zónové súbory v zálohe obsahujú zástupné reťazce, ktoré treba pred importom do DNS servera nahradiť:
[TIMESTAMP_REMOVED]treba nahradiť aktuálnym dátumom a časom[SERIAL_REMOVED]treba nahradiť platným sériovým číslom SOA
Pre núdzové použitie môžete súbory rýchlo pripraviť jednoduchým skriptom:
#!/bin/bash
# Emergency DNS restore preparation script
CURRENT_SERIAL=$(date +%Y%m%d01) # Format: YYYYMMDD01
CURRENT_TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
for zonefile in dns-backups/*.zone; do
echo "Preparing $zonefile for import..."
# Create import-ready version
sed "s/\[SERIAL_REMOVED\]/$CURRENT_SERIAL/g" "$zonefile" | \
sed "s/\[TIMESTAMP_REMOVED\]/$CURRENT_TIMESTAMP/g" > "ready-to-import-$(basename "$zonefile")"
done
echo "Zone files ready for import in ready-to-import-*.zone files"
Prípadne môžete použiť jednoduché hodnoty:
- Sériové číslo:
1(DNS server to pravdepodobne akceptuje a odtiaľ bude inkrementovať) - Časová pečiatka: aktuálny dátum a čas v momente importu
Podstata je, že tieto zástupné reťazce zabraňujú zbytočným commitom, pričom zónové súbory zostávajú vo formáte, ktorý sa dá ľahko previesť na produkčné súbory, keď je to potrebné.
Výhody #
Teraz mám stratégiu zálohovania v dvoch formátoch. Výhody JSON záloh:
- Kompletné Cloudflare metadáta
- Informácie o stave proxy
- ID záznamov a dátumy vytvorenia
- Ideálne na obnovenie na Cloudflare
Výhody BIND9 záloh:
- Univerzálna kompatibilita
- Možno importovať do akéhokoľvek DNS servera
- Štandardný formát zrozumiteľný všetkými DNS nástrojmi
- Pripravené na núdzové situácie so správnymi SOA záznamami
Workflow teraz automaticky udržiava oba formáty vo verzionovacom systéme, takže ľahko vidím, čo sa zmenilo a kedy. Ak by som niekedy potreboval rýchlo migrovať z Cloudflare alebo nastaviť núdzový DNS, mám okamžite použiteľné BIND9 zónové súbory pripravené. Užite si!