{ config, inputs, lib, this, ... }: with lib; let cfg = config.nixfiles.modules.nsd; in { options.nixfiles.modules.nsd = { enable = mkEnableOption "NSD"; fqdn = mkOption { description = "FQDN of this nameserver."; type = with types; str; default = "ns.${config.networking.domain}"; }; }; config = mkIf cfg.enable { nixfiles.modules.nginx = let domain = my.domain.shire; in { enable = true; virtualHosts = mapAttrs' ( _: v: nameValuePair "mta-sts.${v}" { locations."= /.well-known/mta-sts.txt" = { extraConfig = '' add_header default_type text/plain; ''; return = "200 '${ concatStringsSep "\\r\\n" [ "version: STSv1" "mode: enforce" "max_age: 2419200" "mx: ${domain}" ] }'"; }; } ) my.domain; }; services = { nsd = { enable = true; interfaces = with this; [ ipv4.address ipv6.address ]; ipTransparent = true; ratelimit.enable = true; zones = with dns.combinators; let ips = hostname: with my.configurations.${hostname}; { A = [ (a ipv4.address) ]; AAAA = [ (aaaa ipv6.address) ]; }; mkEmailEntries = { domain ? my.domain.shire, dkimKey ? null, }: { MX = [ (mx.mx 10 "${my.domain.shire}.") ]; TXT = [ (spf.soft [ "a" ]) ]; DMARC = [ { p = "quarantine"; sp = "quarantine"; rua = [ "mailto:admin+rua@${domain}" ]; ruf = [ "mailto:admin+ruf@${domain}" ]; } ]; DKIM = optional (dkimKey != null) { selector = "mail"; p = dkimKey; }; subdomains._mta-sts.TXT = [ "v=STSv1; id=20230506134541Z" ]; }; mkZone = { domain, sldIps ? (ips "manwe"), extra ? { }, }: { ${domain}.data = dns.toString domain (mkMerge [ { TTL = 60 * 60; SOA = { nameServer = "${cfg.fqdn}."; adminEmail = "admin+dns@${my.domain.shire}"; serial = 2022091601; # Don't forget to bump the revision! }; NS = with my.domain; [ "ns1.${shire}" # "ns2.${shire}" ]; CAA = letsEncrypt "admin+caa@${my.domain.shire}"; } sldIps extra ]); }; # https://ariadne.id/ # https://docs.keyoxide.org/service-providers/dns/ ariadneIdProof.TXT = [ "openpgp4fpr:${my.pgp.fingerprint}" ]; in mkMerge [ (mkZone rec { domain = my.domain.shire; extra = mkMerge [ (mkEmailEntries { inherit domain; dkimKey = "@DKIM_KEY@"; }) { subdomains = rec { manwe = ips "manwe"; "*.manwe" = manwe; varda = ips "varda"; "*.varda" = varda; yavanna = ips "yavanna"; "*.yavanna" = yavanna; mta-sts = manwe; ns1 = manwe; # ns2 = varda; alertmanager = manwe; bitwarden = manwe; git = manwe; grafana = manwe; loki = manwe; ntfy = manwe; plausible = manwe; prometheus = manwe; radicale = manwe; rss-bridge = manwe; vaultwarden = manwe; flood = yavanna; jackett = yavanna; lidarr = yavanna; }; } ]; }) (mkZone rec { domain = my.domain.azahi; extra = mkMerge [ (mkEmailEntries { inherit domain; dkimKey = "@DKIM_KEY@"; }) ariadneIdProof { subdomains = { mta-sts = ips "manwe"; git = ips "manwe"; }; } ]; }) (mkZone rec { domain = my.domain.gondor; extra = mkMerge [ (mkEmailEntries { inherit domain; dkimKey = "@DKIM_KEY@"; }) { subdomains = { mta-sts = ips "manwe"; frodo = ips "manwe" // ariadneIdProof; }; } ]; }) (mkZone rec { domain = my.domain.rohan; extra = mkMerge [ (mkEmailEntries { inherit domain; dkimKey = "@DKIM_KEY@"; }) { subdomains = { mta-sts = ips "manwe"; frodo = ips "manwe" // ariadneIdProof; }; } ]; }) ]; }; fail2ban.jails.nsd.enabled = true; }; networking.firewall = rec { allowedTCPPorts = [ 53 ]; allowedUDPPorts = allowedTCPPorts; }; topology = with cfg; { nodes.${this.hostname}.services.nsd = { name = "NSD"; icon = "${inputs.homelab-svg-assets}/assets/unbound.svg"; details.listen.text = concatMapStringsSep "\n" (i: "${i}:53") ( filter (i: i != "127.0.0.1" && i != "::1") config.services.nsd.interfaces ); }; }; }; }