Installing BIND DNS on Fedora Server: Smooth Operation with SELinux and firewalld

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

The Real-World Problem: When /etc/hosts Reaches Its Limits

In a homelab or corporate internal network with 20–30 virtual machines, containers, and switches, typing static IPs like 192.168.1.45 quickly becomes painful. Admins often use a quick fix by hardcoding entries into the /etc/hosts file on every machine. This workaround works fine when you have fewer than 5 devices. Beyond that threshold, changing the IP of a single K8s node or GitLab server turns into a nightmare of resynchronizing dozens of workstations.

The cleanest way out is setting up a dedicated Local DNS Server. Fedora Server is an excellent choice for lab infrastructure thanks to its modern kernel and rapidly updated packages. Paired with Fedora, BIND9 (the named daemon) remains the industry standard: lightweight, rock-solid, and extensively documented.

The biggest hurdle lies with SELinux running in Enforcing mode. With the wrong context label or incorrect group permissions, BIND will refuse to load zone files without providing clear error logs. The guide below will help you build a proper DNS infrastructure without needing to disable SELinux.

The Three Core Building Blocks of Local DNS

Before writing configurations, you need to clearly distinguish three concepts:

  • Forward Lookup Zone: Resolves hostnames (FQDN) to IP addresses. For example: querying gitlab.lab.local returns 192.168.1.50.
  • Reverse Lookup Zone: Looks up hostnames from IP addresses via PTR records. This is crucial for debugging centralized logs, verifying SSH sources, and preventing false alarms from network monitoring tools.
  • Forwarders: For public Internet domains like github.com, the internal server does not resolve the root zones directly. Instead, it forwards the query to upstream DNS servers (such as Cloudflare 1.1.1.1 or Google 8.8.8.8). The results are cached locally, cutting query latency from ~35ms down to under 2ms for subsequent requests.

Hands-On: Configuring BIND on Fedora Server

Assumed parameters for the lab environment:

  • Fedora DNS Server IP: 192.168.1.10
  • Internal domain: lab.local
  • Subnet: 192.168.1.0/24

Step 1: Install BIND and bind-utils

Install the BIND service packages and network troubleshooting utilities (which include dig, nslookup, and named-checkconf):

sudo dnf install -y bind bind-utils

Step 2: Modify the Main /etc/named.conf File

Always back up the original file before making changes:

sudo cp /etc/named.conf /etc/named.conf.orig
sudo nano /etc/named.conf

Locate the options block and update the listening IP ranges and query permissions. Then add the declarations for two new zones at the bottom of the file:

options {
    # Listen on port 53 on localhost and the server's static IP
    listen-on port 53 { 127.0.0.1; 192.168.1.10; };
    listen-on-v6 port 53 { ::1; };
    directory 	"/var/named";
    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 clients within the 192.168.1.0/24 range to query DNS
    allow-query     { localhost; 192.168.1.0/24; };

    # Enable recursion to resolve external Internet websites
    recursion yes;

    # Forward external queries to Cloudflare and Google
    forwarders {
        1.1.1.1;
        8.8.8.8;
    };

    dnssec-validation yes;
    managed-keys-directory "/var/named/dynamic";
    pid-file "/run/named/named.pid";
    session-keyfile "/run/named/session.key";
};

# Forward zone for lab.local
zone "lab.local" IN {
    type primary;
    file "lab.local.zone";
    allow-update { none; };
};

# Reverse zone for subnet 192.168.1.0/24
zone "1.168.192.in-addr.arpa" IN {
    type primary;
    file "192.168.1.rev";
    allow-update { none; };
};

Step 3: Create Zone Files for Forward and Reverse Lookups

Create the forward resolution file at /var/named/lab.local.zone:

sudo nano /var/named/lab.local.zone

Define the SOA record, NS record, and specific A records:

$TTL 86400
@   IN  SOA     ns1.lab.local. admin.lab.local. (
        2026110601  ; Serial: YYYYMMDDNN (increment this number whenever editing the file)
        3600        ; Refresh (1 hour)
        1800        ; Retry (30 minutes)
        604800      ; Expire (1 week)
        86400 )     ; Minimum TTL (1 day)

; Authoritative DNS server
@       IN  NS      ns1.lab.local.

; Device mapping records
ns1     IN  A       192.168.1.10
router  IN  A       192.168.1.1
gitlab  IN  A       192.168.1.50
k8s     IN  A       192.168.1.60

Next, create the reverse resolution file at /var/named/192.168.1.rev:

sudo nano /var/named/192.168.1.rev

Add the corresponding PTR records. Note: always append a trailing dot . to the FQDN to prevent BIND from automatically appending the root domain:

$TTL 86400
@   IN  SOA     ns1.lab.local. admin.lab.local. (
        2026110601  ; Serial
        3600
        1800
        604800
        86400 )

@       IN  NS      ns1.lab.local.

; Host octet (last number of the IP address)
10      IN  PTR     ns1.lab.local.
1       IN  PTR     router.lab.local.
50      IN  PTR     gitlab.lab.local.
60      IN  PTR     k8s.lab.local.

Step 4: Configure File Ownership and SELinux Contexts

This is the critical step on RHEL/Fedora-family systems. The named daemon runs under an unprivileged service account. If zone files have incorrect labels, SELinux will immediately block read access.

# Set file ownership to the named group with 640 read permissions
sudo chown root:named /var/named/lab.local.zone /var/named/192.168.1.rev
sudo chmod 640 /var/named/lab.local.zone /var/named/192.168.1.rev

# Restore default security contexts according to system policy
sudo restorecon -v /var/named/lab.local.zone
sudo restorecon -v /var/named/192.168.1.rev

Verify the labels using ls -Z /var/named/. A valid context must display the object type named_zone_t:

ls -Z /var/named/lab.local.zone
# Expected output: system_u:object_r:named_zone_t:s0 /var/named/lab.local.zone

Step 5: Verify Syntax and Open Firewall Ports

Don’t rush to start the service just yet. Run the syntax verification tools first to catch any typos:

# Check the overall configuration file (no output means the file is valid)
sudo named-checkconf

# Validate the syntax of each zone file
sudo named-checkzone lab.local /var/named/lab.local.zone
sudo named-checkzone 1.168.192.in-addr.arpa /var/named/192.168.1.rev

Both checkzone commands should return an OK status along with the serial number. You can now safely enable the service and open port 53 (both UDP and TCP) in firewalld:

# Enable BIND to start on boot
sudo systemctl enable --now named

# Allow DNS service permanently through the firewall
sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --reload

Step 6: Validate Queries with dig

From any workstation on the LAN or directly on the server, use dig pointing directly to IP 192.168.1.10 to verify resolution:

# 1. Test forward lookup
dig @192.168.1.10 gitlab.lab.local +short
# Output: 192.168.1.50

# 2. Test reverse lookup
dig @192.168.1.10 -x 192.168.1.50 +short
# Output: gitlab.lab.local.

# 3. Test Internet resolution capability
dig @192.168.1.10 fedoraproject.org +short

If the command returns the expected IP and the status flag is NOERROR, your DNS server is ready for production.

Summary

Setting up BIND on Fedora Server demands strict attention to file permissions and SELinux contexts. In return, you get an ultra-reliable, enterprise-grade Linux setup. Once your dedicated DNS server is live, simply configure 192.168.1.10 as the primary DNS server in your router’s DHCP settings. From that moment on, any new device joining your LAN will automatically resolve internal hosts by name without ever needing to touch a hosts file again.

Share: