This guide covers security best practices and hardening techniques for dnsdist deployments.
Lua:
-- DNS over TLS configuration
addTLSLocal("0.0.0.0:853",
"/etc/dnsdist/dnsdist.crt",
"/etc/dnsdist/dnsdist.key")
-- Strong cipher configuration
setTLSConfig({
ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305",
ciphers13 = "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256",
minTLSVersion = "TLSv1.2",
preferServerCiphers = true,
sessionTickets = false
})
YAML:
tls:
listen:
- address: "0.0.0.0:853"
cert: "/etc/dnsdist/dnsdist.crt"
key: "/etc/dnsdist/dnsdist.key"
config:
ciphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"
ciphers13: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
min_tls_version: "TLSv1.2"
prefer_server_ciphers: true
session_tickets: false
Generate Strong Certificates:
# Generate 4096-bit RSA key
openssl genrsa -out dnsdist.key 4096
# Generate certificate with 1-year validity
openssl req -new -x509 -key dnsdist.key -out dnsdist.crt \
-days 365 \
-subj "/CN=dns.example.com/O=Example Corp/C=US" \
-addext "subjectAltName=DNS:dns.example.com,DNS:resolver.example.com"
# Set proper permissions
chmod 600 dnsdist.key
chmod 644 dnsdist.crt
Certificate Auto-Reload:
-- Watch for certificate changes (v2.0+)
addTLSLocal("0.0.0.0:853",
"/etc/dnsdist/dnsdist.crt",
"/etc/dnsdist/dnsdist.key",
{
reloadCertificates = true
})
Lua:
-- Global rate limit
addAction(AllRule(), MaxQPSRule(10000))
-- Per-IP rate limit (sustained, burst)
addAction(AllRule(), MaxQPSIPRule(50, 100))
YAML:
rate_limiting:
- rule: "all"
qps: 10000
action: "drop"
- rule: "per_ip"
qps: 50
burst: 100
action: "drop"
Lua:
-- Block ANY queries (amplification attack vector)
addAction(
QTypeRule("ANY"),
DropAction()
)
-- Limit TXT queries
addAction(
AndRule({QTypeRule("TXT"), MaxQPSRule(100)}),
DropAction()
)
-- Limit PTR queries
addAction(
AndRule({QTypeRule("PTR"), MaxQPSRule(50)}),
DropAction()
)
YAML:
rate_limiting:
- query_type: "ANY"
action: "drop"
- query_type: "TXT"
qps: 100
action: "drop"
- query_type: "PTR"
qps: 50
action: "drop"
Lua:
-- Dynamic blocks for abusive clients
addAction(
MaxQPSIPRule(100, 200),
DynBlockRule(
"Abusive query rate",
60, -- Block duration (seconds)
10, -- Evaluation interval
nil, -- Warning rate (none)
50 -- Minimum queries before blocking
)
)
Lua:
-- Layer 1: Global rate limit
addAction(AllRule(), MaxQPSRule(100000))
-- Layer 2: Per-IP rate limit
addAction(AllRule(), MaxQPSIPRule(50, 100))
-- Layer 3: Block amplification vectors
addAction(QTypeRule("ANY"), DropAction())
addAction(QTypeRule("TXT"), MaxQPSRule(100))
-- Layer 4: Dynamic blocking
addAction(
MaxQPSIPRule(200, 500),
DynBlockRule("DDoS protection", 300, 10)
)
-- Layer 5: Query size limit
addAction(
QSizeRule(512),
DropAction()
)
YAML:
rate_limiting:
# Global limit
- rule: "all"
qps: 100000
action: "drop"
# Per-IP limit
- rule: "per_ip"
qps: 50
burst: 100
action: "drop"
# Query type limits
- query_type: "ANY"
action: "drop"
- query_type: "TXT"
qps: 100
action: "drop"
# Query size limit
- query_size: 512
action: "drop"
Lua:
-- Enable RRL
setRRLSize(10000)
setRRLQPS(100)
Lua:
-- Allow only trusted networks
setACL({
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"127.0.0.0/8",
"::1/128"
})
-- Block specific networks
addAction(
NetGroupRule({"198.51.100.0/24", "203.0.113.0/24"}),
DropAction()
)
YAML:
acl:
allow:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
- "127.0.0.0/8"
- "::1/128"
deny:
- "198.51.100.0/24"
- "203.0.113.0/24"
Lua:
-- Block known malicious domains
addAction(
SuffixRule("malware-domain.com"),
DropAction()
)
-- Block queries without RD bit (potential spoofing)
addAction(
NotRule(QRDRule()),
DropAction()
)
YAML:
filters:
- type: "suffix"
pattern: "malware-domain.com"
action: "drop"
- type: "no_rd_bit"
action: "drop"
Lua:
-- Bind to localhost only
webserver("127.0.0.1:8083", "very_secure_password")
-- Set ACL for web interface
setACL({"127.0.0.0/8", "::1/128"})
-- Use strong password (minimum 12 characters, mixed case, numbers)
-- Consider using API keys instead
YAML:
webserver:
address: "127.0.0.1:8083"
password: "very_secure_password_with_mixed_case_and_numbers123"
acl:
- "127.0.0.0/8"
- "::1/128"
Lua:
-- Use API keys instead of password
webserver("127.0.0.1:8083", "", {
apiKey = "secure-api-key-here"
})
Lua:
-- Enable HTTPS for web interface
webserver("0.0.0.0:8083", "password", {
tlsCert = "/etc/dnsdist/dnsdist.crt",
tlsKey = "/etc/dnsdist/dnsdist.key"
})
systemd override:
Create /etc/systemd/system/dnsdist.service.d/override.conf:
[Service]
User=dnsdist
Group=dnsdist
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/cache/dnsdist
version: '3.8'
services:
dnsdist:
image: powerdns/dnsdist-20:2.0.2
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=64m
- /run:noexec,nosuid,size=64m
volumes:
- ./dnsdist.conf:/etc/dnsdist/dnsdist.conf:ro
- ./certs:/etc/dnsdist/certs:ro
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
systemd limits:
Create /etc/systemd/system/dnsdist.service.d/limits.conf:
[Service]
LimitNOFILE=65535
LimitNPROC=64
MemoryMax=2G
CPUQuota=200%
Lua:
-- Block known malware domains
addAction(
SuffixRule("malware-domain.com"),
DropAction()
)
-- Block phishing domains
addAction(
SuffixRule("phishing-site.net"),
DropAction()
)
-- Use Response Policy Zones (RPZ)
-- Requires backend RPZ support
Lua:
-- Block potentially dangerous query types
addAction(QTypeRule("ANY"), DropAction())
addAction(QTypeRule("AXFR"), DropAction())
addAction(QTypeRule("IXFR"), DropAction())
-- Rate limit suspicious query types
addAction(
AndRule({QTypeRule("TXT"), MaxQPSRule(50)}),
DropAction()
)
Lua:
-- Drop oversized queries (potential attack)
addAction(
QSizeRule(512),
DropAction()
)
Lua:
-- Log blocked queries
addAction(
SuffixRule("malware-domain.com"),
LogAction("/var/log/dnsdist/blocked.log", true, true, true)
)
-- Log rate-limited clients
addAction(
MaxQPSIPRule(100),
LogAction("/var/log/dnsdist/rate-limited.log", true)
)
# View real-time statistics
dnsdist -e "showStats()"
# View dynamic blocks
dnsdist -e "showDynamicBlocks()"
# View blocked queries
tail -f /var/log/dnsdist/blocked.log
# dnsdist-alerts.yml
groups:
- name: dnsdist
rules:
- alert: DNSdistHighQueryRate
expr: rate(dnsdist_queries_total[1m]) > 100000
for: 5m
annotations:
summary: "High DNS query rate detected"
- alert: DNSdistDynamicBlocksActive
expr: dnsdist_dynamic_blocks > 100
for: 5m
annotations:
summary: "Many dynamic blocks active - possible attack"
- alert: DNSdistBackendDown
expr: dnsdist_downstream_servers{state="down"} > 0
for: 1m
annotations:
summary: "DNS backend server is down"
# Test DoT connection
kdig @localhost example.com +tls +tls-ca=/etc/dnsdist/dnsdist.crt
# Check certificate validity
openssl x509 -in /etc/dnsdist/dnsdist.crt -text -noout
# Verify certificate dates
openssl x509 -in /etc/dnsdist/dnsdist.crt -dates -noout
# View dynamic blocks
dnsdist -e "showDynamicBlocks()"
# Check rate limit statistics
dnsdist -e "showStats()" | grep -i rate
# Check current ACL
dnsdist -e "getACL()"
# Test from different network
dig @dnsdist-server example.com
Questions? Find all contact information on our contact page.