Gérer une zone DNS sous forme de fichier dans le contrôle de version
Ajouter des enregistrements dans une console fonctionne jusqu’au jour où vous devez savoir ce qui a
changé ou recréer une zone ailleurs. Ce tutoriel conserve chaque enregistrement d’un domaine dans un
fichier texte, l’applique avec un court script et la CLI zcp, puis place ce fichier dans le
contrôle de version avec le reste de votre infrastructure.
À la fin, vous disposez de :
- Un fichier d’enregistrements qui décrit toute la zone
- Un script qui l’applique et peut d’abord vous montrer ce qu’il ferait
- Une méthode répétable pour recréer la zone à partir du fichier
Prévoyez environ 20 minutes.
Avant de commencer
Section intitulée « Avant de commencer »Vous avez besoin de :
- La CLI
zcpinstallée et authentifiée. Consultez l’installation de la CLI. - Un projet et un domaine que vous contrôlez.
- Remplacez
example.capar un domaine que vous contrôlez. Utilisez vos vraies adresses d’entrée et vos serveurs de noms. - Un dépôt Git pour conserver le fichier.
jq, utilisé par le script pour lire le slug de la zone dans la sortie JSON de la CLI. Installez-le avecbrew install jqsur macOS ouapt install jqsur Debian et Ubuntu.
Lisez les limitations connues avant de commencer. Chaque combinaison de nom et de type ne peut aujourd’hui contenir qu’une seule valeur, ce qui limite ce qu’un fichier de ce type peut exprimer.
Étape 1 : Choisir une disposition
Section intitulée « Étape 1 : Choisir une disposition »Utilisez une ligne par enregistrement, avec le contenu à la fin afin qu’il puisse contenir des espaces :
NAME TYPE TTL PRIO CONTENTQuatre règles rendent le fichier lisible et facile à analyser :
@représente l’apex de la zone-indique que le type ne prend pas de priorité- Les lignes qui commencent par
#sont des commentaires CONTENTest le dernier champ et peut donc contenir des espaces
Étape 2 : Écrire le fichier d’enregistrements
Section intitulée « Étape 2 : Écrire le fichier d’enregistrements »Créez dns/zones/example.ca.records. Regroupez les enregistrements par objectif et alignez les
colonnes, afin qu’une personne qui révise le fichier voie la structure de la zone sans l’analyser :
# example.ca - zone records## Columns are whitespace separated and CONTENT is the final field, so it may# contain spaces. "@" is the zone apex and "-" means the type takes no priority.## TXT values keep their double quotes, because the API rejects an unquoted value.
# NAME TYPE TTL PRIO CONTENT
# Website ----------------------------------------------------------------- @ A 300 - 198.51.100.10 www CNAME 300 - example.ca.
# YOW shared ingress ------------------------------------------------------- yow-edge A 300 - 192.0.2.10 status CNAME 300 - yow-edge.example.ca. objects CNAME 300 - yow-edge.example.ca.
# YUL shared ingress ------------------------------------------------------- yul-edge A 300 - 198.51.100.20 api CNAME 300 - yul-edge.example.ca. app CNAME 300 - yul-edge.example.ca.
# Mail routing ------------------------------------------------------------ @ MX 300 10 mail.example.ca.
# Mail authentication ----------------------------------------------------- @ TXT 300 - "v=spf1 include:mailprovider.ca -all" _dmarc TXT 300 - "v=DMARC1; p=quarantine; rua=mailto:security@example.ca"Conservez le fichier près de votre code d’infrastructure, pas dans un dossier personnel. Sa valeur vient du fait que tout le monde le voit et que les changements passent par une révision.
Étape 3 : Utiliser des noms d’entrée régionaux partagés
Section intitulée « Étape 3 : Utiliser des noms d’entrée régionaux partagés »Utilisez un enregistrement A comme cible d’entrée régionale seulement lorsque ses services partagent la même entrée et évoluent ensemble. Dirigez chaque service vers cette cible avec un CNAME direct. Mettre à jour l’enregistrement A change alors la destination de chaque alias dans ce groupe régional. N’utilisez ni chaînes ni boucles de CNAME.
Le fichier example.ca regroupe les cibles yow-edge et yul-edge avec les services qu’elles
servent.
Un nom portant un enregistrement CNAME ne peut pas contenir d’autres données d’enregistrement. Conservez chaque cible régionale sous forme d’enregistrement A direct. Conservez l’apex de la zone comme enregistrement A, car l’apex porte aussi les enregistrements SOA et NS et peut porter des enregistrements MX ou TXT. Les cibles MX et NS doivent pointer directement vers des enregistrements d’adresse, pas vers des CNAME. Consultez la RFC 1034, section 3.6.2 et la RFC 2181, sections 10.1 et 10.3.
Un CNAME change uniquement la recherche DNS. Il ne crée pas de redirection HTTP. Les clients
envoient toujours le nom du service d’origine dans SNI TLS et dans l’en-tête HTTP Host, donc le
routage et les certificats doivent couvrir chaque nom de service. Une cible partagée n’ajoute pas
non plus de haute disponibilité ou de bande passante automatiquement.
Le TTL contrôle la durée pendant laquelle les résolveurs peuvent mettre chaque réponse en cache. Utilisez un TTL court, comme 300, pendant une migration, puis augmentez-le lorsque les enregistrements sont stables. Un TTL plus court n’invalide pas les caches existants. Le comportement du cache DNS est défini dans la RFC 1034, section 2.3.
Étape 4 : Écrire le script d’application
Section intitulée « Étape 4 : Écrire le script d’application »Créez dns/apply-zone.sh. Il crée la zone si elle est absente, puis parcourt le fichier ligne par
ligne :
#!/usr/bin/env bash# Apply a records file to a DNS zone with the zcp CLI.## Usage: dns/apply-zone.sh <zone-name> <records-file> <project-slug> [--dry-run]set -euo pipefail
ZONE="${1:?zone name required}"FILE="${2:?records file required}"PROJECT="${3:?project slug required}"DRY_RUN="${4:-}"
[ -f "$FILE" ] || { echo "records file not found: $FILE" >&2; exit 1; }case "$DRY_RUN" in ''|--dry-run) ;; *) echo "unknown option: $DRY_RUN (expected --dry-run or nothing)" >&2; exit 2 ;;esac
run() { if [ "$DRY_RUN" = "--dry-run" ]; then printf ' would run: zcp %s\n' "$*" else zcp "$@" fi}
slug_for_zone() { zcp dns list --project "$PROJECT" --region default -o json 2>/dev/null \ | jq -r --arg z "$ZONE" '(.data // .)[] | select(.name == $z) | .slug' | head -1}
SLUG="$(slug_for_zone)"if [ -z "$SLUG" ]; then echo "Creating zone $ZONE in project $PROJECT" run dns create --name "$ZONE" --project "$PROJECT" --region default -y [ "$DRY_RUN" = "--dry-run" ] || SLUG="$(slug_for_zone)"fiecho "Zone slug: ${SLUG:-<pending>}"
FAILED=0
# CONTENT is the last field and may contain spaces, so read the first four# fields and let the remainder fall into CONTENT.while read -r NAME TYPE TTL PRIO CONTENT; do case "$NAME" in ''|\#*) continue ;; esac ARGS=(dns record-create --domain "${SLUG:-PENDING}" --name "$NAME" --type "$TYPE" \ --ttl "$TTL" --content "$CONTENT" --project "$PROJECT" --region default -y) [ "$PRIO" != "-" ] && ARGS+=(--priority "$PRIO") echo "-> $NAME $TYPE $CONTENT" if ! run "${ARGS[@]}"; then echo " [FAIL] $NAME $TYPE" >&2 FAILED=1 fidone < "$FILE"
if [ "$FAILED" -ne 0 ]; then echo "One or more records failed. The zone is partially applied." >&2 exit 1fi
echo "Done. Verify with: dig NS $ZONE +short"Rendez-le exécutable :
chmod +x dns/apply-zone.shÉtape 5 : Commencer par une simulation
Section intitulée « Étape 5 : Commencer par une simulation »Ne pointez jamais un nouveau script vers une zone active. Imprimez d’abord les commandes :
dns/apply-zone.sh example.ca dns/zones/example.ca.records my-project --dry-runLisez la sortie. Chaque ligne doit contenir le nom, le type et le contenu attendus, et les lignes MX doivent inclure une priorité. Une valeur TXT entre guillemets doit conserver ses guillemets.
Étape 6 : Appliquer
Section intitulée « Étape 6 : Appliquer »dns/apply-zone.sh example.ca dns/zones/example.ca.records my-projectLe script indique chaque enregistrement au fur et à mesure et marque ceux qui échouent avec [FAIL]
au lieu de s’arrêter. Lisez cette sortie : une application partielle est le mode d’échec à détecter
tôt.
Étape 7 : Vérifier avec le fichier
Section intitulée « Étape 7 : Vérifier avec le fichier »Vérifiez ce que les serveurs de noms servent réellement plutôt que ce que la console affiche :
zcp dns show <zone-slug> --project my-project --region defaultfor ns in ns1.dns.example.ca ns2.dns.example.ca; do dig @"$ns" A example.ca +short dig @"$ns" A yow-edge.example.ca +short dig @"$ns" A yul-edge.example.ca +short dig @"$ns" CNAME status.example.ca +short dig @"$ns" CNAME api.example.ca +short dig @"$ns" MX example.ca +short dig @"$ns" TXT example.ca +shortdoneInterrogez les deux serveurs de noms. Un enregistrement qui répond sur l’un et pas sur l’autre signifie que la zone n’a pas fini de se propager entre eux.
- Validez le fichier, jamais un jeton. Le fichier décrit les enregistrements. Les identifiants appartiennent à votre magasin de secrets et atteignent le script par l’environnement.
- Des TTL courts pendant les itérations. 300 secondes rendent les erreurs peu coûteuses. Augmentez-les lorsque la zone est stabilisée.
- Le fichier est la source de vérité. Lorsqu’une personne modifie un enregistrement dans la console, le fichier et la zone divergent. Réappliquez le fichier ou mettez-le à jour pour qu’il corresponde, et indiquez ce que vous avez fait.
record-createremplace un RRset correspondant. Il remplace un ensemble d’enregistrements existant avec le même nom et le même type. Il ne peut pas migrer un enregistrement A vers un CNAME parce que l’ancien A doit être retiré avant que le CNAME existe pour ce propriétaire. Créez et vérifiez d’abord l’enregistrement A cible, puis retirez chaque ancien A de service et ajoutez son CNAME.- Ne gérez pas les RRsets TXT à plusieurs valeurs avec cette boucle. Chaque ligne TXT indépendante du même nom remplace le RRset TXT de ce nom. Utilisez le support pour un flux qui gère des valeurs distinctes dans un même RRset, ou excluez du fichier les RRsets à plusieurs valeurs gérés ailleurs afin d’éviter tout écrasement.
- Le script ajoute et met à jour, il ne purge pas. Il applique les enregistrements nommés dans
le fichier et rien d’autre. Supprimer une ligne du fichier ne supprime pas cet enregistrement de
la zone. Retirez-le vous-même avec
zcp dns record-delete, puis supprimez la ligne afin que le fichier et la zone restent synchronisés.