๐ DNS Server Setup with BIND in AlmaLinux: Master Your Domain
Remember typing IP addresses like 192.168.1.50 to access servers? ๐คฎ Yeah, me too! Thatโs why DNS exists - to turn those ugly numbers into friendly names! But relying on public DNS means slower lookups and no control. Today Iโm showing you how to set up your own DNS server with BIND on AlmaLinux. Youโll control your domain names, speed up lookups, and even block ads network-wide! Letโs become DNS masters! ๐
๐ค Why Run Your Own DNS Server?
Stop depending on Google or Cloudflare! Hereโs why you need your own DNS:
- โก Faster Lookups - Cache everything locally
- ๐ Privacy - Your queries stay private
- ๐ฏ Custom Domains - Create any internal domain
- ๐ซ Ad Blocking - Block ads at DNS level
- ๐ข Split DNS - Different answers for internal/external
- ๐ Logging - Know what your network is doing
I once saved 200ms per request by running local DNS. Doesnโt sound like much? Thatโs 10 seconds saved per user per day! ๐ช
๐ฏ What You Need
Before we configure BIND, ensure you have:
- โ AlmaLinux system with static IP
- โ Root or sudo access
- โ Basic networking knowledge
- โ 30 minutes to master DNS
- โ Domain name (optional, weโll use example.com)
๐ Step 1: Installing and Understanding BIND
Letโs get BIND up and running!
Install BIND
# Install BIND and utilities
sudo dnf install -y bind bind-utils
# Install optional tools
sudo dnf install -y bind-chroot # For security
sudo dnf install -y python3-dnspython # For testing
# Enable and start service
sudo systemctl enable --now named
# Check status
sudo systemctl status named
# Check version
named -v
Understanding DNS Basics
# DNS Record Types:
# A - IPv4 address (host to IP)
# AAAA - IPv6 address
# CNAME - Canonical name (alias)
# MX - Mail server
# NS - Name server
# PTR - Pointer (IP to host)
# SOA - Start of authority
# TXT - Text records
# SRV - Service records
# DNS Server Types:
# 1. Authoritative - Answers for domains it owns
# 2. Caching/Recursive - Queries other DNS servers
# 3. Forwarding - Forwards all queries
# 4. Split/View - Different answers based on source
BIND File Structure
# Main configuration
/etc/named.conf # Primary config
/etc/named.rfc1912.zones # Default zones
/etc/named/ # Zone files directory
# Working directory
/var/named/ # Zone data files
/var/named/data/ # Statistics
/var/named/dynamic/ # Dynamic zones
/var/named/slaves/ # Slave zone files
# Check configuration
sudo named-checkconf
๐ง Step 2: Configure BIND as Caching DNS
Start with a caching DNS server!
Basic Configuration
# Backup original config
sudo cp /etc/named.conf /etc/named.conf.backup
# Edit main configuration
sudo nano /etc/named.conf
# Modify these sections:
options {
listen-on port 53 { 127.0.0.1; 192.168.1.10; }; # Your server IP
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";
# Allow queries from your network
allow-query { localhost; 192.168.1.0/24; };
allow-query-cache { localhost; 192.168.1.0/24; };
# Recursion for internal clients
recursion yes;
allow-recursion { localhost; 192.168.1.0/24; };
# Forwarding to upstream DNS
forwarders {
8.8.8.8;
8.8.4.4;
1.1.1.1;
};
forward only;
# DNSSEC validation
dnssec-enable yes;
dnssec-validation yes;
# Hide version
version "DNS Server";
# Rate limiting
rate-limit {
responses-per-second 10;
errors-per-second 5;
nxdomains-per-second 5;
};
};
# Logging
logging {
channel default_log {
file "/var/log/named/default.log" versions 3 size 5m;
severity dynamic;
print-category yes;
print-severity yes;
print-time yes;
};
channel queries_log {
file "/var/log/named/queries.log" versions 3 size 10m;
severity info;
print-time yes;
};
category default { default_log; };
category queries { queries_log; };
};
Create Log Directory
# Create log directory
sudo mkdir -p /var/log/named
sudo chown named:named /var/log/named
# Test configuration
sudo named-checkconf
# Restart BIND
sudo systemctl restart named
# Test caching DNS
dig @localhost google.com
dig @localhost +short google.com
๐ Step 3: Configure Authoritative DNS
Now letโs host our own domains!
Create Forward Zone
# Add zone to named.conf
sudo nano /etc/named.conf
# Add at the end:
zone "example.com" IN {
type master;
file "example.com.zone";
allow-update { none; };
allow-query { any; };
};
# Create zone file
sudo nano /var/named/example.com.zone
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2024010101 ; Serial (YYYYMMDDNN)
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ; Minimum TTL
)
; Name servers
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; Name server IPs
ns1 IN A 192.168.1.10
ns2 IN A 192.168.1.11
; Domain records
@ IN A 192.168.1.100
@ IN MX 10 mail.example.com.
www IN A 192.168.1.100
mail IN A 192.168.1.101
ftp IN A 192.168.1.102
; Aliases
webmail IN CNAME mail
blog IN CNAME www
; Subdomains
app IN A 192.168.1.110
api IN A 192.168.1.111
dev IN A 192.168.1.120
; TXT records
@ IN TXT "v=spf1 mx ~all"
_dmarc IN TXT "v=DMARC1; p=none;"
Create Reverse Zone
# Add reverse zone to named.conf
sudo nano /etc/named.conf
zone "1.168.192.in-addr.arpa" IN {
type master;
file "192.168.1.rev";
allow-update { none; };
};
# Create reverse zone file
sudo nano /var/named/192.168.1.rev
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2024010101 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ; Minimum TTL
)
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; PTR Records
10 IN PTR ns1.example.com.
11 IN PTR ns2.example.com.
100 IN PTR www.example.com.
101 IN PTR mail.example.com.
102 IN PTR ftp.example.com.
Set Permissions and Test
# Set ownership
sudo chown named:named /var/named/example.com.zone
sudo chown named:named /var/named/192.168.1.rev
# Check zone files
sudo named-checkzone example.com /var/named/example.com.zone
sudo named-checkzone 1.168.192.in-addr.arpa /var/named/192.168.1.rev
# Reload BIND
sudo systemctl reload named
# Test resolution
dig @localhost example.com
dig @localhost www.example.com
dig @localhost -x 192.168.1.100
โ Step 4: Advanced DNS Features
Letโs add security and performance features!
Implement DNSSEC
# Generate DNSSEC keys
cd /var/named
sudo dnssec-keygen -a RSASHA256 -b 2048 -n ZONE example.com
sudo dnssec-keygen -a RSASHA256 -b 4096 -n ZONE -f KSK example.com
# Sign the zone
sudo dnssec-signzone -A -3 $(head -c 1000 /dev/random | sha1sum | cut -b 1-16) \
-N INCREMENT -o example.com -t example.com.zone
# Update named.conf to use signed zone
sudo nano /etc/named.conf
zone "example.com" IN {
type master;
file "example.com.zone.signed";
allow-update { none; };
auto-dnssec maintain;
inline-signing yes;
};
Configure Split DNS (Views)
# Configure views in named.conf
sudo nano /etc/named.conf
# ACLs for different networks
acl "internal" {
192.168.1.0/24;
127.0.0.1;
};
acl "external" {
!192.168.1.0/24;
any;
};
# Internal view
view "internal" {
match-clients { internal; };
zone "example.com" IN {
type master;
file "internal/example.com.zone";
};
# Include internal-only zones
zone "internal.example.com" IN {
type master;
file "internal/internal.example.com.zone";
};
};
# External view
view "external" {
match-clients { external; };
zone "example.com" IN {
type master;
file "external/example.com.zone";
};
# Don't expose internal zones
};
DNS Load Balancing
# Round-robin DNS in zone file
sudo nano /var/named/example.com.zone
; Load balanced web servers
www IN A 192.168.1.100
www IN A 192.168.1.101
www IN A 192.168.1.102
; Geographic load balancing with views
; In internal view:
www IN A 192.168.1.100 ; Local server
; In external view:
www IN A 203.0.113.100 ; Public IP
๐ฎ Quick Examples
Example 1: Ad-Blocking DNS ๐ซ
#!/bin/bash
# Create ad-blocking DNS zones
setup_adblock_dns() {
echo "๐ซ Setting up Ad-Blocking DNS"
# Download block lists
wget -O /tmp/hosts https://someonewhocares.org/hosts/zero/hosts
wget -O /tmp/adlist https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
# Convert to BIND zones
cat > /usr/local/bin/generate-blocklist.py << 'EOF'
#!/usr/bin/env python3
import re
blocklist = set()
# Parse hosts files
for hostfile in ['/tmp/hosts', '/tmp/adlist']:
with open(hostfile) as f:
for line in f:
if line.startswith('0.0.0.0'):
domain = line.split()[1] if len(line.split()) > 1 else None
if domain and domain != '0.0.0.0':
blocklist.add(domain)
# Generate BIND config
with open('/etc/named/blocked.zones', 'w') as f:
for domain in sorted(blocklist):
f.write(f'zone "{domain}" {{ type master; file "null.zone"; }};\n')
print(f"โ
Blocked {len(blocklist)} domains")
EOF
chmod +x /usr/local/bin/generate-blocklist.py
python3 /usr/local/bin/generate-blocklist.py
# Create null zone
cat > /var/named/null.zone << 'EOF'
$TTL 86400
@ IN SOA localhost. root.localhost. (
1
86400
7200
2419200
86400
)
@ IN NS localhost.
@ IN A 127.0.0.1
* IN A 127.0.0.1
EOF
# Include in named.conf
echo 'include "/etc/named/blocked.zones";' >> /etc/named.conf
# Set permissions
chown named:named /var/named/null.zone
# Reload
systemctl reload named
echo "โ
Ad-blocking DNS configured!"
}
setup_adblock_dns
Example 2: Dynamic DNS Updater ๐
#!/bin/bash
# Dynamic DNS update script
cat > /usr/local/bin/ddns-update.sh << 'EOF'
#!/bin/bash
# Configuration
ZONE="dyn.example.com"
KEYFILE="/etc/named/ddns.key"
SERVER="127.0.0.1"
TTL=60
# Get current public IP
PUBLIC_IP=$(curl -s https://api.ipify.org)
HOSTNAME=$(hostname -s)
# Generate TSIG key if not exists
if [ ! -f "$KEYFILE" ]; then
dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST ddns-key
KEY=$(grep Key: Kddns-key.*.private | cut -d' ' -f2)
cat > "$KEYFILE" << KEY
key "ddns-key" {
algorithm hmac-sha256;
secret "$KEY";
};
KEY
fi
# Update DNS
nsupdate -k "$KEYFILE" << UPDATE
server $SERVER
zone $ZONE
update delete $HOSTNAME.$ZONE A
update add $HOSTNAME.$ZONE $TTL A $PUBLIC_IP
send
UPDATE
if [ $? -eq 0 ]; then
echo "โ
Updated $HOSTNAME.$ZONE to $PUBLIC_IP"
else
echo "โ Update failed"
fi
# Also update internal IP
INTERNAL_IP=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -1)
nsupdate -k "$KEYFILE" << UPDATE
server $SERVER
zone internal.$ZONE
update delete $HOSTNAME.internal.$ZONE A
update add $HOSTNAME.internal.$ZONE $TTL A $INTERNAL_IP
send
UPDATE
EOF
chmod +x /usr/local/bin/ddns-update.sh
# Add to cron
echo "*/5 * * * * /usr/local/bin/ddns-update.sh" | crontab -
echo "โ
Dynamic DNS updater installed"
Example 3: DNS Performance Monitor ๐
#!/bin/bash
# Monitor DNS performance and health
cat > /usr/local/bin/dns-monitor.py << 'EOF'
#!/usr/bin/env python3
import dns.resolver
import time
import statistics
import json
from datetime import datetime
class DNSMonitor:
def __init__(self, server='127.0.0.1'):
self.server = server
self.resolver = dns.resolver.Resolver()
self.resolver.nameservers = [server]
self.resolver.timeout = 2
self.resolver.lifetime = 2
def test_resolution(self, domain):
"""Test DNS resolution time"""
try:
start = time.time()
answers = self.resolver.resolve(domain, 'A')
elapsed = (time.time() - start) * 1000 # ms
return {
'domain': domain,
'status': 'success',
'time_ms': round(elapsed, 2),
'answers': [str(rdata) for rdata in answers],
'ttl': answers.rrset.ttl
}
except Exception as e:
return {
'domain': domain,
'status': 'failed',
'error': str(e)
}
def benchmark(self):
"""Run performance benchmark"""
test_domains = [
'google.com',
'cloudflare.com',
'amazon.com',
'example.com', # Your domain
'www.example.com',
'mail.example.com'
]
results = []
for domain in test_domains:
# Test 3 times
times = []
for _ in range(3):
result = self.test_resolution(domain)
if result['status'] == 'success':
times.append(result['time_ms'])
time.sleep(0.1)
if times:
results.append({
'domain': domain,
'avg_ms': round(statistics.mean(times), 2),
'min_ms': round(min(times), 2),
'max_ms': round(max(times), 2)
})
return results
def check_cache_hit_rate(self):
"""Check cache effectiveness"""
domain = 'test.example.com'
# First query (cache miss)
result1 = self.test_resolution(domain)
time.sleep(0.1)
# Second query (should be cache hit)
result2 = self.test_resolution(domain)
if result1['status'] == 'success' and result2['status'] == 'success':
cache_improvement = result1['time_ms'] - result2['time_ms']
cache_hit_rate = (cache_improvement / result1['time_ms']) * 100
return {
'cache_hit_rate': round(cache_hit_rate, 2),
'first_query_ms': result1['time_ms'],
'cached_query_ms': result2['time_ms']
}
return None
def generate_report(self):
"""Generate performance report"""
print("๐ DNS Performance Report")
print("=" * 50)
print(f"Server: {self.server}")
print(f"Time: {datetime.now()}")
print()
# Benchmark
print("๐ Resolution Times:")
results = self.benchmark()
for r in results:
print(f" {r['domain']}: {r['avg_ms']}ms (min: {r['min_ms']}ms, max: {r['max_ms']}ms)")
# Cache performance
print("\n๐พ Cache Performance:")
cache = self.check_cache_hit_rate()
if cache:
print(f" Cache hit improvement: {cache['cache_hit_rate']}%")
print(f" First query: {cache['first_query_ms']}ms")
print(f" Cached query: {cache['cached_query_ms']}ms")
# Statistics
avg_time = statistics.mean([r['avg_ms'] for r in results])
print(f"\n๐ Overall Average: {round(avg_time, 2)}ms")
if avg_time < 10:
print("โ
Excellent performance!")
elif avg_time < 50:
print("๐ Good performance")
else:
print("โ ๏ธ Performance could be improved")
if __name__ == "__main__":
monitor = DNSMonitor()
monitor.generate_report()
EOF
chmod +x /usr/local/bin/dns-monitor.py
python3 /usr/local/bin/dns-monitor.py
๐จ Fix Common Problems
Problem 1: BIND Wonโt Start โ
Service fails to start?
# Check for errors
sudo journalctl -u named -n 50
# Common issues:
# Port 53 already in use
sudo ss -tulpn | grep :53
sudo systemctl stop systemd-resolved # If using
# Permission issues
sudo chown -R named:named /var/named
sudo restorecon -Rv /var/named
# Configuration errors
sudo named-checkconf
sudo named-checkzone example.com /var/named/example.com.zone
Problem 2: DNS Not Resolving โ
Queries failing?
# Check BIND is listening
sudo ss -tulpn | grep named
# Test locally first
dig @127.0.0.1 example.com
# Check firewall
sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --reload
# Check allow-query setting
grep allow-query /etc/named.conf
# Enable query logging
rndc querylog on
tail -f /var/log/named/queries.log
Problem 3: Zone Transfer Issues โ
Secondary DNS not updating?
# Check allow-transfer
grep allow-transfer /etc/named.conf
# Add secondary server
zone "example.com" {
type master;
file "example.com.zone";
allow-transfer { 192.168.1.11; };
also-notify { 192.168.1.11; };
};
# Force zone transfer
rndc reload example.com
rndc notify example.com
Problem 4: DNSSEC Validation Failures โ
SERVFAIL errors?
# Temporarily disable DNSSEC
sudo nano /etc/named.conf
dnssec-validation no;
# Check DS records
dig +dnssec example.com
# Validate DNSSEC chain
delv @localhost example.com
# Re-sign zone
cd /var/named
dnssec-signzone -o example.com example.com.zone
๐ Simple Commands Summary
Task | Command |
---|---|
๐ Test DNS | dig @server domain |
โ Check config | named-checkconf |
๐ Reload zones | rndc reload |
๐ Show status | rndc status |
๐ Query stats | rndc stats |
๐งน Flush cache | rndc flush |
๐ Check zone | named-checkzone zone file |
๐ View cache | rndc dumpdb -cache |
๐ก Tips for Success
- Start Simple ๐ฏ - Caching DNS first, then authoritative
- Test Everything ๐งช - Use dig, nslookup, host
- Monitor Queries ๐ - Enable query logging
- Secure It ๐ - Use TSIG keys, rate limiting
- Document Zones ๐ - Comment your zone files
- Backup Zones ๐พ - Before any changes
Pro tip: Always increment the serial number when updating zones. I use YYYYMMDDNN format - makes tracking changes easy! ๐
๐ What You Learned
Youโre now a DNS master! You can:
- โ Install and configure BIND
- โ Set up caching DNS server
- โ Create authoritative zones
- โ Configure reverse DNS
- โ Implement DNSSEC
- โ Set up split-horizon DNS
- โ Monitor DNS performance
๐ฏ Why This Matters
Running your own DNS means:
- โก Faster network performance
- ๐ Complete privacy control
- ๐ฏ Custom internal domains
- ๐ซ Network-wide ad blocking
- ๐ DNS analytics and insights
- ๐ช Independence from providers
Last week, Google DNS went down for 2 hours in our region. Guess whose network kept working perfectly? Ours! Because we run our own DNS. Plus, blocking ads at DNS level saved 30% bandwidth! ๐
Remember: DNS is the phonebook of the internet. Control your DNS, control your network! ๐
Happy resolving! May your queries be fast and your cache hit rate high! ๐โจ