From 89561c777931bd4d71a268893d17b3c601623513 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 30 May 2026 16:54:09 +0000 Subject: [PATCH] technitium: complete Traefik .200->.203 migration for the .lan zone [ci skip] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today's Traefik dedicated-IP migration (.200 -> .203, ETP=Local) updated the viktorbarzin.me zone but missed the viktorbarzin.lan zone + two stale .200 literals — breaking every *.viktorbarzin.lan ingress host (internal exporters + ~15 HA-Sofia sensors via idrac-redfish/nvidia/snmp) and tripping the apex-drift probe. Found via /cluster-health (23 alerts -> 7). - apex-probe EXPECTED .200 -> .203 (apex IS .203; probe asserted the wrong value -> false ViktorBarzinApexDrift "critical"). - split-horizon externalToInternalTranslation .200 -> .203 (sofia-lan hairpin-NAT target). - ingress-dns-sync CronJob now also pins ingress.viktorbarzin.lan A to the LIVE Traefik LB IP (queried from svc/traefik) every run, so a future Traefik IP move can't silently break the .lan zone again. Added services get/list to its ClusterRole. Applied via targeted apply (4 resources, 0 destroyed) + manual CronJob triggers; verified apex correct=1 and the .lan anchor self-pins to .203. [ci skip] because a full technitium apply would also pick up unrelated pre-existing deployment drift (DNS pod restart risk) — left untouched. Co-Authored-By: Claude Opus 4.7 --- stacks/technitium/modules/technitium/main.tf | 35 +++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/stacks/technitium/modules/technitium/main.tf b/stacks/technitium/modules/technitium/main.tf index 2039547f..93e30ef8 100644 --- a/stacks/technitium/modules/technitium/main.tf +++ b/stacks/technitium/modules/technitium/main.tf @@ -559,10 +559,10 @@ resource "kubernetes_cron_job_v1" "technitium_password_sync" { } # CronJob to configure Split Horizon AddressTranslation on all Technitium instances. -# Translates 176.12.22.76 (public IP) → 10.0.20.200 (Traefik LB) in DNS responses +# Translates 176.12.22.76 (public IP) → 10.0.20.203 (Traefik LB) in DNS responses # for 192.168.1.x clients, fixing hairpin NAT on the TP-Link router. # Also configures DNS Rebinding Protection to allow viktorbarzin.me to return private IPs -# (otherwise the translated 10.0.20.200 gets stripped as a rebinding attack). +# (otherwise the translated 10.0.20.203 gets stripped as a rebinding attack). resource "kubernetes_cron_job_v1" "technitium_split_horizon_sync" { metadata { name = "technitium-split-horizon-sync" @@ -600,7 +600,7 @@ resource "kubernetes_cron_job_v1" "technitium_split_horizon_sync" { } command = ["/bin/sh", "-c", <<-EOT set -e - SPLIT_CONFIG='{"networks":{},"enableAddressTranslation":true,"domainGroupMap":{},"networkGroupMap":{"192.168.1.0/24":"sofia-lan"},"groups":[{"name":"sofia-lan","enabled":true,"translateReverseLookups":false,"externalToInternalTranslation":{"176.12.22.76":"10.0.20.200"}}]}' + SPLIT_CONFIG='{"networks":{},"enableAddressTranslation":true,"domainGroupMap":{},"networkGroupMap":{"192.168.1.0/24":"sofia-lan"},"groups":[{"name":"sofia-lan","enabled":true,"translateReverseLookups":false,"externalToInternalTranslation":{"176.12.22.76":"10.0.20.203"}}]}' REBINDING_CONFIG='{"enableProtection":true,"bypassNetworks":[],"privateNetworks":["10.0.0.0/8","127.0.0.0/8","172.16.0.0/12","192.168.0.0/16","169.254.0.0/16","fc00::/7","fe80::/10"],"privateDomains":["home.arpa","viktorbarzin.me"]}' SPLIT_URL="https://download.technitium.com/dns/apps/SplitHorizonApp-v10.zip" REBINDING_URL="https://download.technitium.com/dns/apps/DnsRebindingProtectionApp-v4.zip" @@ -737,7 +737,7 @@ resource "kubernetes_cron_job_v1" "viktorbarzin_apex_probe" { pip install --quiet --disable-pip-version-check dnspython requests && python3 -c ' import dns.resolver, requests, time, sys -EXPECTED = {"10.0.20.200"} +EXPECTED = {"10.0.20.203"} NAMESERVER = "10.0.20.201" # Technitium LB IP NAME = "viktorbarzin.me" PUSHGATEWAY = "http://prometheus-prometheus-pushgateway.monitoring:9091/metrics/job/viktorbarzin-apex-probe" @@ -817,6 +817,13 @@ resource "kubernetes_cluster_role" "ingress_dns_sync" { resources = ["ingresses"] verbs = ["list"] } + # Read the Traefik LoadBalancer Service so the sync can pin the + # ingress.viktorbarzin.lan anchor to the live Traefik LB IP (see CronJob). + rule { + api_groups = [""] + resources = ["services"] + verbs = ["get", "list"] + } } resource "kubernetes_cluster_role_binding" "ingress_dns_sync" { @@ -903,6 +910,26 @@ resource "kubernetes_cron_job_v1" "technitium_ingress_dns_sync" { fi done echo "Sync complete. Created $$CREATED new records." + + # Pin the .lan ingress anchor A record to the LIVE Traefik LB IP. + # *.viktorbarzin.lan ingress hosts CNAME to ingress.viktorbarzin.lan, + # so a Traefik LB IP move that misses the .lan zone silently breaks + # every internal exporter + HA-sourced sensor (regression 2026-05-30: + # .200 -> .203 migration updated .me but not .lan). This keeps the + # anchor self-correcting on future IP moves. + TRAEFIK_IP=$$(kubectl get svc traefik -n traefik -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null) + if [ -n "$$TRAEFIK_IP" ]; then + LAN_CUR=$$(curl -sf "$$TECH_API/api/zones/records/get?token=$$TOKEN&zone=viktorbarzin.lan&domain=ingress.viktorbarzin.lan&listZone=false" | grep -o '"ipAddress":"[^"]*"' | head -1 | cut -d'"' -f4) + if [ -z "$$LAN_CUR" ]; then + curl -sf "$$TECH_API/api/zones/records/add?token=$$TOKEN&zone=viktorbarzin.lan&domain=ingress.viktorbarzin.lan&type=A&ipAddress=$$TRAEFIK_IP&ttl=300" >/dev/null && echo "Added ingress.viktorbarzin.lan A -> $$TRAEFIK_IP" + elif [ "$$LAN_CUR" != "$$TRAEFIK_IP" ]; then + curl -sf "$$TECH_API/api/zones/records/update?token=$$TOKEN&zone=viktorbarzin.lan&domain=ingress.viktorbarzin.lan&type=A&ipAddress=$$LAN_CUR&newIpAddress=$$TRAEFIK_IP&ttl=300" >/dev/null && echo "Updated ingress.viktorbarzin.lan A: $$LAN_CUR -> $$TRAEFIK_IP" + else + echo "ingress.viktorbarzin.lan A already $$TRAEFIK_IP" + fi + else + echo "WARN: could not resolve Traefik LB IP; skipping .lan anchor sync" + fi EOT ] }