{ config, lib, ... }: let inherit (builtins) listToAttrs; inherit (lib) nameValuePair mkForce; inherit (config.sops) secrets; inherit (config.networking) domain; splitDNS = listToAttrs ( map (x: nameValuePair x "127.0.0.1:5359") [ "${domain}." ] ); in { services.resolved.enable = mkForce false; sops.secrets = { "powerdns-admin/secret" = { mode = "0660"; owner = "powerdnsadmin"; group = "powerdnsadmin"; }; "powerdns-admin/salt" = { mode = "0660"; owner = "powerdnsadmin"; group = "powerdnsadmin"; }; powerdns = { mode = "0660"; owner = "pdns"; group = "pdns"; }; }; services.postgresql = { enable = true; authentication = '' host powerdnsadmin powerdnsadmin 127.0.0.1/32 trust ''; ensureUsers = [ { name = "powerdnsadmin"; ensureDBOwnership = true; } { name = "pdns"; ensureDBOwnership = true; } ]; ensureDatabases = [ "powerdnsadmin" "pdns" ]; }; services.powerdns = { enable = true; extraConfig = '' launch=gpgsql loglevel=6 webserver-password=$WEB_PASSWORD api=yes api-key=$WEB_PASSWORD gpgsql-host=/var/run/postgresql gpgsql-dbname=pdns gpgsql-user=pdns gpgsql-dnssec=yes webserver=yes webserver-port=8081 local-port=5359 dnsupdate=yes primary=yes secondary=no allow-dnsupdate-from=10.0.0.0/24 allow-axfr-ips=10.0.0.0/24 also-notify=10.0.0.148:53 ''; secretFile = secrets.powerdns.path; }; services.pdns-recursor = { enable = true; forwardZones = { "dn." = "127.0.0.1:5359"; } // splitDNS; forwardZonesRecurse = { # ==== Rspamd DNS ==== # "multi.uribl.com." = "168.95.1.1"; "score.senderscore.com." = "168.95.1.1"; "list.dnswl.org." = "168.95.1.1"; "dwl.dnswl.org." = "168.95.1.1"; # ==== Others ==== # "tw." = "168.95.1.1"; "." = "1.1.1.1"; }; dnssecValidation = "off"; dns.allowFrom = [ "127.0.0.0/8" "10.0.0.0/24" "192.168.100.0/24" ]; dns.port = 5300; settings = { webservice.webserver = true; recordcache.max_negative_ttl = 60; }; }; services.dnsdist = { enable = true; extraConfig = '' newServer("127.0.0.1:${toString config.services.pdns-recursor.dns.port}") addDOHLocal("0.0.0.0:8053", nil, nil, "/", { reusePort = true }) getPool(""):setCache(newPacketCache(65535, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false})) ''; }; services.powerdns-admin = { enable = true; secretKeyFile = config.sops.secrets."powerdns-admin/secret".path; saltFile = config.sops.secrets."powerdns-admin/salt".path; config = # python '' import cachelib BIND_ADDRESS = "127.0.0.1" PORT = 8081 SESSION_TYPE = 'cachelib' SESSION_CACHELIB = cachelib.simple.SimpleCache() SQLALCHEMY_DATABASE_URI = 'postgresql://powerdnsadmin@/powerdnsadmin?host=localhost' ''; }; services.nginx.virtualHosts = { "dns.${domain}" = { useACMEHost = domain; forceSSL = true; locations."/dns-query" = { extraConfig = '' grpc_pass grpc://127.0.0.1:${toString 8053}; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Protocol $scheme; proxy_set_header Range $http_range; proxy_set_header If-Range $http_if_range; ''; }; }; "powerdns.${domain}" = { useACMEHost = domain; forceSSL = true; locations."/api".proxyPass = "http://127.0.0.1:8081"; locations."/".proxyPass = "http://127.0.0.1:8000"; }; }; systemd.services.pdns-recursor.before = [ "acme-setup.service" ]; systemd.services.pdns.before = [ "acme-setup.service" ]; }