How to Setup BIND as a Recursive DNS Resolver

How to Setup BIND as a Recursive DNS Resolver

A recursive DNS resolver is the workhorse of DNS infrastructure, handling client queries by recursively querying authoritative nameservers until it finds the answer. BIND 9 is the most widely deployed DNS software and makes an excellent choice for running your own resolver. In this guide, we'll set up a secure, high-performance BIND recursive resolver.


1. Why Run Your Own Resolver?

Running your own recursive resolver provides several benefits:

  • Privacy - Your DNS queries stay on your network instead of going to third-party providers
  • Performance - Local caching reduces latency for repeated queries
  • Control - You can implement custom filtering, logging, and policies
  • Reliability - No dependency on external DNS providers

2. Installation

Install BIND on your system:

Debian/Ubuntu:

sudo apt update
sudo apt install -y bind9 bind9utils bind9-doc dnsutils

RHEL/Rocky/AlmaLinux:

sudo dnf install -y bind bind-utils

Enable and start the service:

sudo systemctl enable --now named

3. Basic Configuration

The main configuration file is /etc/bind/named.conf (Debian) or /etc/named.conf (RHEL). We'll create a clean, security-focused configuration.

3.1. Define Access Control Lists

First, define which networks are allowed to use your resolver. Create or edit /etc/bind/named.conf.options:

// Define trusted networks
acl "trusted" {
    localhost;
    localnets;
    192.168.0.0/16;
    10.0.0.0/8;
    172.16.0.0/12;
};

options {
    directory "/var/cache/bind";

    // Network interface settings
    listen-on port 53 { any; };
    listen-on-v6 port 53 { any; };

    // Recursion settings - CRITICAL for security
    recursion yes;
    allow-recursion { trusted; };
    allow-query { trusted; };
    allow-query-cache { trusted; };

    // Disable zone transfers (not needed for resolver)
    allow-transfer { none; };

    // DNSSEC validation
    dnssec-validation auto;

    // Query source port randomization (security)
    query-source address * port *;

    // Performance tuning
    max-cache-size 256m;
    max-cache-ttl 86400;
    max-ncache-ttl 3600;

    // Prevent amplification attacks
    minimal-responses yes;

    // Hide version string
    version "not disclosed";

    // Logging
    querylog no;
};

3.2. Security Hardening

Add these security options to prevent common attacks:

options {
    // ... previous options ...

    // Prevent loopback/localhost attacks
    deny-answer-addresses {
        127.0.0.0/8;
        ::1/128;
    } except-from { "none"; };

    // Rate limiting to prevent abuse
    rate-limit {
        responses-per-second 10;
        window 5;
    };

    // Disable empty zones for internal networks
    empty-zones-enable yes;
};

4. Forwarding Configuration (Optional)

If you want your resolver to forward queries to upstream servers instead of performing full recursion, configure forwarders:

options {
    // ... previous options ...

    // Forward to upstream resolvers
    forwarders {
        1.1.1.1;        // Cloudflare
        8.8.8.8;        // Google
        9.9.9.9;        // Quad9
    };

    // Forward first, then recurse if forwarders fail
    forward first;
};

Use forward only; if you want to exclusively use forwarders without fallback recursion.


5. Logging Configuration

Set up proper logging for troubleshooting and security monitoring. Add to your configuration:

logging {
    channel default_log {
        file "/var/log/named/default.log" versions 3 size 5m;
        severity info;
        print-time yes;
        print-severity yes;
        print-category yes;
    };

    channel query_log {
        file "/var/log/named/query.log" versions 5 size 10m;
        severity info;
        print-time yes;
    };

    channel security_log {
        file "/var/log/named/security.log" versions 3 size 5m;
        severity warning;
        print-time yes;
        print-severity yes;
    };

    category default { default_log; };
    category queries { query_log; };
    category security { security_log; };
    category client { security_log; };
};

Create the log directory:

sudo mkdir -p /var/log/named
sudo chown bind:bind /var/log/named

6. Root Hints

Ensure you have current root hints for proper recursion. BIND usually includes these, but you can update them:

sudo curl -o /var/cache/bind/named.root https://www.internic.net/domain/named.root

Reference it in your configuration:

zone "." {
    type hint;
    file "/var/cache/bind/named.root";
};

7. Validate and Start

Check your configuration for errors:

sudo named-checkconf

If there are no errors, restart BIND:

sudo systemctl restart named

Check the status:

sudo systemctl status named
sudo journalctl -u named -f

8. Testing

Test your resolver from a client machine:

# Test basic resolution
dig @your-resolver-ip google.com

# Test DNSSEC validation
dig @your-resolver-ip dnssec-failed.org
# Should return SERVFAIL (DNSSEC validation failure)

# Check resolver statistics
dig @your-resolver-ip +short version.bind txt chaos

Test from the DNS-OARC port randomization tool:

dig @your-resolver-ip +short porttest.dns-oarc.net txt

You should see "GREAT" indicating good source port randomization.


9. Firewall Configuration

Allow DNS traffic through your firewall:

# UFW (Ubuntu)
sudo ufw allow from 192.168.0.0/16 to any port 53

# firewalld (RHEL)
sudo firewall-cmd --permanent --add-service=dns
sudo firewall-cmd --reload

# iptables
sudo iptables -A INPUT -p udp --dport 53 -s 192.168.0.0/16 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 53 -s 192.168.0.0/16 -j ACCEPT

10. Complete Configuration Example

Here's a complete /etc/bind/named.conf.options for reference:

acl "trusted" {
    localhost;
    localnets;
    192.168.0.0/16;
    10.0.0.0/8;
};

options {
    directory "/var/cache/bind";

    listen-on port 53 { any; };
    listen-on-v6 port 53 { any; };

    recursion yes;
    allow-recursion { trusted; };
    allow-query { trusted; };
    allow-query-cache { trusted; };
    allow-transfer { none; };

    dnssec-validation auto;
    query-source address * port *;

    max-cache-size 256m;
    max-cache-ttl 86400;
    max-ncache-ttl 3600;

    minimal-responses yes;
    version "not disclosed";

    deny-answer-addresses {
        127.0.0.0/8;
        ::1/128;
    } except-from { "none"; };

    rate-limit {
        responses-per-second 10;
        window 5;
    };
};

logging {
    channel default_log {
        file "/var/log/named/default.log" versions 3 size 5m;
        severity info;
        print-time yes;
        print-severity yes;
    };

    category default { default_log; };
};

11. Conclusion

You now have a secure, properly configured BIND recursive resolver. Key points to remember:

  • Never run an open resolver - Always restrict access with ACLs
  • Enable DNSSEC validation - Protects against cache poisoning
  • Use rate limiting - Prevents amplification attacks
  • Monitor logs - Watch for unusual activity
  • Keep BIND updated - Security patches are released regularly

In the next posts, we'll cover setting up BIND as an authoritative server, configuring DNSSEC, and implementing DNS over TLS/HTTPS.

Read more

HAProxy Monitoring with Prometheus: Complete Observability Guide

HAProxy Monitoring with Prometheus: Complete Observability Guide

Monitoring HAProxy is essential for maintaining reliable load balancing infrastructure. Prometheus provides powerful metrics collection, alerting capabilities, and seamless Grafana integration for visualizing HAProxy performance and health. Why Prometheus for HAProxy? Prometheus offers: * Pull-based metrics - Prometheus scrapes HAProxy metrics endpoints * Time-series database - Store historical data for trend analysis

By Patrick de Ruiter