DNS servers are surprisingly fragile, and a single misconfiguration can turn your resolver into an unwitting accomplice in massive denial-of-service attacks.
Let’s see what a hardened DNS server looks like in practice. Imagine we’re running BIND, a common DNS server.
# Example BIND configuration snippet
options {
directory "/var/cache/bind";
recursion yes;
allow-query { trusted_clients; }; # Restrict queries to known clients
allow-recursion { trusted_clients; }; # Restrict recursion to known clients
dnssec-enable yes;
dnssec-validation yes;
listen-on { 192.168.1.10; 10.0.0.5; }; # Only listen on specific interfaces
listen-on-v6 { ::1; };
max-cache-size 90%; # Limit cache size to prevent memory exhaustion
pid-file "/var/run/named/named.pid";
session-keyfile "/var/run/named/session.key";
tcp-clients 100; # Limit concurrent TCP connections
recursion-threads 4; # Tune thread count for recursion
auth-nxdomain no; # Disable authoritative answer for NXDOMAIN responses
// ... other options
};
acl "trusted_clients" {
localhost;
localnets; # Define localnets in another ACL block
192.168.1.0/24;
10.0.0.0/8;
};
// Define localnets if not already defined
acl "localnets" {
192.168.1.0/24;
10.0.0.0/8;
};
This configuration aims to prevent common DNS abuse vectors. The allow-query and allow-recursion directives are your first line of defense. By default, many DNS servers allow queries from anywhere, turning them into open resolvers that attackers can use to amplify DDoS attacks. Explicitly defining which IP addresses or networks are allowed to query your server (and more importantly, request recursive lookups) is crucial. localhost; allows the server itself, and localnets; is a BIND-defined keyword that typically includes RFC1918 private address ranges. You’d then define your specific trusted private ranges in an acl block.
Rate limiting is another essential component. If you don’t have a dedicated anycast network with built-in DDoS protection, you’ll need to implement controls within your DNS server. BIND’s rate-limit statement can be configured to limit the number of queries from a single source IP address within a given time window. For example:
rate-limit {
// Limit queries for any single name to 100 per minute
responses-per-interval 100;
// applied to all queries to this zone
// rate-limit-domain { "example.com"; };
// rate-limit-domain "*"; // applies to all domains
// interval 60; // in seconds
// add-randomness no; // not recommended for public servers
// log yes; // log when rate limit is hit
// suppress-key "rate-limited-query"; // internal key for suppression
// max-reply-size 4096; // max response size
// error-response "server-busy"; // response for rate limited query
};
This example, while illustrative, would be configured to limit specific query types or sources to prevent them from overwhelming the server. A more practical configuration might look like:
rate-limit {
responses-per-interval 500; // Allow 500 queries per source IP per minute
interval 60; // The interval is 60 seconds
log yes; // Log when the rate limit is hit
errors-per-interval 10; // Allow 10 error responses per source IP per minute
window 30; // The window for counting is 30 seconds
};
This limits any single client IP to 500 queries in a 60-second interval, helping to mitigate query floods. The errors-per-interval and window directives provide additional granular control.
DNSSEC (DNS Security Extensions) is not just about preventing cache poisoning; it also helps prevent certain types of spoofing and ensures data integrity. Enabling dnssec-enable yes; and dnssec-validation yes; instructs your server to validate the digital signatures of DNS records it retrieves. This means that if a malicious actor tries to inject a forged record, your server will detect the invalid signature and reject the data.
Controlling recursion is paramount. An open recursive resolver is a goldmine for attackers wanting to perform DNS amplification attacks. By setting recursion yes; but coupling it with a strict allow-recursion ACL that only permits trusted clients (like your internal network or specific partner IPs), you prevent external attackers from using your server as part of their botnet. For authoritative-only servers, you’d set recursion no;.
Limiting the max-cache-size prevents a single large or unusually formed DNS response from consuming all available memory, which could lead to a denial-of-service. Setting it to a percentage like 90% ensures some headroom. Similarly, tcp-clients limits the number of concurrent TCP connections, which are often used for larger DNS responses or zone transfers and can be a vector for resource exhaustion if not capped.
One often-overlooked aspect of DNS server hardening is disabling features that are not strictly necessary for your role. For instance, if your server is not authoritative for any zones, you might disable auth-nxdomain. This option, when enabled, causes the server to return an authoritative NXDOMAIN response for queries that don’t exist in its cache or zone data. While seemingly innocuous, in certain attack scenarios, it can provide a more consistent response that attackers might leverage. Disabling it can add a small layer of obscurity.
The next hurdle you’ll likely encounter after securing your DNS server is ensuring its availability during periods of high load, which might involve exploring load balancing and anycast DNS architectures.