{ cluster, config, depot, lib, pkgs, ... }: let inherit (config.reflection) interfaces; inherit (depot.lib.meta) domain; inherit (config.networking) hostName; link = cluster.config.hostLinks.${hostName}.dnsAuthoritative; patroni = cluster.config.links.patroni-pg-access; inherit (cluster.config.hostLinks.${hostName}) acmeDnsApi; otherDnsServers = lib.pipe (cluster.config.services.dns.otherNodes.authoritative hostName) [ (map (node: cluster.config.hostLinks.${node}.dnsAuthoritative.tuple)) (lib.concatStringsSep " ") ]; recordsList = lib.mapAttrsToList (lib.const lib.id) cluster.config.dns.records; recordsPartitioned = lib.partition (record: record.rewrite.target == null) recordsList; staticRecords = let escape = type: { TXT = builtins.toJSON; }.${type} or lib.id; recordName = record: { "@" = "${record.root}."; }.${record.name} or "${record.name}.${record.root}."; in lib.flatten ( map (record: map (target: "${recordName record} ${record.type} ${escape record.type target}") record.target) recordsPartitioned.right ); rewrites = map (record: let maybeEscapeRegex = str: if record.rewrite.type == "regex" then "${lib.escapeRegex str}$" else str; in "rewrite stop name ${record.rewrite.type} ${record.name}${maybeEscapeRegex ".${record.root}."} ${record.rewrite.target}. answer auto") recordsPartitioned.wrong; rewriteConf = pkgs.writeText "coredns-rewrites.conf" '' rewrite stop type DS DS rewrite stop type NS NS rewrite stop type SOA SOA ${lib.concatStringsSep "\n" rewrites} ''; in { links.localAuthoritativeDNS = {}; age.secrets = { acmeDnsDbCredentials = { file = ./acme-dns-db-credentials.age; }; acmeDnsDirectKey = { file = ./acme-dns-direct-key.age; }; }; networking.firewall = { allowedTCPPorts = [ 53 ]; allowedUDPPorts = [ 53 ]; }; services.acme-dns = { enable = true; package = depot.packages.acme-dns; settings = { general = { listen = config.links.localAuthoritativeDNS.tuple; inherit domain; nsadmin = "hostmaster.${domain}"; nsname = "eu1.ns.${domain}"; records = staticRecords; }; api = { ip = acmeDnsApi.ipv4; inherit (acmeDnsApi) port; }; database = { engine = "postgres"; connection = "postgres://acmedns@${patroni.tuple}/acmedns?sslmode=disable"; }; }; }; systemd.services.acme-dns.serviceConfig.EnvironmentFile = with config.age.secrets; [ acmeDnsDbCredentials.path acmeDnsDirectKey.path ]; services.coredns = { enable = true; config = '' .:${link.portStr} { bind ${interfaces.primary.addr} chaos "Private Void DNS" info@privatevoid.net cache { success 4000 86400 denial 0 prefetch 3 serve_stale 86400s verify } template ANY DS { rcode NXDOMAIN } forward service.eu-central.sd-magic.${domain} 127.0.0.1:8600 forward addr.eu-central.sd-magic.${domain} 127.0.0.1:8600 import ${rewriteConf} forward . ${config.links.localAuthoritativeDNS.tuple} ${otherDnsServers} { policy sequential } } ''; }; systemd.services.coredns = { after = [ "acme-dns.service" ]; serviceConfig = { MemoryMax = "200M"; MemorySwapMax = "50M"; CPUQuota = "25%"; }; }; consul.services = { authoritative-dns = { unit = "acme-dns"; definition = { name = "authoritative-dns-backend"; address = config.links.localAuthoritativeDNS.ipv4; port = config.links.localAuthoritativeDNS.port; checks = lib.singleton { interval = "60s"; tcp = config.links.localAuthoritativeDNS.tuple; }; }; }; acme-dns.definition = { name = "acme-dns"; address = acmeDnsApi.ipv4; port = acmeDnsApi.port; checks = lib.singleton { interval = "60s"; http = "${acmeDnsApi.url}/health"; }; }; }; }