coredns/scripts/prepare-zones.sh
Ryan Malloy 10867ee319 coredns: docker compose stack with Vultr zone import
- Auto plugin loads zones-prepared/*.zone (regex zone-name extraction)
- scripts/prepare-zones.sh transforms raw Vultr exports:
  * synthesizes SOA (omitted by Vultr; CoreDNS requires it)
  * prepends @ to leading-TAB apex lines to disambiguate owner inheritance
  * dot-terminates NS/MX/CNAME rdata so $ORIGIN doesn't double-suffix
- DNS_PORT defaults to 1053 (5353=avahi, 53=libvirt dnsmasq on this host)
- Forwards non-authoritative queries to 1.1.1.1/1.0.0.1/9.9.9.9
- Makefile targets: prep, up, down, reload, test, logs
- 91 zones loaded
2026-05-12 01:51:09 -06:00

86 lines
3.1 KiB
Bash
Executable File

#!/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.<zone>.
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})"