Networking AWS

PowerDNS Recursor 5.4.7 with dnsdist 2.1.3 on AWS User Guide

| Product: PowerDNS Recursor 5.4.7 + dnsdist on Ubuntu 24.04 on AWS

Overview

This guide covers launching and verifying PowerDNS Recursor 5.4.7 with dnsdist 2.1.3 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Pi-hole, AdGuard Home & DNS Server AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other DNS and time servers in the listing are described on the listing's guide hub.

The image is a two-daemon DNS appliance built from two open source projects by PowerDNS.COM BV:

  • PowerDNS Recursor 5.4.7 is the recursive resolver. It resolves names for your clients by walking the DNS from the root servers, validates DNSSEC signatures and caches the results.
  • dnsdist 2.1.3 is the DNS load balancer in front of it. It serves your clients over plain DNS, DNS-over-TLS and DNS-over-HTTPS, enforces the client access list and shows live traffic in a web dashboard.

Both are installed from the official PowerDNS repositories (repo.powerdns.com, release lines rec-54 and dnsdist-21). The repository signing key is pinned by its full fingerprint, and unattended upgrades keep the operating system and both release lines patched on your instance.

How the two daemons fit together

  clients --> plain DNS      :53  (udp + tcp) --+
  clients --> DNS-over-TLS   :853             --+--> dnsdist 2.1.3 --> PowerDNS Recursor 5.4.7
  clients --> DNS-over-HTTPS :443/dns-query   --+    0.0.0.0           127.0.0.1:5300 (loopback only)

dnsdist is the only daemon on a routable address. The Recursor listens on loopback and answers 127.0.0.0/8 only, so it cannot be reached from the network even if the dnsdist access list is later widened by mistake.

What is on the image:

  • PowerDNS Recursor 5.4.7 (pdns-recursor.service, user pdns) and dnsdist 2.1.3 (dnsdist.service, user _dnsdist)
  • DNSSEC validation on: bogus answers get SERVFAIL
  • Three client transports from one instance: plain DNS on 53 (UDP and TCP), DNS-over-TLS on 853 and DNS-over-HTTPS on 443 at /dns-query, both TLS 1.2 or newer with a certificate created on your instance's first boot
  • A not an open resolver access list: loopback, the RFC 1918 private ranges, 100.64.0.0/10 and IPv6 ULA/link-local only
  • The dnsdist web dashboard and REST API, the dnsdist console and the Recursor REST API, all on 127.0.0.1 with per-instance credentials
  • dig and kdig for testing all three transports, and a self-test, /usr/local/sbin/powerdns-dnsdist-selftest.sh

Default user: ubuntu · Architecture: x86_64 · Root volume: 8 GiB gp3

Security posture

  • Not an open resolver. dnsdist listens on every interface so clients in your VPC can reach it, but its access list admits only 127.0.0.0/8, ::1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, fd00::/8 and fe80::/10. Queries from any other source are dropped without an answer, over plain DNS, DNS-over-TLS and DNS-over-HTTPS alike. Open resolvers are abused for DNS amplification attacks, so this one answers private networks only.
  • The listing security group opens SSH only. DNS (53, UDP and TCP), DNS-over-TLS (853/tcp) and DNS-over-HTTPS (443/tcp) stay closed until you open them to your own VPC or VPN ranges. Clients use the instance's private IP.
  • No default credentials. The image ships no key, password or certificate. On first boot each instance generates its own dnsdist console key, dashboard password and REST API key, Recursor REST API key and webserver password, and DNS-over-TLS/HTTPS key and certificate. They are stored in root-only files.
  • Fails closed. Both daemons are gated on those per-instance files, and a start-time guard refuses to start them if a secret is missing, too short or a published example, if the access list admits the world, or if a management interface is not on loopback. If first boot cannot finish, it stops both daemons and tries again on the next boot.
  • Management on loopback only. The dnsdist console (5199), the dnsdist dashboard and API (8083) and the Recursor API (8082) listen on 127.0.0.1. Reach the dashboards through an SSH tunnel.
  • No metadata access for the daemons. The Recursor can fetch zones from URLs and dnsdist can forward to remote servers, so both pdns-recursor.service and dnsdist.service are blocked from reaching the instance metadata service (169.254.169.254 and fd00:ec2::254). Amazon Time Sync (169.254.169.123) still works.
  • IMDSv2 required. The AMI is registered with ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests.
  • Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.

Prerequisites

  1. An AWS account and permission to launch EC2 instances
  2. An EC2 key pair in the Region you launch in
  3. A VPC subnet for the resolver, and the CIDR ranges of the clients that will use it

Recommended instance type: m5.large. A resolver for a small network runs comfortably on t3.small or larger.

Connecting to your instance

Version Operating system SSH user
PowerDNS Recursor 5.4.7 + dnsdist on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu

Step 1: Launch the instance

Option A: AWS Marketplace console

  1. Open the cloudimg Hardened Pi-hole, AdGuard Home & DNS Server AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  2. Once the subscription is active, choose Launch your software.
  3. Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose PowerDNS Recursor 5.4.7 + dnsdist on Ubuntu 24.04, then choose your Region.
  4. Pick an instance type (m5.large recommended; the software charge is $0.04 per vCPU per hour) and your key pair.
  5. Security group: the listing's default group allows SSH (22) only. Restrict it to your own IP address. You open the DNS ports to your clients in Step 4.
  6. Choose your VPC and subnet, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.

Option B: AWS CLI

Run this on your own workstation. It creates a security group that allows SSH from your management address and the three DNS transports from your VPC, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDR ranges with your own:

SG=$(aws ec2 create-security-group --group-name powerdns-resolver \
  --description "PowerDNS Recursor + dnsdist" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 53 --cidr 10.0.0.0/16
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 53 --cidr 10.0.0.0/16
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 853 --cidr 10.0.0.0/16
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr 10.0.0.0/16
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=powerdns-resolver}]'

You do not need --metadata-options: the AMI already makes IMDSv2 required.

Step 2: Connect and find your credentials

Connect as ubuntu with the key pair you chose:

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot finishes within a minute of launch. It creates this instance's secrets and certificate, starts both daemons, proves all three transports answer, and writes /root/powerdns-recursor-dnsdist-credentials.txt (mode 0600, root only). Read it with sudo cat; the listing below hides the secret values so they do not appear in this guide:

sudo stat -c '%a %U:%G %n' /root/powerdns-recursor-dnsdist-credentials.txt
sudo sed -E 's/^([a-z.]+\.(key|password))=.*/\1=<hidden>/' /root/powerdns-recursor-dnsdist-credentials.txt | grep -E '^[a-z]'
600 root:root /root/powerdns-recursor-dnsdist-credentials.txt
dnsdist.console.key=<hidden>
dnsdist.webserver.password=<hidden>
dnsdist.api.key=<hidden>
dnsdist.webserver.url=http://127.0.0.1:8083/
dnsdist.tls.certificate=/etc/dnsdist/tls/dnsdist.crt
dnsdist.tls.fingerprint.sha256=F0:B5:F7:ED:D8:61:AE:04:A7:16:9C:0D:8E:3F:62:B4:5E:EE:C6:48:6A:F3:59:D5:3D:4E:26:70:DA:75:86:BE
dnsdist.tls.spki.pin.sha256=CqUUqZtNtZneO4qnawfQ8uFgE7pNAhxP576A+VMJxrQ=
powerdns.recursor.api.key=<hidden>
powerdns.recursor.webserver.password=<hidden>
powerdns.recursor.api.url=http://127.0.0.1:8082/api/v1/servers/localhost
appliance.private.ip=172.31.85.217
appliance.public.ip=3.83.157.82
Entry Used for
dnsdist.console.key the dnsdist console, sudo dnsdist -c (read automatically from the configuration)
dnsdist.webserver.password the dnsdist web dashboard (Step 8)
dnsdist.api.key the dnsdist REST API (X-API-Key header)
powerdns.recursor.api.key the Recursor REST API (X-API-Key header)
powerdns.recursor.webserver.password the Recursor web dashboard (Step 8)
dnsdist.tls.fingerprint.sha256, dnsdist.tls.spki.pin.sha256 identifying this instance's DNS-over-TLS/HTTPS certificate (Step 7)

Step 3: Confirm both daemons are running

Both daemons and the first-boot unit are active, and the self-test checks the versions, every per-instance credential (a wrong key gets 401, the right one 200), all three transports with this instance's certificate verified, the access list, and the exact set of listening ports:

systemctl is-active pdns-recursor.service dnsdist.service powerdns-recursor-dnsdist-firstboot.service
pdns_recursor --version 2>&1 | head -1
dnsdist --version | head -1
sudo /usr/local/sbin/powerdns-dnsdist-selftest.sh
active
active
active
PowerDNS Recursor 5.4.7 (C) PowerDNS.COM BV
dnsdist 2.1.3 (Lua 5.1.4 [LuaJIT 2.1.1703358377])
[pdns-dnsdist-selftest] versions: PowerDNS Recursor 5.4.7, dnsdist 2.1.3
[pdns-dnsdist-selftest] per-instance API keys and dashboard password authenticate; wrong ones get 401
[pdns-dnsdist-selftest] plain DNS, DoT and DoH (certificate verified) answer on 127.0.0.1 and 172.31.85.217; recursion resolves example.com
[pdns-dnsdist-selftest] not an open resolver: 192.0.2.53 got no answer on udp/53, tcp/53, DoT and DoH (HTTP 000); acl-drops 12 -> 16
[pdns-dnsdist-selftest] listening set OK (tcp: 22 53 443 853 ; udp: 53 68 ); recursor + management on 127.0.0.1 only
PDNS_DNSDIST_SELFTEST_OK

Both daemons active, PowerDNS Recursor 5.4.7 and dnsdist 2.1.3, the self-test passing, and the listening ports: DNS, DoT and DoH on all interfaces, the Recursor and every management port on 127.0.0.1

Step 4: Open the resolver to your clients

The listing's security group allows SSH only. To serve DNS, add inbound rules to the instance's security group from your own VPC or VPN CIDR ranges only, never from 0.0.0.0/0:

Protocol Port Source Purpose
UDP 53 your VPC / VPN CIDRs DNS
TCP 53 your VPC / VPN CIDRs DNS (large answers)
TCP 853 your VPC / VPN CIDRs DNS-over-TLS
TCP 443 your VPC / VPN CIDRs DNS-over-HTTPS

In the EC2 console choose the instance, open Security, choose the security group, then Edit inbound rules. Point clients at the instance's private IP (appliance.private.ip in Step 2). These are the only ports the instance listens on; everything except SSH and the three DNS transports is on 127.0.0.1:

sudo ss -Hltnu | awk '{print $1, $5}' | sort -u
tcp 0.0.0.0:22
tcp 0.0.0.0:443
tcp 0.0.0.0:53
tcp 0.0.0.0:853
tcp 127.0.0.1:5199
tcp 127.0.0.1:5300
tcp 127.0.0.1:8082
tcp 127.0.0.1:8083
tcp [::]:22
tcp [::]:53
udp 0.0.0.0:53
udp 127.0.0.1:323
udp 127.0.0.1:5300
udp 172.31.85.217%ens5:68
udp [::1]:323
udp [::]:53

Port 5300 is the Recursor (dnsdist is its only client), 5199 the dnsdist console, 8083 the dnsdist dashboard and API, 8082 the Recursor API, 323 chrony's local control socket and 68 the DHCP client.

From a client inside your VPC, test the resolver against its private IP:

dig @<private-ip> +dnssec example.com

To make clients use the resolver permanently, set its private IP as the DNS server in a DHCP options set for the VPC, or on each client's network configuration.

Step 5: Query over plain DNS, DNS-over-TLS and DNS-over-HTTPS

The same signed name over all three transports. The ad (Authenticated Data) flag shows the Recursor validated the DNSSEC signatures. kdig verifies the server certificate against this instance's certificate file (+tls-ca); it runs with sudo only because the certificate sits in the root-only /etc/dnsdist/tls/ directory next to the private key:

dig @127.0.0.1 +dnssec nlnetlabs.nl A | grep -E "status|flags:|^nlnetlabs.*IN.A|SERVER"
sudo kdig +tls +tls-ca=/etc/dnsdist/tls/dnsdist.crt +tls-hostname=127.0.0.1 @127.0.0.1 -p 853 +dnssec nlnetlabs.nl A | grep -E "TLS session|status|Flags|IN.A|From"
sudo kdig +https +tls-ca=/etc/dnsdist/tls/dnsdist.crt +tls-hostname=127.0.0.1 @127.0.0.1 +dnssec nlnetlabs.nl A | grep -E "TLS session|HTTP session|status|Flags|IN.A|From"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26770
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
; EDNS: version: 0, flags: do; udp: 512
nlnetlabs.nl.       54  IN  A   128.140.76.106
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 8965
;; Flags: qr rd ra ad; QUERY: 1; ANSWER: 2; AUTHORITY: 0; ADDITIONAL: 1
;; nlnetlabs.nl.            IN  A
nlnetlabs.nl.           54  IN  A   128.140.76.106
;; From 127.0.0.1@853(TLS) in 2.5 ms
;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)
;; HTTP session (HTTP/2-POST)-(127.0.0.1/dns-query)-(status: 200)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 0
;; Flags: qr rd ra ad; QUERY: 1; ANSWER: 2; AUTHORITY: 0; ADDITIONAL: 1
;; nlnetlabs.nl.            IN  A
nlnetlabs.nl.           54  IN  A   128.140.76.106
;; From 127.0.0.1@443(HTTPS) in 2.6 ms

From a client in your VPC, copy the certificate (/etc/dnsdist/tls/dnsdist.crt, which is public) to the client and use the private IP, for example kdig +tls +tls-ca=dnsdist.crt +tls-hostname=<private-ip> @<private-ip> -p 853 example.com. DNS-over-HTTPS clients use the URL https://<private-ip>/dns-query.

Step 6: Confirm DNSSEC rejects bad data

dnssec-failed.org is a public test domain whose signatures are deliberately broken. A validating resolver must answer SERVFAIL rather than pass the data on. Validation is set to validate in the Recursor configuration:

dig @127.0.0.1 dnssec-failed.org A | grep -E "status"
grep -A3 '^dnssec:' /etc/powerdns/recursor.d/cloudimg-recursor.yml
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 16022
dnssec:
  # Validate DNSSEC-signed answers (bogus answers get SERVFAIL).
  validation: validate
recursor:

nlnetlabs.nl answered with the ad flag over plain DNS, DNS-over-TLS and DNS-over-HTTPS with the certificate verified, and dnssec-failed.org rejected with SERVFAIL

Step 7: The DNS-over-TLS and DNS-over-HTTPS certificate

The certificate is self-signed and was created on this instance's first boot, with the instance's private IP, private DNS name, public IP and public DNS name in its Subject Alternative Names. The private key is readable only by root and the _dnsdist group:

sudo openssl x509 -in /etc/dnsdist/tls/dnsdist.crt -noout -subject -dates -ext subjectAltName -fingerprint -sha256
sudo stat -c '%a %U:%G %n' /etc/dnsdist/tls/dnsdist.key /etc/dnsdist/tls/dnsdist.crt
subject=CN = 172.31.85.217, O = cloudimg PowerDNS Recursor with dnsdist
notBefore=Oct 20 21:17:39 2026 GMT
notAfter=Oct 17 21:17:39 2036 GMT
X509v3 Subject Alternative Name: 
    DNS:localhost, IP Address:127.0.0.1, IP Address:172.31.85.217, DNS:ip-172-31-85-217.ec2.internal, IP Address:3.83.157.82, DNS:ec2-3-83-157-82.compute-1.amazonaws.com
sha256 Fingerprint=F0:B5:F7:ED:D8:61:AE:04:A7:16:9C:0D:8E:3F:62:B4:5E:EE:C6:48:6A:F3:59:D5:3D:4E:26:70:DA:75:86:BE
640 root:_dnsdist /etc/dnsdist/tls/dnsdist.key
644 root:_dnsdist /etc/dnsdist/tls/dnsdist.crt

Configure clients to trust it explicitly:

  • Pin the public key. Clients such as Stubby accept an SPKI pin. Use dnsdist.tls.spki.pin.sha256 from the credentials file, with the private IP as the authentication name.
  • Or trust the certificate. Copy /etc/dnsdist/tls/dnsdist.crt to the client and add it to the client's trust store or pass it as the CA file.

To use a certificate from your own certificate authority instead, replace the two files (keep the key 0640 root:_dnsdist) and restart dnsdist:

sudo cp my-server.crt /etc/dnsdist/tls/dnsdist.crt
sudo cp my-server.key /etc/dnsdist/tls/dnsdist.key
sudo chown root:_dnsdist /etc/dnsdist/tls/dnsdist.crt /etc/dnsdist/tls/dnsdist.key
sudo chmod 0640 /etc/dnsdist/tls/dnsdist.key
sudo systemctl restart dnsdist

Step 8: Open the dashboards

dnsdist and the Recursor each serve a web dashboard on 127.0.0.1. Open an SSH tunnel from your workstation, then browse to the forwarded ports:

ssh -i ~/.ssh/my-key-pair.pem -L 8083:127.0.0.1:8083 -L 8082:127.0.0.1:8082 ubuntu@<public-ip>
  • dnsdist: http://127.0.0.1:8083/, user admin, password dnsdist.webserver.password from Step 2. It shows the query rate, latency per transport, the listeners and access list, the Recursor backend and its health, and the active rules, including the per-client rate limit.

The dnsdist 2.1.3 dashboard: query rate, listeners on 53, 853 and 443, the private-range access list, the PowerDNS Recursor backend up on 127.0.0.1:5300 and the per-client rate-limit rule

  • PowerDNS Recursor: http://127.0.0.1:8082/, user admin, password powerdns.recursor.webserver.password. It shows the resolver's query rate, cache hit rate and the most-queried names.

The PowerDNS Recursor 5.4.7 dashboard: query rate, packet cache hit rate and the most-queried domains

Further down, the Recursor lists the names whose DNSSEC validation failed, such as dnssec-failed.org from Step 6:

The PowerDNS Recursor dashboard's SERVFAIL and bogus-domain tables, showing dnssec-failed.org rejected by DNSSEC validation

Step 9: Confirm it is not an open resolver

The access list is the setACL block in /etc/dnsdist/dnsdist.conf:

sed -n '/^setACL/,/^})/p' /etc/dnsdist/dnsdist.conf
setACL({
  '127.0.0.0/8',
  '::1/128',
  '10.0.0.0/8',
  '172.16.0.0/12',
  '192.168.0.0/16',
  '100.64.0.0/10',
  'fd00::/8',
  'fe80::/10',
})

You can prove the policy from the instance itself. This adds a temporary public test address (192.0.2.53, from the TEST-NET-1 documentation range) to the loopback interface, sends a query from it to the private IP over DNS and over DNS-over-TLS, sends the same query from the private IP itself, removes the test address and reads dnsdist's acl-drops counter. The public source gets no answer at all; the private one is answered:

PRIV=$(hostname -I | awk '{print $1}')
sudo ip addr add 192.0.2.53/32 dev lo
dig +time=2 +tries=1 -b 192.0.2.53 @"$PRIV" nlnetlabs.nl A | grep -E "status|timed out" || true
sudo kdig +tls +tls-ca=/etc/dnsdist/tls/dnsdist.crt +tls-hostname="$PRIV" +timeout=2 +retry=0 -b 192.0.2.53 @"$PRIV" -p 853 nlnetlabs.nl A 2>&1 | grep -E "status|WARNING|ERROR" || true
dig +time=2 +tries=1 -b "$PRIV" @"$PRIV" nlnetlabs.nl A | grep -oE "status: [A-Z]+"
sudo ip addr del 192.0.2.53/32 dev lo
sudo dnsdist -c -e 'dumpStats()' | grep -oE 'acl-drops[[:space:]]+[0-9]+'
;; communications error to 172.31.85.217#53: timed out
;; WARNING: TLS, handshake failed (Error in the pull function.)
;; ERROR: failed to query server 172.31.85.217@853(TCP)
status: NOERROR
acl-drops                                    19

The private-range access list, a query from the public test address 192.0.2.53 timing out over DNS and DNS-over-TLS while the private IP is answered, and dnsdist's acl-drops counter

Step 10: Allow another of your own networks (optional)

To serve a network outside the private ranges, for example a VPN range that uses public addresses, add it inside the setACL({ ... }) block of /etc/dnsdist/dnsdist.conf, check the configuration and restart dnsdist. Replace 198.51.100.0/24 with your own range:

sudoedit /etc/dnsdist/dnsdist.conf        # add  '198.51.100.0/24',  inside setACL({ ... })
sudo dnsdist --check-config
sudo systemctl restart dnsdist

Never add 0.0.0.0/0 or ::/0: that turns the instance into an open resolver, and the start-time guard refuses to start dnsdist with it. Remember to allow the same range in the security group (Step 4).

Step 11: Metadata access is blocked for both daemons

pdns-recursor.service and dnsdist.service each carry a systemd IPAddressDeny rule for the instance metadata addresses. Each entry is a single address, so the Amazon Time Sync Service at 169.254.169.123 and the Amazon VPC resolver keep working, as chrony shows:

systemctl show -p IPAddressDeny pdns-recursor.service dnsdist.service
chronyc -n sources | grep 169.254
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128

IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
^* 169.254.169.123               3   4   377     9  -1782ns[-2585ns] +/-  390us

The Recursor sends its own queries over IPv4 (outgoing.source_address: 0.0.0.0 in /etc/powerdns/recursor.d/cloudimg-recursor.yml), which suits the many EC2 subnets that have no IPv6 route. On a dual-stack subnet, add - '::' under source_address and restart the Recursor to also query over IPv6.

Step 12: Day-to-day operations

sudo dnsdist -c opens the dnsdist console (it reads the console key from the configuration), and rec_control talks to the Recursor over its local control socket:

sudo dnsdist -c -e 'showServers()'
sudo rec_control get-all | grep -E '^(questions|cache-hits|packetcache-hits|cache-entries)'
#   Name                 Address                                      State     Qps    Qlim        Ord         Wt    Queries   Drops Drate   Lat   TCP Outstanding Pools
0   powerdns-recursor    127.0.0.1:5300                                  up     0.0       0          1          1         48       0   0.0   2.8   0.0           0 
All                                                                             0.0                                       48       0                               
cache-entries   135
cache-hits  10
packetcache-hits    233
questions   247

Other common commands:

sudo rec_control wipe-cache example.com$     # drop one name (and everything under it) from the cache
sudo rec_control reload-zones                # re-read forward zones after editing them
sudo dnsdist -c -e 'topQueries(10)'          # the ten most-queried names in dnsdist's ring buffer
sudo systemctl restart dnsdist pdns-recursor # after a configuration change

The Recursor's configuration lives in /etc/powerdns/recursor.d/ (YAML) and dnsdist's in /etc/dnsdist/dnsdist.conf (Lua). The per-instance secrets are in separate root-only files next to them (cloudimg-secrets.yml and cloudimg-secrets.conf); leave those in place.

Step 13: Security recommendations

  • Keep the security group tight. Open 53, 853 and 443 only to the networks that need the resolver, and SSH only to your management address.
  • Keep the access list scoped. Only add networks you trust (Step 10), never 0.0.0.0/0. The access list is a second layer of defence behind the security group.
  • Leave DNSSEC validation on. It protects your clients from spoofed or tampered answers.
  • Prefer DNS-over-TLS or DNS-over-HTTPS for clients that support them, so queries cannot be read or altered on the network.
  • Keep the dashboards on loopback. Reach them through an SSH tunnel; the credentials file is root-only for a reason.
  • Keep the software patched. Unattended upgrades apply Ubuntu security updates and the PowerDNS 5.4.x and dnsdist 2.1.x releases from repo.powerdns.com.

Support and licensing

PowerDNS Recursor and dnsdist are developed by PowerDNS.COM BV and distributed under the GNU General Public License version 2. This image ships the unmodified packages from repo.powerdns.com. cloudimg provides the packaging, the not an open resolver defaults, DNSSEC validation, per-instance credentials and DNS-over-TLS/HTTPS certificate, the metadata block, and 24/7 support.

cloudimg is not affiliated with or endorsed by PowerDNS.COM BV. PowerDNS and dnsdist are marks of their owner and are used here only to identify the software.

For help with deployment or configuration, email support@cloudimg.co.uk or visit cloudimg support.