{ config, host, ... }: let inherit (config.services) unbound; inherit (config.users) users; in { networking.resolvconf.useLocalResolver = true; services.unbound = { enable = true; # DOC: https://calomel.org/unbound_dns.html settings = { remote-control = { control-enable = true; control-interface = "/run/unbound/unbound.socket"; }; server = { log-queries = false; verbosity = 1; interface = [ "127.0.0.1" "::1" ]; access-control = [ "0.0.0.0/0 refuse" "::0/0 refuse" "127.0.0.0/8 allow" "::1 allow" ]; prefer-ip4 = !config.networking.enableIPv6; port = 53; # The file which contains the listing of primary root DNS servers. # To be updated once every six months. root-hints = "/var/lib/unbound/named.root"; # Do no answer id.server and hostname.bind queries. hide-identity = true; # Do not answer version.server and version.bind queries. hide-version = true; # Will trust glue only if it is within the servers authority. # Harden against out of zone rrsets, to avoid spoofing attempts. # Hardening queries multiple name servers for the same data to make # spoofing significantly harder and does not mandate dnssec. harden-glue = true; # Require DNSSEC data for trust-anchored zones, if such data is absent, the # zone becomes bogus. Harden against receiving dnssec-stripped data. If you # turn it off, failing to validate dnskey data for a trustanchor will trigger # insecure mode for that zone (like without a trustanchor). Default on, # which insists on dnssec data for trust-anchored zones. harden-dnssec-stripped = true; # Use 0x20-encoded random bits in the query to foil spoof attempts. # http://tools.ietf.org/html/draft-vixie-dnsext-dns0x20-00 # # When Unbound sends a query to a remote server it sends the hostname # string in random upper and lower characters. The remote server must # resolve the hostname as if all the characters were lower case. The remote # server must then send the query back to Unbound in the same random upper # and lower characters that Unbound sent. If the characters of the hostname # in the response are in the same format as the query then the dns-0x20 # check is satisfied. # Attackers hoping to poison a Unbound DNS cache must therefore guess the # mixed-case encoding of the query and the timing of the return dns answer # in addition to all other fields required in a DNS poisoning attack. # dns-0x20 increases the difficulty of the attack significantly. # # It may result in maybe 0.4% of domains getting no answers # due to no support on the authoritative server side use-caps-for-id = true; #cache-min-ttl = 3600; cache-max-ttl = 86400; # Perform prefetching of close to expired message cache entries. If a client # requests the dns lookup and the TTL of the cached hostname is going to # expire in less than 10% of its TTL, unbound will (1st) return the IP of the # host to the client and (2nd) pre-fetch the DNS request from the remote DNS server. # This method has been shown to increase the amount of cached hits by # local clients by 10% on average. prefetch = true; # Number of threads to create. 1 disables threading. # This should equal the number of CPU cores in the host. num-threads = host.CPUs; # The number of slabs to use for cache and must be a power of 2 times the # number of num-threads set above. more slabs reduce lock contention, # but fragment memory usage. msg-cache-slabs = 8; rrset-cache-slabs = 8; infra-cache-slabs = 8; key-cache-slabs = 8; # Increase the memory size of the cache. Use roughly twice as much rrset cache # memory as you use msg cache memory. Due to malloc overhead, the total memory # usage is likely to rise to double (or 2.5x) the total cache memory. rrset-cache-size = "32m"; msg-cache-size = "16m"; # buffer size for UDP port 53 incoming (SO_RCVBUF socket option). This sets # the kernel buffer larger so that no messages are lost in spikes in the traffic. so-rcvbuf = "1m"; # Enforce privacy of these addresses. Strips them away from answers. # It may cause DNSSEC validation to additionally mark it as bogus. # Protects against 'DNS Rebinding' (uses browser as network proxy). # Only 'private-domain' and 'local-data' names are allowed # to have these private addresses. No default. private-address = [ "192.168.0.0/16" "172.16.0.0/12" "10.0.0.0/8" ]; # Allow the domain (and its subdomains) to contain private addresses. # local-data statements are allowed to contain private addresses too. #private-domain = "home.lan"; # If nonzero, unwanted replies are not only reported in statistics, but also # a running total is kept per thread. If it reaches the threshold, a warning # is printed and a defensive action is taken, the cache is cleared to flush # potential poison out of it. A suggested value is 10000000, the default is # 0 (turned off). calomel.org thinks 10K is a good value. unwanted-reply-threshold = 10000; # IMPORTANT FOR TESTING: If you are testing and setup NSD or BIND on # localhost you will want to allow the resolver to send queries to localhost. # Make sure to set do-not-query-localhost = true;. do-not-query-localhost = true; # Should additional section of secure message also be kept clean of unsecure # data. Useful to shield the users of this validator from potential bogus # data in the additional section. All unsigned data in the additional section # is removed from secure messages. val-clean-additional = true; }; }; }; networking.nftables.ruleset = '' table inet filter { chain output-net { meta l4proto { udp, tcp } \ th dport domain \ skuid ${users.unbound.name} \ counter accept comment "unbound" } } ''; systemd.services.unbound = { serviceConfig = { RuntimeDirectory = "unbound"; RuntimeDirectoryMode = "0700"; }; preStart = '' install -m 444 \ ${../../networking/named.root} \ /var/lib/unbound/named.root ''; }; }