{"id":5517,"date":"2026-05-22T21:53:10","date_gmt":"2026-05-23T01:53:10","guid":{"rendered":"https:\/\/dft.wiki\/?p=5517"},"modified":"2026-06-03T08:20:07","modified_gmt":"2026-06-03T12:20:07","slug":"resilient-dns-infrastructure","status":"publish","type":"post","link":"https:\/\/dft.wiki\/?p=5517","title":{"rendered":"Resilient Nameserver Infrastructure &#8211; The Tools"},"content":{"rendered":"<p>There are many mature DNS services, such as <strong>unbound<\/strong>, <strong>dnsmasq<\/strong>, and <strong>bind9<\/strong><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">, but some may argue that <strong>PowerDNS<\/strong> is the most modern and flexible implementation [<a href=\"https:\/\/github.com\/PowerDNS\/pdns\">Link<\/a>]<\/span>.<\/p>\n<p>PowerDNS has 4 basic components:<\/p>\n<ul>\n<li><strong>DNSDist<\/strong>\n<ul>\n<li>It is a DNS-aware load balancer that sits in front of your actual DNS servers (like the <strong>Recursor<\/strong> or <strong>Authoritative<\/strong>) to accept incoming traffic, inspect it, and decide exactly where it should go or should be dropped (if deemed malicious or exceeded a rate limit).<\/li>\n<\/ul>\n<\/li>\n<li><strong>Recursor<\/strong>\n<ul>\n<li>It is a caching resolver that recursively reaches the Internet&#8217;s root servers and top-level domains to find the answers. It does not host any zone.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Authoritative Server<\/strong>\n<ul>\n<li>It is responsible for hosting zones, with definitive records for a specific domain. It does not respond to any request for zones that it does not own.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Backend<\/strong>\n<ul>\n<li>The so-called backend, in this context, is how the Authoritative Server stores the records (e.g., MySQL, PostgreSQL, or BIND zone files).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>Note: <\/strong> The Authoritative Server holds no data itself (stateless), all data lives in the database (the state). To make it highly available, just connect multiple server instances to that same Backend (a database cluster, Galera, or a replicated setup, etc).<\/p>\n<hr \/>\n<p><strong>INTERNAL DNS SERVICE<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5520\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/powerdns-00-1.png\" alt=\"\" width=\"617\" height=\"212\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/powerdns-00-1.png 617w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/powerdns-00-1-300x103.png 300w\" sizes=\"auto, (max-width: 617px) 100vw, 617px\" \/><\/p>\n<p>For this example, a DNS service will be deployed to serve an internal network (aka LAN). It will have a layer of protection and a recursive resolver behind it. Feel free to deploy multiple instances for greater resilience and fault tolerance as necessary.<\/p>\n<p>Installing <strong>DNSDist<\/strong> on Debian-based distros.<\/p>\n<pre>sudo apt-get install -y dnsdist -y\r\nsudo sed -i 's\/^#\\?DNSStubListener=.*\/DNSStubListener=no\/' \/etc\/systemd\/resolved.conf\r\nsudo sed -i 's\/^#\\?DNS=.*\/DNS=127.0.0.1\/' \/etc\/systemd\/resolved.conf\r\nsudo systemctl restart systemd-resolved\r\nsudo nano \/etc\/dnsdist\/dnsdist.conf<\/pre>\n<pre>-- dnsdist configuration file, an example can be found in \/usr\/share\/doc\/dnsdist\/examples\/\r\n-- disable security status polling via DNS.\r\nsetSecurityPollSuffix(\"\")\r\n-- Bind to any interface on port 53 (both IPv4 and IPv6).\r\nsetLocal(\"0.0.0.0:53\")\r\naddLocal(\"[::]:53\")\r\n-- Allow requests from any address (IPv4 and IPv6).\r\naddACL(\"0.0.0.0\/0\")\r\naddACL(\"::\/0\")\r\n-- Define your backend DNS server(s) that dnsdist will forward traffic to.\r\nnewServer({address=\"<strong>1.1.1.1<\/strong>\", qps=1000})<\/pre>\n<p><strong>Note:<\/strong> The example above is over-permissive for demonstration purposes and shall be tailored\/locked down to the network requirements. Refer to <code>\/usr\/share\/doc\/dnsdist\/examples\/dnsdist.conf<\/code> for more examples.<\/p>\n<pre>sudo dnsdist --check-config\r\nsudo systemctl restart dnsdist<\/pre>\n<p>Test it!<\/p>\n<pre>dig google.com @127.0.0.1 +noall +answer<\/pre>\n<p>On another instance&#8230;<\/p>\n<p>Installing <strong>Recursor<\/strong> on Debian-based distros.<\/p>\n<pre>sudo apt-get install pdns-recursor -y\r\nsudo sed -i 's\/^#\\?DNSStubListener=.*\/DNSStubListener=no\/' \/etc\/systemd\/resolved.conf \r\nsudo sed -i 's\/^#\\?DNS=.*\/DNS=127.0.0.1\/' \/etc\/systemd\/resolved.conf \r\nsudo systemctl restart systemd-resolved\r\nsudo nano \/etc\/powerdns\/recursor.conf<\/pre>\n<p>Updates the following blocks.<\/p>\n<pre>dnssec:\r\n  # validation: process # default\r\n  trustanchorfile: \/usr\/share\/dns\/root.key\r\nrecursor:\r\n  hint_file: \/usr\/share\/dns\/root.hints\r\n  include_dir: \/etc\/powerdns\/recursor.d\r\n  security_poll_suffix: ''\r\n#  forward_zones_recurse:\r\n#    - zone: .\r\n#      forwarders:\r\n#        - 1.1.1.1\r\n#        - 8.8.8.8\r\n#    - zone: example.internal\r\n#      forwarders:\r\n#        - 192.168.10.10\r\nincoming:\r\n  listen:\r\n    - 0.0.0.0\r\n  allow_from:\r\n    - 0.0.0.0\/0\r\noutgoing:\r\n # source_address:\r\n # - 0.0.0.0 # default<\/pre>\n<p><strong>Note:<\/strong> It is also over-permissive and needs to be secured appropriately to suit the environment&#8217;s needs. There are also examples to forward to an external resolver and for an internal zone, if that is the case.<\/p>\n<pre>sudo systemctl restart pdns-recursor\r\n<\/pre>\n<p>Test it!<\/p>\n<pre>dig google.mx @127.0.0.1 +noall +answer<\/pre>\n<p>If everything works, change <strong>DNSDist<\/strong> that was configured and tested with <code>1.1.1.1<\/code> to forward to <strong>Recursor<\/strong>.<\/p>\n<pre>sudo sed -i 's\/1\\.1\\.1\\.1\/192\\.168\\.10\\.20\/' \/etc\/dnsdist\/dnsdist.conf\r\nsudo dnsdist --check-config\r\nsudo systemctl restart dnsdist<\/pre>\n<hr \/>\n<p><strong>HOSTING DNS ZONES<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5522\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/powerdns-01.png\" alt=\"\" width=\"415\" height=\"85\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/powerdns-01.png 415w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/powerdns-01-300x61.png 300w\" sizes=\"auto, (max-width: 415px) 100vw, 415px\" \/><\/p>\n<p>Compared to the example above, in this case a real zone such as <strong>example.com<\/strong> needs to be publicly exposed.<\/p>\n<p>This example will have a layer of protection (using <strong>DNSDist<\/strong> again) and an Authoritative Nameserver behind it. Feel free to deploy multiple instances for greater resilience and fault tolerance as necessary.<\/p>\n<p>Install <strong>DNSDist<\/strong> with the same steps demonstrated before.<\/p>\n<p>On another instance&#8230;<\/p>\n<p>Installing <strong>Authoritative Server<\/strong> on Debian-based distros.<\/p>\n<pre>sudo apt-get install pdns-server -y\r\nsudo grep -E -v '^(#|$)' \/etc\/powerdns\/pdns.conf<\/pre>\n<p>The output of this command defines what Backend strategy the server is configured to use. For the sake of learning, this is how to configure to use Bind9 files for zones.<\/p>\n<pre>sudo nano \/etc\/powerdns\/named.conf<\/pre>\n<p>Append the configuration file with a reference to the new zone.<\/p>\n<pre>zone \"example.com\" {\r\n    type master;\r\n    file \"\/etc\/powerdns\/zones\/example.com.zone\";\r\n};<\/pre>\n<p>Then, create the file with the records for the new zone.<\/p>\n<pre>sudo mkdir -p \/etc\/powerdns\/zones\r\nsudo nano \/etc\/powerdns\/zones\/example.com.zone<\/pre>\n<p>Add the following content.<\/p>\n<pre>$TTL 3600\r\n@   SOA ns1.example.com. admin.example.com. 1 10800 3600 604800 86400\r\n\r\n@   NS  ns1.example.com.\r\n@   NS  ns2.example.com.\r\n\r\nns1 A   192.0.2.1\r\nns2 A   192.0.2.2\r\n\r\n@   A   192.0.2.10\r\nwww A   192.0.2.10<\/pre>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><strong>Administrator<\/strong> (<code>admin.example.com.<\/code>)\n<ul>\n<li>The notation that represents the email of the administrator of this zone <code>admin@example.com<\/code> ( <code>@<\/code> is not allowed, so a <code>.<\/code> is used).<\/li>\n<\/ul>\n<\/li>\n<li><strong>Serial<\/strong> (<code>1<\/code>)\n<ul>\n<li>The version number of your zone file. When any record is changed, it must increment this number, or secondary servers won&#8217;t notice the update.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Refresh<\/strong> (<code>10800<\/code>)\n<ul>\n<li>3 hours (in seconds). How often the Secondary DNS servers ask your master server if the Serial number has gone up.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Retry<\/strong> (<code>3600<\/code>)\n<ul>\n<li>1 hour (in seconds). If a secondary server tries to check the master and fails, this is how long it waits before trying again.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Expire<\/strong> (<code>604800<\/code>)\n<ul>\n<li>1 week (in seconds). If your master server goes completely offline, secondary servers will keep answering for your domain for this long before giving up and assuming the data is too old to trust.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Minimum<\/strong> (<code>86400<\/code>)\n<ul>\n<li>1 day (in seconds). This controls &#8220;negative caching.&#8221; If someone asks for a record that doesn&#8217;t exist (like fake.example.com), internet routers will remember that it doesn&#8217;t exist for 1 day, saving your server from being hammered with identical bad requests.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>These parameters are responsible for the dreaded &#8220;DNS propagation&#8221; delay. In fact, nothing really propagates at all. What these numbers define is how long answers are cached along the way. Consider using shorter periods to reflect changes more quickly between servers and resolvers.<\/p>\n<p>Restart the Authoritative Server to load the configuration.<\/p>\n<pre>sudo systemctl restart pdns<\/pre>\n<p>Test it!<\/p>\n<pre>dig example.com A @127.0.0.1 +noall +answer\r\ndig www.example.com A @127.0.0.1 +noall +answer\r\ndig example.com NS @127.0.0.1 +noall +answer\r\ndig ns1.example.com A @127.0.0.1 +noall +answer\r\ndig ns2.example.com A @127.0.0.1 +noall +answer\r\ndig example.com SOA @127.0.0.1<\/pre>\n<p>Next time changes are done to this zone, reload the configuration without restarting the service.<\/p>\n<pre>sudo pdns_control <strong>bind-reload-now<\/strong> example.com<\/pre>\n<p>Back to the <strong>DNSDist<\/strong> instance&#8230;<\/p>\n<p>Define a routing rule (Policy) that sends requests matching <code>example.com<\/code> to the <strong>Authoritative Server<\/strong> as a new backend pool.<\/p>\n<pre>echo 'newServer({address=\"<strong>192.168.10.10<\/strong>\", pool=\"<span style=\"text-decoration: underline;\">internal-pool<\/span>\", qps=1000})' | tee -a \/etc\/dnsdist\/dnsdist.conf\r\necho 'addAction(\"example.com.\", PoolAction(\"<span style=\"text-decoration: underline;\">internal-pool<\/span>\"))' | tee -a \/etc\/dnsdist\/dnsdist.conf\r\nsudo systemctl restart dnsdist<\/pre>\n<p>Test it!<\/p>\n<pre>dig example.com @127.0.0.1<\/pre>\n<hr \/>\n<p><strong>USING MYSQL AS THE BACKEND<\/strong><\/p>\n<p>This tutorial will not explain how to deploy any type of database clusterization or replication because that is already covered in other posts.<\/p>\n<p>Install <strong>MySQL<\/strong> server with the following command. Alternatively, MariaDB or PostgreSQL would be great alternatives too.<\/p>\n<pre>sudo apt install mysql-server pdns-backend-mysql -y<\/pre>\n<p><strong>Note:<\/strong> The example above is installing the DB on the same instance of the Authoritative Server for simplification.<\/p>\n<pre>sudo mysql -u root<\/pre>\n<p>Create the database and service account.<\/p>\n<pre>mysql&gt; CREATE DATABASE powerdns CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\r\nmysql&gt; CREATE USER 'pdns_user'@'localhost' IDENTIFIED BY 'your_secure_password';\r\nmysql&gt; GRANT ALL PRIVILEGES ON powerdns.* TO 'pdns_user'@'localhost';\r\nmysql&gt; FLUSH PRIVILEGES;\r\nmysql&gt; EXIT;<\/pre>\n<p>Load the database schema.<\/p>\n<pre>sudo mysql -u pdns_user -p powerdns &lt; \/usr\/share\/doc\/pdns-backend-mysql\/schema.mysql.sql\r\nmysql -u pdns_user -p -e 'SHOW TABLES;' powerdns<\/pre>\n<p>Remove the Bind9 configuration and load the MySQL.<\/p>\n<pre>sudo rm -f \/etc\/powerdns\/pdns.d\/bind.conf\r\nsudo nano \/etc\/powerdns\/pdns.d\/gmysql.conf<\/pre>\n<p>Add the following content.<\/p>\n<pre># Launch the gmysql backend engine\r\nlaunch=gmysql\r\n\r\n# Database connection parameters\r\ngmysql-host=127.0.0.1\r\ngmysql-port=3306\r\ngmysql-dbname=powerdns\r\ngmysql-user=pdns_user\r\ngmysql-password=password\r\ngmysql-dnssec=yes<\/pre>\n<p>Protect the file and restart the Server.<\/p>\n<pre>sudo chmod 640 \/etc\/powerdns\/pdns.d\/gmysql.conf\r\nsudo chown root:pdns \/etc\/powerdns\/pdns.d\/gmysql.conf\r\nsudo systemctl restart pdns<\/pre>\n<p>Test it!<\/p>\n<pre>dig example.com @127.0.0.1<\/pre>\n<p>You might have an empty response as a successful request because the zone is not present. A failed tentative will timeout.<\/p>\n<hr \/>\n<p><strong>MANAGING ZONES<\/strong><\/p>\n<pre>sudo pdnsutil create-zone example.com\r\nsudo pdnsutil clear-zone example.com\r\nsudo pdnsutil add-record example.com example.com SOA \"ns1.example.com. admin.example.com. 1 10800 3600 604800 86400\"\r\nsudo pdnsutil add-record example.com example.com NS ns1.example.com\r\nsudo pdnsutil add-record example.com example.com NS ns2.example.com\r\nsudo pdnsutil add-record example.com ns1.example.com A 192.0.2.1\r\nsudo pdnsutil add-record example.com ns2.example.com A 192.0.2.2\r\nsudo pdnsutil add-record example.com example.com A 192.0.2.10\r\nsudo pdnsutil add-record example.com www.example.com A 192.0.2.10\r\nsudo pdnsutil list-zone example.com\r\nsudo pdnsutil set-kind example.com master<\/pre>\n<p>Test it!<\/p>\n<pre>dig www.example.com @127.0.0.1<\/pre>\n<hr \/>\n<p><strong>USING THE WEB GUI<\/strong><\/p>\n<p>On the Authoritative Server, edit the PowerDNS once more to enable the Web Server and the API.<\/p>\n<pre>sudo nano \/etc\/powerdns\/pdns.conf<\/pre>\n<pre>api=yes\r\napi-key=<strong>A_SUPER_SECRET_API_KEY_FOR_EXAMPLE_A_UUID<\/strong>\r\nwebserver=yes\r\nwebserver-address=0.0.0.0\r\nwebserver-allow-from=0.0.0.0\/0,::\/0\r\nwebserver-port=8081<\/pre>\n<p><strong>Note:<\/strong> The example above is EXTREMELY over-permissive and needs to be secured appropriately to suit the environment&#8217;s needs.<\/p>\n<pre>sudo systemctl restart pdns<\/pre>\n<p>Navigate to <strong>http:\/\/192.168.10.10:8081\/<\/strong> (replace with your IP).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5528\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_19-55-05.png\" alt=\"\" width=\"793\" height=\"1240\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_19-55-05.png 793w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_19-55-05-192x300.png 192w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_19-55-05-655x1024.png 655w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_19-55-05-768x1201.png 768w\" sizes=\"auto, (max-width: 793px) 100vw, 793px\" \/><\/p>\n<p>PowerDNS can also be monitored with <strong>Prometheus<\/strong> via <strong>http:\/\/192.168.10.10:8081\/metrics<\/strong>.<\/p>\n<p>Even though it presents an enormous amount of information and valuable metrics, PowerDNS does not have an official Admin Dashboard-like service.<\/p>\n<p>There are well-known frontend apps that can be used for eventually managing zones, such as <strong>PowerDNS-Admin<\/strong> [<a href=\"https:\/\/github.com\/PowerDNS-Admin\/PowerDNS-Admin\">Link<\/a>].<\/p>\n<pre>sudo docker run -d --name powerdns-admin -e SECRET_KEY='<strong>A_SUPER_SECRET_API_KEY_FOR_EXAMPLE_A_UUID<\/strong>' -v pda-data:\/data -p 127.0.0.1:9191:80 powerdnsadmin\/pda-legacy:latest<\/pre>\n<p>For reference, the following commands stop and start the container.<\/p>\n<pre>docker <strong>stop<\/strong> powerdns-admin<\/pre>\n<pre>docker <strong>start<\/strong> powerdns-admin<\/pre>\n<p>Navigate to <strong>http:\/\/127.0.0.1:9191\/<\/strong><\/p>\n<p>Click on <strong>Create an account<\/strong>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5534\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-11-11.png\" alt=\"\" width=\"380\" height=\"402\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-11-11.png 380w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-11-11-284x300.png 284w\" sizes=\"auto, (max-width: 380px) 100vw, 380px\" \/><\/p>\n<p>Then, log in as this account. The first one will have Admin privileges automatically.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5533\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-12-00.png\" alt=\"\" width=\"380\" height=\"775\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-12-00.png 380w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-12-00-147x300.png 147w\" sizes=\"auto, (max-width: 380px) 100vw, 380px\" \/><\/p>\n<p>Enter the URL for the Authoritative Server (e.g, <strong>http:\/\/192.168.10.10:8081\/<\/strong>) and the API Key.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5532\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-15-49.png\" alt=\"\" width=\"1083\" height=\"928\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-15-49.png 1083w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-15-49-300x257.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-15-49-1024x877.png 1024w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-15-49-768x658.png 768w\" sizes=\"auto, (max-width: 1083px) 100vw, 1083px\" \/><\/p>\n<p>On the left menu, there will be options to create, remove, and manage Zones.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5531\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-17-10.png\" alt=\"\" width=\"1083\" height=\"379\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-17-10.png 1083w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-17-10-300x105.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-17-10-1024x358.png 1024w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-17-10-768x269.png 768w\" sizes=\"auto, (max-width: 1083px) 100vw, 1083px\" \/><\/p>\n<p>Click on a zone to manage its records.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5530\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-18-10.png\" alt=\"\" width=\"1083\" height=\"685\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-18-10.png 1083w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-18-10-300x190.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-18-10-1024x648.png 1024w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-18-10-768x486.png 768w\" sizes=\"auto, (max-width: 1083px) 100vw, 1083px\" \/><\/p>\n<p>It is all intuitive and self-explanatory.<\/p>\n<p>Consider using a reverse proxy with TLS termination for encrypted traffic to the GUI and API.<\/p>\n<hr \/>\n<p><strong>DNS VALIDATION TLS CERTIFICATE<\/strong><\/p>\n<p>To issue TLS certificates with DNS Challenge via ACME protocol (<strong>acme.sh<\/strong> or <strong>certbot<\/strong>), use the following method.<\/p>\n<pre>export PDNS_Url=\"<strong>http:\/\/192.168.10.10:8081<\/strong>\"\r\nexport PDNS_Key=\"<strong>A_SUPER_SECRET_API_KEY_FOR_EXAMPLE_A_UUID<\/strong>\"\r\nacme.sh --issue --dns dns_pdns -d example.com -d www.example.com<\/pre>\n<p>Another way is to use PowerDNS-Admin as an API Proxy with RBAC by creating keys with minimal permissions and limited access to specific zones.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5535\" src=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-48-44.png\" alt=\"\" width=\"1015\" height=\"754\" srcset=\"https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-48-44.png 1015w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-48-44-300x223.png 300w, https:\/\/dft.wiki\/wp-content\/uploads\/sites\/15\/2026\/05\/Screenshot_2026-05-22_21-48-44-768x571.png 768w\" sizes=\"auto, (max-width: 1015px) 100vw, 1015px\" \/><\/p>\n<p>This allows granular control of keys per client\/consumer, limiting the risk of exposure and facilitating rotation when necessary.<\/p>\n<hr \/>\n<p><strong>REFLECTIONS<\/strong><\/p>\n<p>This post was designed to set the foundation for the basic concepts for two scenarios: recursive name resolution and authoritative zones.<\/p>\n<p>In the second post, architectural designs for a geographically fault-tolerant and highly available service will be covered.<\/p>\n<p>A third post will cover security aspects, such as tampering resistance and encryption.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are many mature DNS services, such as unbound, dnsmasq, and bind9, but some may [&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-5517","post","type-post","status-publish","format-standard","hentry","category-ccna"],"_links":{"self":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/5517","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=5517"}],"version-history":[{"count":12,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/5517\/revisions"}],"predecessor-version":[{"id":5583,"href":"https:\/\/dft.wiki\/index.php?rest_route=\/wp\/v2\/posts\/5517\/revisions\/5583"}],"wp:attachment":[{"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5517"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5517"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dft.wiki\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5517"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}