summary refs log tree commit diff
path: root/modules
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-02-21 00:04:39 +0100
committerMel <mel@rnrd.eu>2025-02-21 00:04:39 +0100
commit5a8161e086232c5d2262fc53a005464d16006eb5 (patch)
treedfbf36285f54b81a9c29f6e663edc48f179ff25d /modules
parent5db8f66792fd9a0d73eda57b188dd0e0074281b9 (diff)
downloadnetwork-5a8161e086232c5d2262fc53a005464d16006eb5.tar.zst
network-5a8161e086232c5d2262fc53a005464d16006eb5.zip
Expose DNS server with additional DoH/DoT capabilities
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'modules')
-rw-r--r--modules/dns.nix134
1 files changed, 115 insertions, 19 deletions
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}";
   };
 }