summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--assets/internal-tls.crt14
-rw-r--r--machines/corsac/default.nix2
-rw-r--r--modules/dns.nix134
-rw-r--r--secrets/internal-tls.age12
-rw-r--r--secrets/secrets.nix4
5 files changed, 146 insertions, 20 deletions
diff --git a/assets/internal-tls.crt b/assets/internal-tls.crt
new file mode 100644
index 0000000..091395a
--- /dev/null
+++ b/assets/internal-tls.crt
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICKzCCAbCgAwIBAgIUYcBKrl7FErRDTmgQxxVty3FCVh4wCgYIKoZIzj0EAwIw
+TDELMAkGA1UEBhMCREUxDzANBgNVBAoMBlJlbmFyZDEQMA4GA1UEAwwHcm5yZC5l
+dTEaMBgGCSqGSIb3DQEJARYLbWVsQHJucmQuZXUwHhcNMjUwMjE5MDIyNTQ1WhcN
+MzUwMjE3MDIyNTQ1WjBMMQswCQYDVQQGEwJERTEPMA0GA1UECgwGUmVuYXJkMRAw
+DgYDVQQDDAdybnJkLmV1MRowGAYJKoZIhvcNAQkBFgttZWxAcm5yZC5ldTB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABNddXJB4zyixrOCRBotlqGbLTFpIZzUqqoQvAavK
+hSqMfci+vglB/Quz5tm68HL2ibR3j2TT8hR1nSiojLSLljcfyIXxCP+QrE39Er0G
+uV3DCl8qARMwL2RTUzQm0l5JTKNTMFEwHQYDVR0OBBYEFOUCR4AvmCrjXKuxAUGk
+YuG+HNhQMB8GA1UdIwQYMBaAFOUCR4AvmCrjXKuxAUGkYuG+HNhQMA8GA1UdEwEB
+/wQFMAMBAf8wCgYIKoZIzj0EAwIDaQAwZgIxAJbLR1QLVKHF1u92lGV8OxeBiNMJ
+4idfA0Usd9x0OCcDK7G7NqFmgi6Au/Z81LD3qwIxAPOBt6iKW4U2L+dpcKm1N3ek
+MEgCtSv4o9mgprgOZr2HBsZIdmIZuSrkruhsAlqfTQ==
+-----END CERTIFICATE-----
diff --git a/machines/corsac/default.nix b/machines/corsac/default.nix
index 66c898c..fde09f6 100644
--- a/machines/corsac/default.nix
+++ b/machines/corsac/default.nix
@@ -19,7 +19,7 @@
 
   foundation.monitoring = {
     client.enable = true;
-    services = [ "base" "jellyfin" "immich" ];
+    services = [ "base" "dns" "jellyfin" "immich" ];
   };
 
   system.stateVersion = "24.05";
diff --git a/modules/dns.nix b/modules/dns.nix
index 235411d..0e17259 100644
--- a/modules/dns.nix
+++ b/modules/dns.nix
@@ -1,42 +1,138 @@
-{ oisd, ... }:
+{ config, oisd, ... }:
 
 let
+  inherit (config.age) secrets;
+
   tailscaleDns = [ "/serval-moth.ts.net/100.100.100.100" ];
 
   cloudflareServers = [
-    "1.1.1.1" "1.0.0.1"
-    "2606:4700:4700::1111" "2606:4700:4700::1001"
+    "1.1.1.1"
+    "1.0.0.1"
+    "2606:4700:4700::1111"
+    "2606:4700:4700::1001"
   ];
 
   quad9Servers = [
-    "9.9.9.9" "149.112.112.112"
-    "2620:fe::fe" "2620:fe::9"
+    "9.9.9.9"
+    "149.112.112.112"
+    "2620:fe::fe"
+    "2620:fe::9"
   ];
 
   upstreamServers = cloudflareServers ++ quad9Servers ++ tailscaleDns;
+
+  dnsProxyPort = 4157;
+  dohInternalPort = 4158;
+  dotInternalPort = 4159;
+
+  # well-known
+  dnsPort = 53;
+  dotPort = 853;
 in
 {
+  networking.firewall = {
+    allowedTCPPorts = [
+      dnsPort
+      dotPort
+    ];
+    allowedUDPPorts = [ dnsPort ];
+  };
   services.resolved.enable = false;
 
-  services.dnsmasq = {
-    enable = true;
+  age.secrets.internal-tls = {
+    file = ../secrets/internal-tls.age;
+  };
+
+  services = {
+    dnsmasq = {
+      enable = true;
+
+      # Ref: https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
+      settings = {
+        interface = [
+          "enp1s0"
+          "tailscale0"
+        ];
+        bind-dynamic = true;
+
+        server = upstreamServers;
+        cache-size = 4096;
+
+        no-resolv = true;
+        bogus-priv = true;
+        domain-needed = true;
+        localise-queries = true;
 
-    # Ref: https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
-    settings = {
-      interface = [ "enp1s0" "tailscale0" ];
-      bind-dynamic = true;
+        conf-file = "${oisd}/dnsmasq2_big.txt";
 
-      server = upstreamServers;
-      cache-size = 4096;
+        log-queries = "extra";
+      };
+    };
 
-      no-resolv = true;
-      bogus-priv = true;
-      domain-needed = true;
-      localise-queries = true;
+    dnsproxy = {
+      enable = true;
+      settings = {
+        listen-addrs = [ "127.0.0.1" ];
+        listen-ports = [ dnsProxyPort ]; # just so that it doesn't bind to 53
+        upstream = [ "127.0.0.1:53" ];
+        cache = false;
 
-      conf-file = "${oisd}/dnsmasq2_big.txt";
+        # NOTE: DoH only supports DNS Wireformat so far.
+        # JSON support has been requested.
+        # see issue: https://github.com/AdguardTeam/dnsproxy/issues/422
+        # NOTE(2): also, there's an open PR to avoid dealing with these
+        # certificates and expose dnsproxy through HTTP, to later
+        # reverse proxy Nginx to it, like I'm doing here.
+        # see: https://github.com/AdguardTeam/dnsproxy/pull/302
+        https-port = [ dohInternalPort ];
+        tls-port = [ dotInternalPort ];
 
-      log-queries = "extra";
+        tls-crt = ../assets/internal-tls.crt;
+      };
+      flags = [ "--tls-key=\${CREDENTIALS_DIRECTORY}/internal-tls" ];
     };
+
+    nginx = {
+      # DNS-over-HTTPS
+      virtualHosts."dns.rnrd.eu" = {
+        useACMEHost = "rnrd.eu";
+        forceSSL = true;
+
+        locations."/" = {
+          proxyPass = "https://127.0.0.1:${toString dohInternalPort}";
+          extraConfig = ''
+            proxy_ssl_trusted_certificate ${../assets/internal-tls.crt};
+            proxy_ssl_verify off;
+          '';
+        };
+
+        extraConfig = ''
+          access_log /var/log/nginx/dns.access.log json_combined;
+        '';
+      };
+
+      # DNS-over-TLS
+      streamConfig =
+        let
+          rnrdCertPath = config.security.acme.certs."rnrd.eu".directory;
+        in
+        ''
+          server {
+            listen [::]:${toString dotPort} ssl ipv6only=off;
+            ssl_certificate ${rnrdCertPath}/fullchain.pem;
+            ssl_certificate_key ${rnrdCertPath}/key.pem;
+            ssl_trusted_certificate ${rnrdCertPath}/chain.pem;
+
+            proxy_pass 127.0.0.1:${toString dotInternalPort};
+            proxy_ssl on;
+            proxy_ssl_trusted_certificate ${../assets/internal-tls.crt};
+            proxy_ssl_verify off;
+          }
+        '';
+    };
+  };
+
+  systemd.services.dnsproxy.serviceConfig = {
+    LoadCredential = "internal-tls:${secrets.internal-tls.path}";
   };
 }
diff --git a/secrets/internal-tls.age b/secrets/internal-tls.age
new file mode 100644
index 0000000..9739177
--- /dev/null
+++ b/secrets/internal-tls.age
@@ -0,0 +1,12 @@
+age-encryption.org/v1
+-> ssh-ed25519 QWV68w Zg5WaS3I4moNEdTVL4P2vOP3x167qcglRtcI+alpgik
+juZiGCS7mt69uYw+FPu48MJg6gPcUvkpIP8u75n7nFc
+-> ssh-ed25519 ztr2Fw gbBZeff8Le8yZjLK7FhRtXoSse/xLt3jXOv3EpOp7Hw
+yoq4YvmmLdXfkjKqcNB4RCFe09pFCYzhdAF1CH+hhGk
+-> ssh-ed25519 COVM9Q 3qjc1AURNkmokmwA1w4w65zoPb0HXPxMzq9qRWBd0Gk
+QmABFr6ETCE+nVgKQnFMdHaHj9x7iKdjc76V6hNI3LM
+-> ssh-ed25519 aV3pTQ MesVv49L93aUOkq29Dn8x8Qet2bJAtwJNdNgsWJdcXg
+qMy/C1oWQEt/xfwi0qqFiW5zOzTHtSF3Dtw8vcjjQBo
+--- oYdUWwxwhjtaCRtaSmcw0CfDcGpsBoFQ3gEPFinp+kU
+hr~ϲٻҫcwRFn0-ؔ6ClB5޹c
&<-}PҟXz5Xq, r9^06yO;IJ\HPVrw`r%ϱ[Ltpݚݥ5HuOkd}:+e#!jPUC\W96pp0}iK2,]jP^
+1yY,ܦi߬Ԧ&±s̮2HMYٱJ{P
{(6J@ipbR@zF-&]T΋
\ No newline at end of file
diff --git a/secrets/secrets.nix b/secrets/secrets.nix
index 1b768d4..704213e 100644
--- a/secrets/secrets.nix
+++ b/secrets/secrets.nix
@@ -23,5 +23,9 @@ in
     corsac
   ] ++ allAdmins;
 
+  "internal-tls.age".publicKeys = [
+    corsac
+  ] ++ allAdmins;
+
   "password.age".publicKeys = allSystems ++ allAdmins;
 }