Setting up BIND9 for Public DNS on RHEL9 #
This guide covers setting up BIND9/named as an authoritative public DNS server on RHEL9. Unlike internal DNS servers that provide recursive resolution, public authoritative DNS servers are responsible for answering queries about domains you own and control.
This setup includes security hardening measures such as proper logging and zone transfer restrictions to protect your DNS infrastructure from abuse.
All IP addresses, domain names, and server configurations in this guide are examples. Replace them with your actual values.
For the entirety of the guide we’ll be running every single command as root.
All the commands are intended only for RPM and dnf/yum based systems such as Red Hat, Fedora, CentOS etc.
Domain Registrar Configuration #
The first thing you need to do is configure your two DNS servers as glue records with your domain registrar:
- ns01.example.com (203.0.113.10)
- ns02.example.com (203.0.113.20)
Installation #
Installing BIND9 #
# Install BIND9 and utilities
dnf install bind bind-utils
# Start and enable the service
systemctl start named
systemctl enable named
# Check the service status
systemctl status named
Firewall Configuration #
Open the necessary ports for DNS services:
# Allow DNS traffic on both UDP and TCP
firewall-cmd --permanent --add-port=53/udp
firewall-cmd --permanent --add-port=53/tcp
# Reload firewall rules
firewall-cmd --reload
# Verify the configuration
firewall-cmd --list-all
Network Planning #
For this public DNS setup, I’m using:
- Primary DNS server: 203.0.113.10 (ns01.example.com)
- Secondary DNS server: 203.0.113.20 (ns02.example.com)
- Example domains:
- example.com
Main Configuration File #
The main BIND9 configuration file is /etc/named.conf. Here’s a complete public DNS configuration with security hardening:
//
// BIND9 Public DNS Configuration
// Authoritative-only DNS server with security hardening
//
options {
// Listen on specific interfaces - replace with your server's IP
listen-on port 53 { 127.0.0.1; 203.0.113.10; };
listen-on-v6 port 53 { ::1; };
// Directory for zone files
directory "/var/named";
// Log and statistics files
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";
// Allow queries from anyone
allow-query { any; };
// CRITICAL: Disable recursion for public authoritative servers
recursion no;
// Security settings
version "not currently available"; // Hide BIND version
allow-transfer { none; }; // Disable zone transfers by default
// Enable query logging for monitoring
querylog yes;
// Disable DNSSEC (enable if you plan to sign your zones)
dnssec-enable no;
dnssec-validation no;
// Process and key file locations
managed-keys-directory "/var/named/dynamic";
pid-file "/run/named/named.pid";
session-keyfile "/run/named/session.key";
// Include system crypto policies
include "/etc/crypto-policies/back-ends/bind.config";
};
// Comprehensive logging configuration
logging {
channel default_debug {
file "data/named.run";
severity dynamic;
};
// Zone transfer logging
channel zone_transfer_log {
file "data/transfer.log" versions 10 size 50m;
print-time yes;
print-category yes;
print-severity yes;
severity info;
};
// Blocked queries logging
channel security_log {
file "data/security.log" versions 10 size 50m;
severity info;
print-time yes;
print-category yes;
print-severity yes;
};
// Query logging
channel query_log {
file "data/query.log" versions 10 size 100m;
severity info;
print-time yes;
print-category yes;
print-severity yes;
};
// Category mappings
category notify { zone_transfer_log; };
category xfer-in { zone_transfer_log; };
category xfer-out { zone_transfer_log; };
category security { security_log; };
category queries { query_log; };
};
// Root hints
zone "." IN {
type hint;
file "named.ca";
};
// Include standard zones
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
// Your domain zones
zone "example.com" {
type master;
file "/var/named/example.com";
allow-transfer { 203.0.113.20; }; // Secondary DNS server
};
Zone Files #
Primary Domain Zone (example.com) #
Create /var/named/example.com:
$ORIGIN example.com.
$TTL 300
@ IN SOA ns01.example.com. admin.example.com. (
2025070301 ; serial (YYYYMMDDNN format)
21600 ; refresh after 6 hours
3600 ; retry after 1 hour
1209600 ; expire after 2 weeks
86400 ) ; minimum TTL of 1 day
; Name servers
IN NS ns01.example.com.
IN NS ns02.example.com.
; A records
www IN A 203.0.113.100
mail IN A 203.0.113.101
ftp IN A 203.0.113.102
ns01 IN A 203.0.113.10
ns02 IN A 203.0.113.20
; CNAME records for services
webmail IN CNAME mail.example.com.
blog IN CNAME www.example.com.
shop IN CNAME www.example.com.
api IN CNAME www.example.com.
cdn IN CNAME www.example.com.
support IN CNAME www.example.com.
docs IN CNAME www.example.com.
admin IN CNAME www.example.com.
portal IN CNAME www.example.com.
status IN CNAME www.example.com.
; Mail exchange records
@ IN MX 10 mail.example.com.
@ IN MX 20 backup-mail.example.com.
; TXT records
@ IN TXT "v=spf1 include:_spf.example.com ~all"
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"
mail._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC..."
@ IN TXT "example domain for documentation"
Configuration Validation #
Syntax Validation #
Always validate your configuration before restarting the service:
# Check named.conf syntax
named-checkconf /etc/named.conf
# Check individual zone files
named-checkzone example.com /var/named/example.com
Restart Service #
# Restart BIND9
systemctl restart named
# Check service status
systemctl status named
# View recent logs
journalctl -u named -f
DNS Testing #
Test your DNS configuration:
# Test local resolution
dig @127.0.0.1 example.com NS
dig @127.0.0.1 ns01.example.com A
# Test from external resolver
dig @8.8.8.8 example.com NS
dig @8.8.8.8 ns01.example.com A
# Test specific records
dig @127.0.0.1 www.example.com A
dig @127.0.0.1 example.com MX
dig @127.0.0.1 example.com TXT
Secondary DNS Server Setup #
For redundancy, configure a secondary DNS server:
Note: Complete the installation and firewall configuration steps on the secondary server (203.0.113.20) before proceeding.
On Secondary Server #
# Add zone configurations to named.conf
zone "example.com" {
type slave;
file "/var/named/slaves/example.com";
masters { 203.0.113.10; };
};
Create Slaves Directory #
# Create directory for slave zone files
mkdir -p /var/named/slaves
chown named:named /var/named/slaves
chmod 755 /var/named/slaves
Client Configuration #
Configure your clients to use your DNS server:
Linux #
Edit /etc/resolv.conf:
nameserver 203.0.113.10
nameserver 203.0.113.20
search example.com
Troubleshooting Common Issues #
DNS Not Resolving #
# Check if BIND9 is running
systemctl status named
# Check listening ports
netstat -tulnp | grep :53
# Test local resolution
dig @127.0.0.1 example.com NS
# Check firewall
firewall-cmd --list-ports