fix: convert Technitium zone sync from one-time Job to CronJob

Secondary/tertiary DNS instances had no custom zones — only the
primary had viktorbarzin.lan and viktorbarzin.me. The old setup Job
ran once at deployment and never synced new zones.

New CronJob runs every 30 minutes:
- Gets all zones from primary
- Enables zone transfer on primary
- Creates missing zones as Secondary type on replicas
- Resyncs existing zones via AXFR

Fixes .lan resolution failures (2/3 queries returned NXDOMAIN).

[ci skip]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-04-14 12:18:19 +00:00
parent c0a33b5157
commit 803cb5fd26

View file

@ -356,88 +356,95 @@ resource "kubernetes_pod_disruption_budget_v1" "technitium_dns" {
} }
# Setup Job configures secondary + tertiary zones via Technitium REST API # Setup Job configures secondary + tertiary zones via Technitium REST API
resource "kubernetes_job" "technitium_secondary_setup" { # Zone sync CronJob replicates all primary zones to secondary/tertiary
# Runs every 30 minutes. Idempotent: skips zones that already exist on replicas.
resource "kubernetes_cron_job_v1" "technitium_zone_sync" {
metadata { metadata {
name = "technitium-replica-setup" name = "technitium-zone-sync"
namespace = kubernetes_namespace.technitium.metadata[0].name namespace = kubernetes_namespace.technitium.metadata[0].name
} }
spec { spec {
backoff_limit = 5 schedule = "*/30 * * * *"
template { successful_jobs_history_limit = 3
failed_jobs_history_limit = 3
concurrency_policy = "Forbid"
job_template {
metadata {} metadata {}
spec { spec {
restart_policy = "OnFailure" backoff_limit = 2
container { template {
name = "setup" metadata {}
image = "curlimages/curl:latest" spec {
command = ["/bin/sh", "-c", <<-SCRIPT restart_policy = "OnFailure"
set -e container {
PRIMARY="http://technitium-primary.technitium.svc.cluster.local:5380" name = "zone-sync"
REPLICAS="http://technitium-secondary-web.technitium.svc.cluster.local:5380 http://technitium-tertiary-web.technitium.svc.cluster.local:5380" image = "curlimages/curl:latest"
command = ["/bin/sh", "-c", <<-SCRIPT
set -e
PRIMARY="http://technitium-primary.technitium.svc.cluster.local:5380"
REPLICAS="http://technitium-secondary-web.technitium.svc.cluster.local:5380 http://technitium-tertiary-web.technitium.svc.cluster.local:5380"
# Wait for primary # Login to primary
until curl -sf "$PRIMARY/api/user/login?user=$TECH_USER&pass=$TECH_PASS" -o /tmp/p.json; do echo "Waiting for primary..."; sleep 5; done P_TOKEN=$(curl -sf "$PRIMARY/api/user/login?user=$TECH_USER&pass=$TECH_PASS" | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
P_TOKEN=$(cat /tmp/p.json | sed -n 's/.*"token":"\([^"]*\)".*/\1/p') if [ -z "$P_TOKEN" ]; then echo "ERROR: Cannot login to primary"; exit 1; fi
# Get zones from primary # Get zones from primary (excluding default zones that don't need replication)
curl -sf "$PRIMARY/api/zones/list?token=$P_TOKEN" | tr ',' '\n' | sed -n 's/.*"name":"\([^"]*\)".*/\1/p' > /tmp/zones.txt curl -sf "$PRIMARY/api/zones/list?token=$P_TOKEN" | tr ',' '\n' | sed -n 's/.*"name":"\([^"]*\)".*/\1/p' | \
echo "Found zones:"; cat /tmp/zones.txt grep -v -E '^(localhost|0\.in-addr\.arpa|127\.in-addr\.arpa|255\.in-addr\.arpa|1\.0\.0.*ip6\.arpa)$$' > /tmp/primary_zones.txt
echo "Primary has $(wc -l < /tmp/primary_zones.txt) zones to replicate"
# Enable zone transfers on primary # Enable zone transfers on primary for all zones
while read -r zone; do while read -r zone; do
echo "Enabling zone transfer for: $zone" curl -sf "$PRIMARY/api/zones/options/set?token=$P_TOKEN&zone=$zone&zoneTransfer=Allow" > /dev/null || true
curl -sf "$PRIMARY/api/zones/options/set?token=$P_TOKEN&zone=$zone&zoneTransfer=Allow" || true done < /tmp/primary_zones.txt
done < /tmp/zones.txt
# Configure each replica # Sync to each replica
for REPLICA in $REPLICAS; do SYNCED=0
echo "=== Configuring replica: $REPLICA ===" for REPLICA in $REPLICAS; do
until curl -sf "$REPLICA/api/user/login?user=$TECH_USER&pass=$TECH_PASS" -o /tmp/r.json; do echo "Waiting for $REPLICA..."; sleep 5; done R_TOKEN=$(curl -sf "$REPLICA/api/user/login?user=$TECH_USER&pass=$TECH_PASS" | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
R_TOKEN=$(cat /tmp/r.json | sed -n 's/.*"token":"\([^"]*\)".*/\1/p') if [ -z "$R_TOKEN" ]; then echo "WARN: Cannot login to $REPLICA, skipping"; continue; fi
while read -r zone; do # Get existing zones on this replica
echo "Creating secondary zone: $zone on $REPLICA" curl -sf "$REPLICA/api/zones/list?token=$R_TOKEN" | tr ',' '\n' | sed -n 's/.*"name":"\([^"]*\)".*/\1/p' > /tmp/replica_zones.txt
curl -sf "$REPLICA/api/zones/create?token=$R_TOKEN&zone=$zone&type=Secondary&primaryNameServerAddresses=$PRIMARY_IP" || true
done < /tmp/zones.txt
while read -r zone; do while read -r zone; do
echo "Resyncing: $zone on $REPLICA" if grep -qx "$zone" /tmp/replica_zones.txt; then
curl -sf "$REPLICA/api/zones/resync?token=$R_TOKEN&zone=$zone" || true # Zone exists just resync
done < /tmp/zones.txt curl -sf "$REPLICA/api/zones/resync?token=$R_TOKEN&zone=$zone" > /dev/null || true
done else
# New zone create as Secondary and sync
echo "NEW: Creating $zone on $REPLICA"
curl -sf "$REPLICA/api/zones/create?token=$R_TOKEN&zone=$zone&type=Secondary&primaryNameServerAddresses=$PRIMARY_IP" > /dev/null || true
SYNCED=$((SYNCED + 1))
fi
done < /tmp/primary_zones.txt
done
echo "Replica zone setup complete" echo "Zone sync complete. $$SYNCED new zone(s) created."
SCRIPT SCRIPT
] ]
env { env {
name = "TECH_USER" name = "TECH_USER"
value = var.technitium_username value = var.technitium_username
} }
env { env {
name = "TECH_PASS" name = "TECH_PASS"
value = var.technitium_password value = var.technitium_password
} }
env { env {
name = "PRIMARY_IP" name = "PRIMARY_IP"
value = kubernetes_service.technitium_primary.spec[0].cluster_ip value = kubernetes_service.technitium_primary.spec[0].cluster_ip
} }
} }
dns_config { dns_config {
option { option {
name = "ndots" name = "ndots"
value = "2" value = "2"
}
}
} }
} }
} }
} }
} }
depends_on = [
kubernetes_deployment.technitium,
kubernetes_deployment.technitium_secondary,
kubernetes_deployment.technitium_tertiary,
kubernetes_service.technitium_primary,
kubernetes_service.technitium_secondary_web,
kubernetes_service.technitium_tertiary_web,
]
} }