Overview

In this guide, you will:

  • Install and configure Unbound as your recursive DNS server.
  • Ensure Unbound bypasses any external resolvers and directly queries root servers.
  • Set up a secure and optimized Unbound configuration.
  • Configure the system to use your new DNS server exclusively.
  • Automate daily updates of the latest root hints.

Prerequisites

  • Rocky Linux 9 installed on your server.
  • Root or sudo privileges.
  • Basic familiarity with editing configuration files and using the command line.
  • An understanding of DNS concepts, particularly root hints and recursive queries.

1. Install Unbound

Install Unbound using the system’s package manager:

dnf install -y unbound

2. Configure Unbound for Direct Root Queries

Edit the main configuration file:

nano /etc/unbound/unbound.conf

Paste the following configuration:

server:
# Listen on all IPv4 and IPv6 interfaces
interface: 0.0.0.0
interface: ::0

# Allow queries from any IP address
access-control: 0.0.0.0/0 allow
access-control: ::0/0 allow

# Logging and security settings
verbosity: 1
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes

# DNS query handling
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
prefetch: yes
serve-expired: yes
unwanted-reply-threshold: 10000
do-not-query-localhost: no

# Performance and caching settings
num-threads: 4
rrset-cache-size: 200m
cache-max-ttl: 86400
cache-min-ttl: 3600

# Specify location of root hints file
root-hints: "/etc/unbound/root.hints"

Note: Do not include any forward-zone directives, as this forces Unbound to query the root servers directly.


3. Download the Latest Root Hints

Download the current root hints from IANA:

curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache
chown unbound:unbound /etc/unbound/root.hints
chmod 644 /etc/unbound/root.hints

4. Disable Hetzner’s Default DNS Resolvers

To prevent your system from using Hetzner’s resolvers, configure your resolver settings:

  1. Edit /etc/resolv.conf: echo "nameserver 127.0.0.1" > /etc/resolv.conf
  2. Lock the File: Prevent DHCP or NetworkManager from overwriting it: chattr +i /etc/resolv.conf
  3. If Using NetworkManager: Disable automatic DNS updates: nmcli connection modify eth0 ipv4.ignore-auto-dns yes systemctl restart NetworkManager

Tip: Replace eth0 with the actual network interface name if different.


5. Configure Firewall

Ensure that DNS queries on port 53 are allowed:

firewall-cmd --permanent --add-service=dns<br>firewall-cmd --reload

6. Enable and Start Unbound

Enable Unbound to start on boot and start the service immediately:

systemctl enable --now unbound<br>systemctl restart unbound

7. Test Your DNS Server

Verify that Unbound is working correctly by running:

dig @127.0.0.1 example.com
dig +trace @127.0.0.1 example.com
dig +dnssec @127.0.0.1 example.com

Check the output to ensure responses are coming directly from root servers and that DNSSEC validations pass.


8. Automate Daily Root Hints Refresh

To keep your root hints current, create a script that updates the hints daily and restarts Unbound.

a. Create the Update Script

Create a new script file:

nano /usr/local/bin/update-root-hints.sh

Insert the following content:

#!/bin/bash
ROOT_HINTS="/etc/unbound/root.hints"
TMP_FILE="/tmp/root.hints"

# Download the latest root hints silently
curl -s -o "$TMP_FILE" https://www.internic.net/domain/named.cache

# Check if the download was successful (file is not empty)
if [[ -s "$TMP_FILE" ]]; then
    mv "$TMP_FILE" "$ROOT_HINTS"
    chown unbound:unbound "$ROOT_HINTS"
    chmod 644 "$ROOT_HINTS"

    # Restart Unbound to apply the new root hints
    systemctl restart unbound

    echo "$(date) - Root hints updated successfully" >> /var/log/root-hints-update.log
else
    echo "$(date) - Failed to update root hints" >> /var/log/root-hints-update.log
fi

Save and exit.

b. Make the Script Executable

chmod +x /usr/local/bin/update-root-hints.sh

c. Set Up a Daily Cron Job

Edit the crontab for the root user:

crontab -e

Add the following line to run the script every day at 3 AM:

0 3 * * * /usr/local/bin/update-root-hints.sh

Save and exit.

d. Verify the Cron Job

List your cron jobs to confirm:

crontab -l

You can also manually run the script to test:

/usr/local/bin/update-root-hints.sh

Then check the log file:

cat /var/log/root-hints-update.log

Conclusion

You now have a fully independent and optimized recursive DNS server on Rocky Linux 9 that:

  • Uses Unbound to directly query root servers.
  • Avoids reliance on Hetzner’s resolvers.
  • Has robust performance and security configurations.
  • Automatically updates its root hints daily.

Feel free to further tweak performance settings (like num-threads or cache sizes) based on your hardware and usage patterns. Enjoy your perfectly built DNS server!

Last Update: March 2, 2025