From 087c5a79f496c97ba202e87ab3a1d5d0632979f3 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Tue, 24 Mar 2026 09:27:21 -0500 Subject: [PATCH] Add native DNS-over-TLS (DoT) on dot.nook.family:853 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ddclient.tf: dynamic DNS daemon that keeps dot.nook.family A record pointing at the home public IP via Cloudflare API (updates every 5m) - adguard.tf: cert-manager Certificate for dot.nook.family via DNS-01, updated TLS config block with DoT paths and port 853 - apps/adguard.yaml: mount adguard-dot-tls secret, expose port 853, init container enforces TLS/DoT settings on every restart - adguard.tf: fix stale neuralplatform.dev → neuralplatform.ai rewrite Android Private DNS: set to dot.nook.family Router: forward TCP 853 → 192.168.68.77 --- servers/legion/adguard.tf | 31 +++++++++++- servers/legion/apps/adguard.yaml | 23 ++++++++- servers/legion/ddclient.tf | 81 ++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 servers/legion/ddclient.tf diff --git a/servers/legion/adguard.tf b/servers/legion/adguard.tf index c1ddb0d..6bf9c09 100644 --- a/servers/legion/adguard.tf +++ b/servers/legion/adguard.tf @@ -1,4 +1,4 @@ -# AdGuard Home — household DNS with ad blocking, local resolution, and DoH via dns.nook.family +# AdGuard Home — household DNS with ad blocking, local resolution, DoH via dns.nook.family, and DoT via dot.nook.family:853 resource "kubernetes_namespace" "dns" { metadata { @@ -89,6 +89,11 @@ resource "kubernetes_config_map" "adguard_defaults" { name: EasyPrivacy id: 5 tls: + enabled: true + server_name: dot.nook.family + port_dns_over_tls: 853 + certificate_path: /etc/adguard/tls/tls.crt + private_key_path: /etc/adguard/tls/tls.key allow_unencrypted_doh: true filtering: filtering_enabled: true @@ -96,13 +101,35 @@ resource "kubernetes_config_map" "adguard_defaults" { - domain: '*.nook.family' answer: ${var.legion_ip} enabled: true - - domain: '*.neuralplatform.dev' + - domain: '*.neuralplatform.ai' answer: ${var.legion_ip} enabled: true EOT } } +# TLS certificate for DoT — issued via cert-manager DNS-01 (Cloudflare) +resource "kubernetes_manifest" "adguard_dot_cert" { + manifest = { + apiVersion = "cert-manager.io/v1" + kind = "Certificate" + metadata = { + name = "adguard-dot-tls" + namespace = kubernetes_namespace.dns.metadata[0].name + } + spec = { + secretName = "adguard-dot-tls" + issuerRef = { + name = "letsencrypt-prod" + kind = "ClusterIssuer" + } + dnsNames = ["dot.nook.family"] + } + } + + depends_on = [kubernetes_manifest.cluster_issuer_prod] +} + # Deployment + Service managed by Argo CD — see servers/legion/apps/adguard.yaml resource "kubernetes_ingress_v1" "adguard" { diff --git a/servers/legion/apps/adguard.yaml b/servers/legion/apps/adguard.yaml index e5c4e82..0f834bb 100644 --- a/servers/legion/apps/adguard.yaml +++ b/servers/legion/apps/adguard.yaml @@ -57,8 +57,15 @@ spec: else: cfg.setdefault("filters", []).append(df) - # Enforce allow_unencrypted_doh - cfg.setdefault("tls", {})["allow_unencrypted_doh"] = True + # Enforce TLS / DoT settings + cfg.setdefault("tls", {}).update({ + "enabled": True, + "server_name": "dot.nook.family", + "port_dns_over_tls": 853, + "certificate_path": "/etc/adguard/tls/tls.crt", + "private_key_path": "/etc/adguard/tls/tls.key", + "allow_unencrypted_doh": True, + }) # Fix/ensure rewrites cfg.setdefault("filtering", {}) @@ -77,6 +84,9 @@ spec: mountPath: /opt/adguardhome/conf - name: defaults mountPath: /opt/adguard-defaults + - name: tls + mountPath: /etc/adguard/tls + readOnly: true containers: - name: adguard image: adguard/adguardhome:latest @@ -87,6 +97,9 @@ spec: - name: dns-udp containerPort: 53 protocol: UDP + - name: dot + containerPort: 853 + protocol: TCP - name: http containerPort: 3000 protocol: TCP @@ -95,6 +108,9 @@ spec: mountPath: /opt/adguardhome/conf - name: data mountPath: /opt/adguardhome/work + - name: tls + mountPath: /etc/adguard/tls + readOnly: true resources: requests: memory: 128Mi @@ -112,6 +128,9 @@ spec: - name: defaults configMap: name: adguard-defaults + - name: tls + secret: + secretName: adguard-dot-tls --- apiVersion: v1 kind: Service diff --git a/servers/legion/ddclient.tf b/servers/legion/ddclient.tf new file mode 100644 index 0000000..25fe480 --- /dev/null +++ b/servers/legion/ddclient.tf @@ -0,0 +1,81 @@ +# ddclient — dynamic DNS daemon that keeps dot.nook.family pointing at the home public IP +# Updates Cloudflare A record every 5 minutes; required for DoT (port 853) which can't use Cloudflare proxy + +resource "kubernetes_secret" "ddclient_config" { + metadata { + name = "ddclient-config" + namespace = kubernetes_namespace.dns.metadata[0].name + } + + data = { + "ddclient.conf" = <<-EOT + daemon=300 + syslog=yes + pid=/var/run/ddclient/ddclient.pid + use=web, web=https://api.ipify.org + + protocol=cloudflare + zone=nook.family + ttl=120 + login=${var.cloudflare_email} + password=${var.cloudflare_api_key} + dot.nook.family + EOT + } +} + +resource "kubernetes_deployment" "ddclient" { + metadata { + name = "ddclient" + namespace = kubernetes_namespace.dns.metadata[0].name + labels = { app = "ddclient" } + } + + spec { + replicas = 1 + selector { + match_labels = { app = "ddclient" } + } + template { + metadata { + labels = { app = "ddclient" } + } + spec { + container { + name = "ddclient" + image = "ghcr.io/linuxserver/ddclient:latest" + + env { + name = "PUID" + value = "1000" + } + env { + name = "PGID" + value = "1000" + } + + volume_mount { + name = "config" + mount_path = "/config/ddclient.conf" + sub_path = "ddclient.conf" + read_only = true + } + + resources { + requests = { memory = "32Mi", cpu = "10m" } + limits = { memory = "64Mi" } + } + } + + volume { + name = "config" + secret { + secret_name = kubernetes_secret.ddclient_config.metadata[0].name + } + } + } + } + } + + depends_on = [kubernetes_namespace.dns] +}