{"id":5572,"date":"2026-06-09T15:22:39","date_gmt":"2026-06-09T19:22:39","guid":{"rendered":"https:\/\/dft.wiki\/?p=5572"},"modified":"2026-06-17T20:18:05","modified_gmt":"2026-06-18T00:18:05","slug":"resilient-nameserver-infrastructure-the-security","status":"publish","type":"post","link":"https:\/\/dft.wiki\/?p=5572","title":{"rendered":"Resilient Nameserver Infrastructure \u2013 The Security"},"content":{"rendered":"<p>This post is a continuation of posts <strong>Resilient Nameserver Infrastructure \u2013 The Tools<\/strong> [<a href=\"https:\/\/dft.wiki\/?p=5517\">Link<\/a>] and <strong>Resilient Nameserver Infrastructure \u2013 The Architecture<\/strong> [<a href=\"https:\/\/dft.wiki\/?p=5539\">Link<\/a>].<\/p>\n<p>Those posts introduced the tools and foundational knowledge. This post builds on them to cover the security features and considerations that should be taken into account when setting up DNS.<\/p>\n<p>The content of this post is based on practical experience enriched by lessons learned from <strong>NIST SP 800-81r3 &#8211; Secure Domain Name System (DNS) Deployment Guide<\/strong> [<a href=\"https:\/\/nvlpubs.nist.gov\/nistpubs\/SpecialPublications\/NIST.SP.800-81r3.pdf\">Link<\/a>].<\/p>\n<p><strong>Why?<\/strong> Because DNS has shifted from a pure name-resolution utility into a security control and policy enforcement point (PEP).<\/p>\n<p><strong>Scenarios<\/strong><\/p>\n<ul>\n<li><strong>Recursive Resolver<\/strong>\n<ul>\n<li>A public or private\/internal resolver for clients on a network, VPN, or the open internet.\n<ul>\n<li><strong>Note:<\/strong> This post focuses on infrastructure and network services, not on client stub resolvers or applications with built-in DoH capabilities.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<ul>\n<li><strong>Authoritative Nameserver<\/strong>\n<ul>\n<li>Generally public, but could also be a purely internal authoritative nameserver that only resolves its own zones.\n<ul>\n<li><strong style=\"color: #777777; font-size: 1rem;\">Note:<\/strong><span style=\"color: #777777; font-size: 1rem;\"> It is recommended NOT to combine recursive and authoritative roles on any internet-facing server (cache-poisoning surface). NIST even recommends a &#8220;hidden primary&#8221; authoritative server.<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>Core Concepts<\/strong><\/p>\n<ul>\n<li><strong>DNSSEC<\/strong>\n<ul>\n<li>A common misconception is that DNSSEC provides encryption. It does not.<\/li>\n<li>It operates during recursion, between the authoritative server (zone owner) and recursive resolvers.\n<ul>\n<li>SIGNING happens at the authoritative server: the zone owner publishes RRSIG\/DNSKEY records and a DS record at the parent (registrar).<\/li>\n<li>VALIDATION happens at the recursive resolver, which walks the chain of trust at each level.<\/li>\n<\/ul>\n<\/li>\n<li>DNSSEC provides data-origin authentication and integrity.\n<ul>\n<li>It protects against rogue authoritative servers (zone owner impersonation) and resolution tampering, since signatures cannot be forged.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><strong>Encrypted DNS<\/strong>\n<ul>\n<li>Operates on the last mile, between the client and the recursive resolver.<\/li>\n<li>DoT (DNS over TLS)\n<ul>\n<li>A simple socket layer that wraps standard DNS using port 853\/TCP.<\/li>\n<li>Easy to block in organisations that need to control or filter malicious or prohibited domains.<\/li>\n<\/ul>\n<\/li>\n<li>DoH (DNS over HTTPS)\n<ul>\n<li>Traffic goes over port 443\/TCP (plus UDP if HTTP\/3) and is indistinguishable from regular HTTPS traffic.<\/li>\n<li>Difficult for organisations to inspect or filter.<\/li>\n<li>Layer 7 packet inspection is required, with a CA certificate installed on managed devices.<\/li>\n<li>The better choice for circumventing government censorship.<\/li>\n<\/ul>\n<\/li>\n<li>DoQ (DNS over QUIC)\n<ul>\n<li>Faster because it runs over UDP on port 853 using the QUIC protocol.<\/li>\n<li>Technologically advanced, but the newest and least widely supported of the three.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><strong>Time Accuracy<\/strong>\n<ul>\n<li>Cryptographic technologies such as DNSSEC and TLS are time-sensitive because they include expiration and validity periods.<\/li>\n<li>It is essential to ensure nameservers have time synchronization configured and monitored.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Other Facts<\/strong>\n<ul>\n<li>A DNS-aware proxy is recommended but not mandatory.\n<ul>\n<li>A proxy adds a layer of defence to any resolver (recursive or authoritative).<\/li>\n<li>It can offload TLS termination, apply rate-limiting, drop malicious traffic, and load-balance legitimate queries.<\/li>\n<\/ul>\n<\/li>\n<li><span style=\"color: #777777; font-size: 1rem;\">Be aware that DNSSEC increases operational cost and adds CPU overhead on resolvers.<\/span><\/li>\n<li><span style=\"color: #777777; font-size: 1rem;\">Encrypted DNS can be used by attackers for C2 and exfiltration since the channel is hidden, which may force organisations<\/span> to block external encrypted DNS.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>Advanced Concepts<\/strong><\/p>\n<ul>\n<li>Protective DNS (PDNS)\n<ul>\n<li>A set of measures that goes beyond blocking known malicious domains from a deny list.<\/li>\n<li>It identifies lookalikes of common domains and domains that appear to be algorithmically generated.<\/li>\n<li>It also checks the full recursive resolution chain for malicious CNAMEs and similar threats.<\/li>\n<li>It blocks malicious IPs before responding to the client.<\/li>\n<\/ul>\n<\/li>\n<li>DNS Firewalls \/ Response Policy Zones (RPZ)\n<ul>\n<li>Uses threat-intel feeds loaded as a secondary zone internally.<\/li>\n<li>Returns NXDOMAIN (or a sinkhole) instead of the real answer for malicious matches.<\/li>\n<li>When deployed closest to the client, it enables per-client attribution at higher operational cost.<\/li>\n<li>When deployed closest to the internet, it reduces operational cost but loses client-level visibility.<\/li>\n<\/ul>\n<\/li>\n<li>DNS for DFIR (Digital Forensics and Incident Response)\n<ul>\n<li>Log all query\/response data and DHCP lease history.<\/li>\n<li>Forward logs to a SIEM\/XDR\/SOAR for correlation.<\/li>\n<li>Retain malicious-domain hits indefinitely.<\/li>\n<\/ul>\n<\/li>\n<li>ODoH (Oblivious DoH)\n<ul>\n<li>Hides the client&#8217;s IP address when querying for domain resolution:\n<ul>\n<li>A proxy forwards requests and responses in encrypted form, so it cannot see the query content.<\/li>\n<li>The resolver cannot see the client&#8217;s IP because it is hidden behind the proxy.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>QNAME Minimization\n<ul>\n<li>Reduces the amount of metadata disclosed at each step of recursive resolution.<\/li>\n<li>Adds a small extra query cost up front, negligible once the cache warms up.<\/li>\n<\/ul>\n<\/li>\n<li>EDNS Padding\n<ul>\n<li>Appends padding to round up the request size to a fixed block (128 or 256 bytes).<\/li>\n<li>Makes all encrypted DNS queries appear the same size, preventing traffic analysis by length fingerprinting.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<hr \/>\n<p><strong>RECURSIVE<\/strong><\/p>\n<p><strong>WARNING:<\/strong> NIST is explicit (4.3) that recursive resolvers shall only be accessible internally.<\/p>\n<ul>\n<li><strong>ACLs<\/strong> in <strong>DNSDist.<\/strong><\/li>\n<\/ul>\n<pre>sudo nano \/etc\/dnsdist\/dnsdist.conf<\/pre>\n<pre>setACL({'192.168.0.0\/16', '10.0.0.0\/8'})\r\naddACL('200.100.50.25\/32')<\/pre>\n<pre>sudo dnsdist --check-config\r\nsudo systemctl restart dnsdist<\/pre>\n<ul>\n<li><strong>Rate Limiting<\/strong> (runs on every packet) in <strong>DNSDist<\/strong>.<\/li>\n<\/ul>\n<pre>-- Queries Per Second per unique IPv4.\r\naddAction(MaxQPSIPRule(<strong>100<\/strong>), <strong>DropAction<\/strong>())\r\n-- Or, providing the prefix lengths IPv4\/IPv6.\r\naddAction(MaxQPSIPRule(100, <strong>32<\/strong>, <strong>64<\/strong>), <strong>DropAction<\/strong>())\r\n-- Global ceiling to a backend pool.\r\n<span class=\"token token\">addAction(NotRule(MaxQPSRule(<strong>10000<\/strong>)), <strong>DropAction<\/strong>())\r\n-- Alternatively, consider forcing TCP to mitigate spoofed UDP.\r\naddAction(MaxQPSIPRule(<strong>50<\/strong>), <strong>TCAction<\/strong>())<\/span><\/pre>\n<p>OR use <strong>Dynamic Blocking<\/strong> (evaluates rules in a group).<\/p>\n<pre>local dbr = dynBlockRulesGroup()\r\n-- If &gt;100 QPS over 10s, block for 60s\r\ndbr:setQueryRate(<strong>100<\/strong>, <strong>10<\/strong>, \"Exceeded query rate\", <strong>60<\/strong>)\r\n-- More strict treshold based on returned code.\r\ndbr:setRCodeRate(DNSRCode.NXDOMAIN, <strong>20<\/strong>, <strong>10<\/strong>, \"NXDOMAIN flood\", <strong>600<\/strong>)\r\ndbr:setRCodeRate(DNSRCode.SERVFAIL, <strong>20<\/strong>, <strong>10<\/strong>, \"SERVFAIL flood\", <strong>600<\/strong>)\r\ndbr:setQTypeRate(DNSQType.ANY, <strong>2<\/strong>, <strong>10<\/strong>, \"ANY flood\", <strong>600<\/strong>)\r\n-- Mitigates amplification attacks by limiting bytes\/s per client.\r\ndbr:setResponseByteRate(<strong>10000<\/strong>, 10, \"Response bandwidth\", 60)\r\n-- Applies the Rules Group.\r\nfunction maintenance()\r\n  dbr:apply()\r\nend<\/pre>\n<ul>\n<li><strong>Layer 4 Hardening<\/strong> in <strong>DNSDist<\/strong>.<\/li>\n<\/ul>\n<pre>setUDPTimeout(<strong>2<\/strong>)\r\nsetMaxTCPClientThreads(<strong>20<\/strong>)\r\nsetMaxTCPConnectionsPerClient(<strong>20<\/strong>)\r\nsetMaxTCPConnectionDuration(<strong>30<\/strong>)\r\nsetMaxTCPQueriesPerConnection(<strong>50<\/strong>)\r\nsetTCPRecvTimeout(<strong>5<\/strong>)\r\nsetTCPSendTimeout(<strong>5<\/strong>)<\/pre>\n<ul>\n<li><strong>Caching at the Edge<\/strong> with <strong>DNSDist<\/strong>.<\/li>\n<\/ul>\n<pre>-- Keeps exact queries in cache even when backend servers are not reachable.\r\nsetStaleCacheEntriesTTL(<strong>86400<\/strong>)\r\npc = newPacketCache(<strong>100000<\/strong>, {maxTTL=3600, minTTL=10, temporaryFailureTTL=60, staleTTL=60, <strong>keepStaleData=true<\/strong>})\r\ngetPool(\"\"):setCache(pc)<\/pre>\n<ul>\n<li><strong>DNSSEC Validation<\/strong> is enabled by default on <strong>PowerDNS Recursor<\/strong>.<\/li>\n<li>QNAME Minimization can be enabled with a single setting.<\/li>\n<\/ul>\n<pre>sudo nano \/etc\/powerdns\/recursor.conf<\/pre>\n<pre>dnssec:\r\n  # ...\r\n  <strong>validation: process<\/strong>\r\nrecursor:\r\n  # ...\r\n  <strong>qname_minimization: true<\/strong><\/pre>\n<pre>pdns_recursor --config-check\r\nsudo systemctl restart pdns-recursor<\/pre>\n<p><strong>Note: <code>process<\/code><\/strong> (recommended) returns SERVFAIL to the client if signature validation fails. Alternatively, <code>log-fail<\/code> still returns the result to the client while logging a warning. The config style above uses the legacy <strong>key=value<\/strong> syntax in <code>.conf<\/code> files. Since PowerDNS Recursor 5.x, YAML is the preferred syntax. Both are supported, but only one style can be used at a time.<\/p>\n<p>Test it!<\/p>\n<pre>dig @127.0.0.1 isc.org <strong>+dnssec<\/strong>\r\ndig @127.0.0.1 sigfail.bonne.cr <strong>+dnssec<\/strong><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5578\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-41-21-1.png\" alt=\"\" width=\"823\" height=\"463\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-41-21-1.png 823w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-41-21-1-300x169.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-41-21-1-768x432.png 768w\" sizes=\"auto, (max-width: 823px) 100vw, 823px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5577\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-42-01.png\" alt=\"\" width=\"823\" height=\"327\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-42-01.png 823w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-42-01-300x119.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-26_14-42-01-768x305.png 768w\" sizes=\"auto, (max-width: 823px) 100vw, 823px\" \/><\/p>\n<ul>\n<li><strong>DoT<\/strong> between <strong>Client<\/strong> and <strong>DNSDist<\/strong>.\n<ul>\n<li>Valid public <strong>TLS<\/strong> certificate\n<ul>\n<li>In Strict \/ Authenticated mode, the client validates the certificate against the public CA chain.<\/li>\n<\/ul>\n<\/li>\n<li>Self-signed\n<ul>\n<li>Works for Opportunistic \/ Non-authenticated mode.<\/li>\n<li>\n<pre>sudo openssl req -x509 -newkey rsa:2048 -nodes -keyout \/etc\/dnsdist\/key.pem -out \/etc\/dnsdist\/cert.pem -days 365 -subj \"\/CN=dns.home.lab\" -addext \"subjectAltName=DNS:dns.internal,IP:10.154.196.5\"\r\nsudo chown root:_dnsdist \/etc\/dnsdist\/*.pem\r\nsudo chmod 640 \/etc\/dnsdist\/*.pem<\/pre>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre># ...\r\n-- DoT on 853\r\naddTLSLocal(\"0.0.0.0:853\", \"\/etc\/dnsdist\/cert.pem\", \"\/etc\/dnsdist\/key.pem\", { minTLSVersion=\"tls1.2\" })\r\naddTLSLocal(\"[::]:853\", \"\/etc\/dnsdist\/cert.pem\", \"\/etc\/dnsdist\/key.pem\", { minTLSVersion=\"tls1.2\" })\r\n# ...<\/pre>\n<p>Test it!<\/p>\n<pre>dig @127.0.0.1 <strong>-p 853 +tls<\/strong> example.com\r\nopenssl s_client -connect 127.0.0.1:853 <strong>-showcerts<\/strong><\/pre>\n<ul>\n<li><strong>DoH<\/strong> on <strong>DNSDist<\/strong><\/li>\n<\/ul>\n<pre>-- DoH on 443\r\naddDOHLocal(\"0.0.0.0:443\", \"\/etc\/dnsdist\/cert.pem\", \"\/etc\/dnsdist\/key.pem\", \"\/dns-query\", { minTLSVersion=\"tls1.2\" })\r\naddDOHLocal(\"[::]:443\", \"\/etc\/dnsdist\/cert.pem\", \"\/etc\/dnsdist\/key.pem\", \"\/dns-query\", { minTLSVersion=\"tls1.2\" })<\/pre>\n<p>Test it!<\/p>\n<pre>dig google.com @127.0.0.1 +https<\/pre>\n<ul>\n<li><strong>DoQ<\/strong> on <strong>DNSDist<\/strong><\/li>\n<\/ul>\n<pre>-- DoQ on UDP 853\r\naddDOQLocal(\"0.0.0.0:853\", \"\/etc\/dnsdist\/cert.pem\", \"\/etc\/dnsdist\/key.pem\", { reusePort=true })\r\naddDOQLocal(\"[::]:853\", \"\/etc\/dnsdist\/cert.pem\", \"\/etc\/dnsdist\/key.pem\", { reusePort=true })<\/pre>\n<p><code>dig<\/code> does not support QUIC, so test it with:<\/p>\n<pre>sudo apt install knot-dnsutils -y\r\nkdig google.com @127.0.0.1 +quic<\/pre>\n<ul>\n<li><strong>Forwarding TLS<\/strong> to Upstream Resolver with <strong>Recursor<\/strong><\/li>\n<\/ul>\n<pre>recursor:\r\n  # ...\r\n  forward_zones_recurse:\r\n    - zone: '.'\r\n      forwarders:\r\n        - '<strong>9.9.9.9<\/strong>:853'\r\n        - '<strong>1.1.1.1<\/strong>:853'\r\noutgoing:\r\n  dot_to_port_853: true\r\n<\/pre>\n<ul>\n<li>Blocking <strong>Known Malicious<\/strong> Domains with <strong>Recursor.<\/strong><\/li>\n<\/ul>\n<p>Pull any community-maintained list. This is the one used by Pi-hole.<\/p>\n<pre>curl -L https:\/\/raw.githubusercontent.com\/StevenBlack\/hosts\/master\/hosts -o <strong>\/etc\/powerdns\/hosts.blocked<\/strong><\/pre>\n<p>The simple way is to load the <code>hosts<\/code> file directly.<\/p>\n<pre>recursor:\r\n  # ...\r\n  etc_hosts_file: <strong>\/etc\/powerdns\/hosts.blocked<\/strong>\r\n  export_etc_hosts: <strong>true<\/strong><\/pre>\n<p>OR<\/p>\n<p>If the blocklist is a standard <strong>BIND-style Zone<\/strong> file:<\/p>\n<pre><span class=\"token token key\">recursor:\r\n  # ...\r\n  rpzs:\r\n    - name: \/etc\/powerdns\/hosts.blocked.rpz\r\n      addresses: []\r\n      defpol: NXDOMAIN\r\n      policyName: blocklist\r\n      extendedErrorCode: 15\r\n      extendedErrorExtra: 'Blocked by local policy'\r\n<\/span><\/pre>\n<p>Apply changes without a full restart.<\/p>\n<pre>rec_control reload-lua-config<\/pre>\n<ul>\n<li>Subscribe to a <strong>Published RPZ Feed<\/strong> in <strong>Recursor<\/strong>.<\/li>\n<\/ul>\n<p>Verify that the remote RPZ primary zone is reachable and allows AXFR queries.<\/p>\n<pre>dig axfr bad-hosts.blocked @<strong>200.100.50.25<\/strong><\/pre>\n<p>Then configure it to replicate the zone.<\/p>\n<pre>rpzPrimary({\"<strong>200.100.50.25<\/strong>\", \"<strong>2020:1010:a:b::1<\/strong>\"}, \"<strong>malware.host.example<\/strong>\", {\r\n  defpol = Policy.NXDOMAIN,\r\n  refresh = 900,\r\n  axfrTimeout = 180,\r\n  dumpFile  = \"\/var\/spool\/powerdns\/malware.rpz\",\r\n  seedFile  = \"\/var\/spool\/powerdns\/malware.rpz\",\r\n  -- If the feed uses TSIG\r\n  -- tsigname = \"...\", tsigalgo = \"...\", tsigsecret = \"...\"\r\n})<\/pre>\n<ul>\n<li><strong>Other Settings<\/strong> in <strong>DNSDist<\/strong>.<\/li>\n<\/ul>\n<pre>-- Drop queries with no Recursion-Desired flag.\r\naddAction(NotRule(RDRule()), RCodeAction(DNSRCode.REFUSED))\r\n-- Mitigates amplification\/fragmentation by setting a EDNS UDP payload size.\r\nsetPayloadSizeOnSelfGeneratedAnswers(1232)\r\n-- Drops if &gt;5 labels (names separated by dots) and &gt;100 characteres.\r\naddAction(QNameLabelsCountRule(1, <strong>5<\/strong>), DropAction())\r\naddAction(QNameWireLengthRule(1, <strong>100<\/strong>), DropAction())\r\n-- Delay forwarding TXT type if too frequent. Potential tunneling going on.\r\nlocal tunnel_delay = MaxQPSIPRule(<strong>5<\/strong>, 32, 128)\r\naddAction(AndRule({tunnel_delay, QTypeRule(DNSQType.<strong>TXT<\/strong>)}), DelayAction(<strong>1000<\/strong>))<\/pre>\n<hr \/>\n<p><strong>AUTHORITATIVE<\/strong><\/p>\n<ul>\n<li><strong>Hidden Primary<\/strong>\n<ul>\n<li>The primary <strong>must not be listed<\/strong> in the NS RRset; only secondaries are visible (publicly known and reachable).<\/li>\n<li><span style=\"color: #777777; font-size: 1rem;\">See this strategy in the previous post <strong>DNS: HA plus Hidden Primary<\/strong> [<a href=\"https:\/\/dft.wiki\/?p=5870\">Link<\/a>].<\/span>\n<ul>\n<li><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5895\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Hidden-Primary-1.png\" alt=\"\" width=\"467\" height=\"370\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Hidden-Primary-1.png 467w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Hidden-Primary-1-300x238.png 300w\" sizes=\"auto, (max-width: 467px) 100vw, 467px\" \/><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><strong>ACLs<\/strong>\n<ul>\n<li>Restrict <strong>AXFR<\/strong> (Full) and <strong>IXFR<\/strong> (Incremental) zone transfers to known secondaries.<\/li>\n<li>Only accept <strong>NOTIFY<\/strong> (which triggers zone transfers) from the hidden primary.<\/li>\n<li>Only accept <strong>Dynamic Updates<\/strong> from authorised senders (e.g., DHCP, Active Directory).<\/li>\n<\/ul>\n<\/li>\n<li><strong>Confidentiality and Integrity<\/strong>\n<ul>\n<li>Cryptographically authenticate with <strong>TSIG<\/strong> (symmetric key) or <strong>SIG(0)<\/strong> (public key).<\/li>\n<li>Add <strong>ZONEMD<\/strong> as a zone-content digest. Unlike DNSSEC, which signs per record, ZONEMD signs the entire zone.<\/li>\n<li>Use <strong>XoT<\/strong> (zone transfer over TLS) for confidentiality.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Denial of Existence<\/strong>\n<ul>\n<li>Prefer plain <strong>NSEC<\/strong> over NSEC3, as NSEC3 does not truly prevent zone-walking and adds overhead.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Zone Drift<\/strong> \/ <strong>Zone Thrash<\/strong> (SOA RR)\n<ul>\n<li>Refresh and Retry set <strong>too high<\/strong>:\n<ul>\n<li>Zone data drifts because changes <strong>take too long to propagate<\/strong>.<\/li>\n<\/ul>\n<\/li>\n<li>Refresh and Retry set <strong>too low<\/strong>:\n<ul>\n<li>High-frequency syncing can cause <strong>performance degradation and potential DoS<\/strong> due to increased load.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><strong>TTL Sizing<\/strong>\n<ul>\n<li>Record TTLs should be below the <strong>RRSIG<\/strong> TTL, but never set to 0.<\/li>\n<li>Use short TTLs on <strong>DS\/DNSKEY<\/strong> records for faster emergency key rollover propagation.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Information-leakage Hygiene<\/strong>\n<ul>\n<li>Remove <strong>HINFO<\/strong> (Host Information), <strong>RP<\/strong> (Responsible Person), and <strong>LOC<\/strong> (Location) records to reduce recon surface.<\/li>\n<li>Regularly audit records and delegations for <strong>dangling or stale<\/strong> entries.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Before enabling DNSSEC<\/strong>, choose the appropriate parameters.\n<ul>\n<li>Prefer <strong>ECC over RSA<\/strong>: smaller keys and signatures help keep responses under the UDP size limit.<\/li>\n<li>Limit <strong>key lifetimes<\/strong> to 1 to 3 years, and keep <strong>RRSIG validity<\/strong> short (5 to 7 days) to limit the blast radius of a compromised key.<\/li>\n<li>Consider storing KSKs (Key-signing Keys) in an <strong>HSM<\/strong> for important zones.<\/li>\n<li><strong>Do not sign internal zones.<\/strong> Use an encrypted private resolver with split-zone instead.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Enabling DNSSEC<\/strong><\/li>\n<\/ul>\n<p>As mentioned earlier, setting up DNSSEC involves creating keys on the authoritative server and adding their fingerprints at the parent zone (typically the registrar). On the recursive resolver side, enabling validation requires no additional key management.<\/p>\n<p>In this example, the domain is not registered through Cloudflare, but is hosted there. The concept is the same whether using PowerDNS, Technitium, cPanel, GoDaddy, or any other provider.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5884\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-52-09.png\" alt=\"\" width=\"1242\" height=\"625\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-52-09.png 1242w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-52-09-300x151.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-52-09-1024x515.png 1024w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-52-09-768x386.png 768w\" sizes=\"auto, (max-width: 1242px) 100vw, 1242px\" \/><\/p>\n<p>This will run the necessary routines to generate the keys inside the authoritative server.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5885\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-54-17.png\" alt=\"\" width=\"933\" height=\"784\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-54-17.png 933w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-54-17-300x252.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-54-17-768x645.png 768w\" sizes=\"auto, (max-width: 933px) 100vw, 933px\" \/><\/p>\n<p>The output is a set of parameters used to configure the parent in the DNS chain. Here is how it looks on Hover.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5886\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-55-31.png\" alt=\"\" width=\"763\" height=\"536\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-55-31.png 763w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-55-31-300x211.png 300w\" sizes=\"auto, (max-width: 763px) 100vw, 763px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5887\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-20.png\" alt=\"\" width=\"1192\" height=\"367\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-20.png 1192w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-20-300x92.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-20-1024x315.png 1024w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-20-768x236.png 768w\" sizes=\"auto, (max-width: 1192px) 100vw, 1192px\" \/><\/p>\n<p>It will remain in a pending state for about 30 minutes.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5888\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-32.png\" alt=\"\" width=\"831\" height=\"194\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-32.png 831w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-32-300x70.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_10-56-32-768x179.png 768w\" sizes=\"auto, (max-width: 831px) 100vw, 831px\" \/><\/p>\n<p>Then it will show as completed.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5889\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-01-46.png\" alt=\"\" width=\"831\" height=\"177\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-01-46.png 831w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-01-46-300x64.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-01-46-768x164.png 768w\" sizes=\"auto, (max-width: 831px) 100vw, 831px\" \/><\/p>\n<p>There are plenty of online tools for testing and diagnosing DNSSEC. See <strong>Verisign Labs<\/strong> [<a href=\"https:\/\/dnssec-analyzer.verisignlabs.com\/\">Link<\/a>].<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5890\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-04-03.png\" alt=\"\" width=\"458\" height=\"819\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-04-03.png 458w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-04-03-168x300.png 168w\" sizes=\"auto, (max-width: 458px) 100vw, 458px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5893\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-03-07.png\" alt=\"\" width=\"1194\" height=\"541\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-03-07.png 1194w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-03-07-300x136.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-03-07-1024x464.png 1024w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-03-07-768x348.png 768w\" sizes=\"auto, (max-width: 1194px) 100vw, 1194px\" \/><\/p>\n<p>Only four fields are required: <strong>KEY-TAG<\/strong>, <strong>ALGORITHM<\/strong>, <strong>DIGEST-ALGORITHM<\/strong>, and the <strong>DIGEST<\/strong>. Some registrars may use more or fewer parameters. See example:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5891\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-07-56.png\" alt=\"\" width=\"914\" height=\"512\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-07-56.png 914w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-07-56-300x168.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-07-56-768x430.png 768w\" sizes=\"auto, (max-width: 914px) 100vw, 914px\" \/><\/p>\n<p>Some registrars may not support this feature at all. What a shame!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5892\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-10-10.png\" alt=\"\" width=\"1194\" height=\"222\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-10-10.png 1194w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-10-10-300x56.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-10-10-1024x190.png 1024w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-12_11-10-10-768x143.png 768w\" sizes=\"auto, (max-width: 1194px) 100vw, 1194px\" \/><\/p>\n<p>For self-hosters, here is how to enable DNSSEC on Technitium and PowerDNS.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5924\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-24.png\" alt=\"\" width=\"940\" height=\"389\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-24.png 940w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-24-300x124.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-24-768x318.png 768w\" sizes=\"auto, (max-width: 940px) 100vw, 940px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5925\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-39.png\" alt=\"\" width=\"940\" height=\"389\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-39.png 940w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-39-300x124.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-39-768x318.png 768w\" sizes=\"auto, (max-width: 940px) 100vw, 940px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5926\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-57.png\" alt=\"\" width=\"523\" height=\"438\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-57.png 523w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-49-57-300x251.png 300w\" sizes=\"auto, (max-width: 523px) 100vw, 523px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5927\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-20.png\" alt=\"\" width=\"965\" height=\"467\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-20.png 965w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-20-300x145.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-20-768x372.png 768w\" sizes=\"auto, (max-width: 965px) 100vw, 965px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5928\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-43.png\" alt=\"\" width=\"799\" height=\"672\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-43.png 799w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-43-300x252.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-51-43-768x646.png 768w\" sizes=\"auto, (max-width: 799px) 100vw, 799px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5929\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-16.png\" alt=\"\" width=\"958\" height=\"465\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-16.png 958w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-16-300x146.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-16-768x373.png 768w\" sizes=\"auto, (max-width: 958px) 100vw, 958px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5930\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-50.png\" alt=\"\" width=\"954\" height=\"640\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-50.png 954w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-50-300x201.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/06\/Screenshot_2026-06-15_06-53-50-768x515.png 768w\" sizes=\"auto, (max-width: 954px) 100vw, 954px\" \/><\/p>\n<hr \/>\n<p><strong>Key Points to Remember<\/strong><\/p>\n<ul>\n<li>DNS is a <strong>security control<\/strong>, not just a name-resolution service.<\/li>\n<li><strong>Defense-in-depth<\/strong> turns DNS into a policy enforcement point and a source of visibility.<\/li>\n<li><strong>Protective DNS<\/strong> blocks malicious domains before the connection ever starts.<\/li>\n<li><strong>Encrypted DNS<\/strong> (DoT, DoH, DoQ) protects the channel between client and recursive resolver for confidentiality, not integrity.<\/li>\n<li><strong>DNSSEC<\/strong> protects data integrity by signing and validating records. It is not encryption.<\/li>\n<li><strong>DFIR<\/strong> log feeds can integrate with the broader security stack, including SIEM, XDR, and SOAR.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This post is a continuation of posts Resilient Nameserver Infrastructure \u2013 The Tools [Link] and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-5572","post","type-post","status-publish","format-standard","hentry","category-ccna"],"_links":{"self":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/5572","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5572"}],"version-history":[{"count":30,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/5572\/revisions"}],"predecessor-version":[{"id":5945,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/5572\/revisions\/5945"}],"wp:attachment":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5572"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5572"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5572"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}