{ 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" ]; quad9Servers = [ "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; 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; conf-file = "${oisd}/dnsmasq2_big.txt"; log-queries = "extra"; }; }; 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; # 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 ]; 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}"; }; }