#!/usr/bin/env bash # Injects an SOA record into each raw Vultr zone file and writes the result # to zones-prepared/. Source files in zones/ are never modified. # # Two corrections applied to each zone: # 1. Synthesize SOA — Vultr's export omits it, but CoreDNS rejects zones # without one. Serial is YYYYMMDD01 (override via SERIAL env var). # 2. Add trailing dots to NS/MX/CNAME rdata. Vultr exports unqualified # hostnames (e.g. "ns1.vultr.com") which $ORIGIN then incorrectly # suffixes with the zone name. We dot-terminate any rdata token that # contains a "." and doesn't already end in one. set -euo pipefail SRC_DIR="${SRC_DIR:-zones}" DST_DIR="${DST_DIR:-zones-prepared}" SERIAL="${SERIAL:-$(date +%Y%m%d)01}" ADMIN_EMAIL="${ADMIN_EMAIL:-admin}" # becomes admin.. mkdir -p "$DST_DIR" count=0 for src in "$SRC_DIR"/*.zone; do fname=$(basename "$src") zone="${fname%.zone}" dst="$DST_DIR/$fname" { echo "; Auto-prepared by scripts/prepare-zones.sh on $(date -Iseconds)" echo "; Source: $src" echo "\$ORIGIN ${zone}." echo "\$TTL 3600" echo "@ 3600 IN SOA ns1.vultr.com. ${ADMIN_EMAIL}.${zone}. (" echo " ${SERIAL} ; serial" echo " 3600 ; refresh (1 hour)" echo " 1800 ; retry (30 minutes)" echo " 604800 ; expire (1 week)" echo " 300 ; minimum (5 minutes)" echo " )" echo "" # Strip source's own $ORIGIN/$TTL/comments, then apply two fixes: # # (a) Apex disambiguation. Vultr uses leading-TAB-then-TTL to mean # "apex record", but RFC 1035 says lines starting with whitespace # inherit the *previous* owner. When a non-apex record precedes # a leading-TAB apex line, the apex line silently gets attached # to the wrong name. Prepend "@" to make apex explicit. # # (b) Dot-terminate NS/MX/CNAME rdata. Vultr exports unqualified # hostnames which $ORIGIN then incorrectly suffixes. # # We never re-emit via awk fields (preserves whitespace inside the # line for owner-inheritance correctness in any unfixed lines). grep -vE '^\$(ORIGIN|TTL)|^;' "$src" | awk ' NF == 0 { print; next } { # (a) Detect Vultr-style apex line: leading whitespace, then TTL, # then "IN". Prepend "@" so the owner is explicit. if ($0 ~ /^[[:space:]]+[0-9]+[[:space:]]+IN[[:space:]]/) { sub(/^[[:space:]]+/, "@\t", $0) # awk has re-split $0 — re-parse for the next step. # (NF/$N references below are based on the modified $0.) } # (b) Dot-terminate trailing hostname for NS/CNAME/MX rdata. type = "" for (i = 1; i <= NF; i++) { if ($i == "NS" || $i == "CNAME" || $i == "MX") { type = $i; break } } if (type != "") { target = $NF if (index(target, ".") > 0 && substr(target, length(target), 1) != ".") { sub(/[[:space:]]+$/, "", $0) print $0 "." next } } print } ' } > "$dst" count=$((count + 1)) done echo "Prepared ${count} zone files in ${DST_DIR}/ (serial=${SERIAL})"