NCAE: DNS Server Operations
BIND configuration, zone files, and port forwarding for DNS
DNS Server Operations
DNS (Domain Name System) translates human-readable names like google.com into IP addresses like 142.250.80.46. Without DNS, you’d have to memorize the IP address of every website you visit.
This page teaches you how DNS works from the ground up, how to configure a DNS server using BIND, how to write zone files, and how to get DNS scoring in competition. DNS carries a 2x scoring weight in NCAE CyberGames — getting it right matters.
Prerequisites: You should be comfortable with file editing and service management from Weeks 1-2.
1. What DNS Does
Every time you type a URL in your browser, your computer needs to convert that name to an IP address before it can connect. DNS is the system that does this conversion.
Think of it like a phone book. You look up “Pizza Hut” and get a phone number. DNS looks up “google.com” and gets 142.250.80.46.
# Ask DNS for an IP address
dig google.com
# Short answer only
dig +short google.com
142.250.80.46
# Ask a specific DNS server
dig @8.8.8.8 google.com
There are several types of DNS records, each serving a different purpose:
| Record Type | Purpose | Example |
|---|---|---|
| A | Maps a name to an IPv4 address | web.example.com → 10.0.5.10 |
| AAAA | Maps a name to an IPv6 address | web.example.com → 2001:db8::1 |
| CNAME | Alias — points one name to another | www.example.com → web.example.com |
| MX | Mail server for the domain | example.com → mail.example.com |
| NS | Nameserver for the domain | example.com → ns1.example.com |
| SOA | Start of Authority — metadata about the zone | Serial number, refresh intervals |
| PTR | Reverse lookup — maps IP to name | 10.0.5.10 → web.example.com |
Checkpoint: You run dig example.com and get no answer. You run dig @8.8.8.8 example.com and get an answer. What does this tell you?
Your local DNS resolver (the one your system is configured to use in /etc/resolv.conf) is either down or can’t reach the authoritative server. Google’s DNS (8.8.8.8) works fine, so the domain’s DNS records exist — the problem is your resolver, not the domain. Check /etc/resolv.conf to see which DNS server your system is using and whether it’s reachable.
2. How DNS Resolution Works
When you type google.com into your browser, your computer doesn’t just ask one DNS server. It triggers a chain of queries across multiple servers:
Your Computer
↓ "What's the IP for google.com?"
Recursive Resolver (your ISP or 8.8.8.8)
↓ "Who handles .com?"
Root Nameserver (one of 13 worldwide)
↓ "Ask the .com TLD server at 192.5.6.30"
TLD Nameserver (.com)
↓ "Ask Google's nameserver at 216.239.32.10"
Authoritative Nameserver (ns1.google.com)
↓ "google.com is 142.250.80.46"
Back to your computer
The recursive resolver does all the legwork. Your computer asks it one question, and it chases the answer through the hierarchy. Once it gets the answer, it caches it so the next query for the same name is instant.
Key terms:
- Recursive resolver: The server that chases queries through the hierarchy (8.8.8.8, your ISP’s DNS)
- Authoritative nameserver: The server that has the definitive answer for a domain (“I am the authority for example.com”)
- Root nameserver: The starting point of the hierarchy (13 root servers, labeled a.root-servers.net through m.root-servers.net)
- TLD nameserver: Handles a top-level domain (.com, .org, .net)
- TTL (Time to Live): How long a cached answer is valid (in seconds)
In NCAE competition, you run an authoritative nameserver. The scoring engine queries your server for records in your domain, and your server must return the correct answers.
3. BIND — The DNS Server
BIND (Berkeley Internet Name Domain) is the most widely used DNS server software. The daemon is called named (pronounced “name-dee”). Configuration lives in /etc/named.conf (RHEL/CentOS) or /etc/bind/named.conf (Debian/Ubuntu).
Install BIND
# Debian/Ubuntu
sudo apt install bind9 bind9-utils
# RHEL/CentOS
sudo dnf install bind bind-utils
Service management
sudo systemctl start named # Start the DNS server
sudo systemctl enable named # Start on boot
sudo systemctl status named # Check if it's running
sudo systemctl restart named # Restart after config changes
# Or use rndc for graceful operations
sudo rndc reload # Reload zone files without restart
sudo rndc status # Check server status
BIND listens on port 53 (both TCP and UDP). UDP handles normal queries. TCP handles zone transfers and responses larger than 512 bytes.
# Verify BIND is listening
ss -tulnp | grep :53
Checkpoint: You start named but ss shows nothing listening on port 53. What should you check?
Check the listen-on directive in named.conf. If it says listen-on { 127.0.0.1; };, BIND only listens on localhost and won’t accept queries from other machines. Change it to listen-on { any; }; or listen-on { 127.0.0.1; 10.0.5.1; }; (replace with your server’s IP). Then reload: sudo rndc reload. Also check journalctl -u named -e for startup errors.
4. Zone Files
A zone file is the database for one domain. It maps names to IP addresses (and other records) for everything in that domain. Zone files live in /var/named/ (RHEL) or /var/cache/bind/ (Debian).
Anatomy of a zone file
Here’s a complete zone file for team5.cyber.local with annotations:
$TTL 86400 ; Default TTL: 24 hours (in seconds)
; SOA record: Start of Authority
; Defines the primary nameserver and admin contact
@ IN SOA ns1.team5.cyber.local. admin.team5.cyber.local. (
2026032701 ; Serial (YYYYMMDDNN format)
3600 ; Refresh: how often secondaries check for updates
900 ; Retry: how long before retrying a failed refresh
604800 ; Expire: when secondaries stop answering if primary is gone
86400 ; Minimum TTL for negative responses
)
; NS records: nameservers for this domain
@ IN NS ns1.team5.cyber.local.
; A records: name → IP address
ns1 IN A 10.0.5.1
web IN A 10.0.5.10
mail IN A 10.0.5.20
db IN A 10.0.5.30
ftp IN A 10.0.5.40
; CNAME records: aliases
www IN CNAME web.team5.cyber.local.
; MX records: mail servers (priority number, lower = preferred)
@ IN MX 10 mail.team5.cyber.local.
Zone file rules
- Every name that doesn’t end with a dot (
.) gets the zone name appended. Sowebbecomesweb.team5.cyber.local.butweb.team5.cyber.local.(with the trailing dot) is absolute. @means the zone name itself (the domainteam5.cyber.local.).- The serial number must increase every time you edit the zone. BIND uses it to detect changes. The convention
YYYYMMDDNN(date + a two-digit counter) makes it easy to increment. - Semicolons (
;) start comments.
Common zone file mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| Missing trailing dot on absolute names | web.team5.cyber.local becomes web.team5.cyber.local.team5.cyber.local. |
Always add the trailing dot: web.team5.cyber.local. |
| Serial not incremented | Changes don’t take effect after reload | Increment the serial and rndc reload |
| Tab/space issues | Parse errors | Use tabs between columns consistently |
| Missing SOA record | Zone won’t load at all | Every zone must start with SOA |
Checkpoint: You add an A record for "api" pointing to 10.0.5.50, reload BIND, but dig @localhost api.team5.cyber.local returns NXDOMAIN. What went wrong?
Check three things: (1) Did you increment the serial number? If not, rndc reload won’t pick up the change. (2) Run named-checkzone team5.cyber.local /var/named/team5.cyber.local.zone to check for syntax errors in the zone file. (3) Make sure the A record line has correct formatting: api IN A 10.0.5.50 with tabs between fields.
5. named.conf
The main configuration file tells BIND which zones to serve, where the zone files are, and who can query the server.
Minimal working configuration
options {
listen-on port 53 { any; };
listen-on-v6 port 53 { any; };
directory "/var/named"; // Where zone files live
allow-query { any; }; // Who can query this server
recursion no; // Don't resolve for others (authoritative only)
dnssec-validation auto;
};
zone "team5.cyber.local" IN {
type master;
file "team5.cyber.local.zone"; // Zone file path (relative to directory)
};
Key directives
| Directive | Purpose |
|---|---|
listen-on |
Which IP addresses BIND listens on. Use any for competition. |
allow-query |
Who can send queries. Use any to let the scoring engine reach you. |
recursion |
Whether this server will recursively resolve domains it doesn’t own. Set no for authoritative-only servers. |
directory |
Base directory for zone files. |
forwarders |
If recursion is on, forward queries to these servers (e.g., 8.8.8.8). |
Zone block types
// Master zone: this server owns the zone
zone "team5.cyber.local" IN {
type master;
file "team5.cyber.local.zone";
};
// Forward zone: forward queries for this domain to another server
zone "other.local" IN {
type forward;
forwarders { 10.0.5.100; };
};
After editing, always validate before restarting:
# Check named.conf syntax
named-checkconf
# Check a zone file
named-checkzone team5.cyber.local /var/named/team5.cyber.local.zone
Both commands produce no output on success. Any output means errors that must be fixed.
6. Internal vs External DNS
This is the #1 reason DNS scoring fails in NCAE competition, and it confuses almost every team the first time.
The network layout
┌──────────────────┐
Scoring Engine │ Router │ Your DNS Server
(External: 172.16.x.x) ──▶ External IP │ (Internal: 10.0.5.1)
│ 172.16.5.1 │
│ │
│ Internal IP ────│──▶ Port 53 on 10.0.5.1
│ 10.0.5.254 │
└──────────────────┘
Your DNS server sits on an internal network (10.0.5.x). The scoring engine sits on an external network (172.16.x.x). The scoring engine queries your router’s external IP on port 53, expecting a DNS response. But your DNS server is on the internal network.
Port forwarding bridges the gap. The router must forward incoming port 53 traffic from its external IP to your internal DNS server:
# On the router: forward port 53 UDP and TCP to internal DNS server
sudo iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to-destination 10.0.5.1:53
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 53 -j DNAT --to-destination 10.0.5.1:53
# Also need to allow the forwarded traffic through
sudo iptables -A FORWARD -p udp -d 10.0.5.1 --dport 53 -j ACCEPT
sudo iptables -A FORWARD -p tcp -d 10.0.5.1 --dport 53 -j ACCEPT
# Enable IP forwarding (if not already on)
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
Testing the chain
# Test from the DNS server itself (should always work)
dig @localhost team5.cyber.local
# Test from another internal machine
dig @10.0.5.1 team5.cyber.local
# Test from outside (simulates the scoring engine)
dig @172.16.5.1 team5.cyber.local
If the first works but the third doesn’t, the problem is port forwarding. If neither works, the problem is BIND itself.
Checkpoint: dig @localhost works perfectly, dig @10.0.5.1 from another internal machine also works, but the scoring engine on 172.16.x.x can't reach your DNS. Where's the problem?
The problem is port forwarding on the router. Your DNS server is working correctly (proven by localhost and internal tests). The external scoring engine’s queries arrive at the router but aren’t being forwarded to 10.0.5.1. Check: (1) Are the PREROUTING iptables rules in place? (2) Is IP forwarding enabled? (cat /proc/sys/net/ipv4/ip_forward — must be 1) (3) Are FORWARD rules allowing the traffic? (4) Is the router’s firewall blocking port 53 inbound?
7. Diagnostic Commands
Your complete DNS diagnostic toolkit:
# Check named.conf syntax (no output = valid)
named-checkconf
# Check zone file syntax
named-checkzone team5.cyber.local /var/named/team5.cyber.local.zone
# Query your own DNS server
dig @localhost team5.cyber.local
dig @localhost web.team5.cyber.local
# Query with full detail (useful for debugging)
dig @localhost team5.cyber.local +noall +answer +authority
# Check what's listening on port 53
ss -tulnp | grep :53
# Reload zones without restarting (faster, no downtime)
sudo rndc reload
# Check BIND status
sudo rndc status
systemctl status named
# Check the BIND log for errors
journalctl -u named -e
tail -50 /var/log/messages | grep named # RHEL
tail -50 /var/log/syslog | grep named # Debian
Common dig output explained
; <<>> DiG 9.18.28 <<>> @localhost web.team5.cyber.local
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;; ANSWER SECTION:
web.team5.cyber.local. 86400 IN A 10.0.5.10
- status: NOERROR — Query succeeded. The record exists.
- status: NXDOMAIN — The name doesn’t exist in the zone.
- status: SERVFAIL — The server encountered an error (bad zone file, can’t load zone).
- status: REFUSED — The server refused your query (
allow-querydoesn’t include your IP).
8. Competition DNS Playbook
Get DNS scoring within 10 minutes of competition start:
Minute 0-2: Verify BIND is running
systemctl status named
# Not running? Start it:
sudo systemctl start named && sudo systemctl enable named
# Check it's listening
ss -tulnp | grep :53
Minute 2-4: Check the config
named-checkconf
# Fix any errors. Common: missing semicolons, wrong file paths.
# Verify allow-query includes the scoring network
grep "allow-query" /etc/named.conf
# Should be: allow-query { any; };
Minute 4-6: Check the zone
# Find the zone file
grep "file" /etc/named.conf
# Validate it
named-checkzone team5.cyber.local /var/named/team5.cyber.local.zone
# Test a query
dig @localhost team5.cyber.local +short
Minute 6-8: Check port forwarding (router)
# On the router, verify DNAT rules exist
sudo iptables -t nat -L PREROUTING -n | grep 53
# If missing, add them
sudo iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to-destination 10.0.5.1:53
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 53 -j DNAT --to-destination 10.0.5.1:53
Minute 8-10: End-to-end test
# From the DNS server
dig @localhost team5.cyber.local
# From another internal machine
dig @10.0.5.1 team5.cyber.local
# From outside (if you can access the external network)
dig @172.16.5.1 team5.cyber.local
If all three return the correct answer, DNS is scoring.
Exercises
-
Zone File Construction: Write a zone file for
practice.localwith: an SOA record, two NS records, five A records (web, mail, db, ftp, api), one CNAME (www pointing to web), and one MX record. Validate it withnamed-checkzone. -
Broken DNS Lab: Set up BIND with a valid zone. Then introduce one error (wrong serial format, missing trailing dot,
listen-on { 127.0.0.1; },allow-query { none; }). Have a partner diagnose and fix it. -
Port Forwarding Practice: Set up two VMs — one as a router (two network interfaces) and one as a DNS server (internal only). Configure iptables DNAT on the router so external queries reach the internal DNS server. Test with
digfrom outside. -
Record Types Drill: Using
dig, querygoogle.comfor each record type: A, AAAA, MX, NS, SOA, TXT. Record what each returns and explain why that information is useful.
Resources
Practice: TryHackMe — DNS in Detail (search “DNS”) · OverTheWire — Natas (web/DNS challenges)
Reference: BIND 9 Administrator Reference Manual · DNS record types explained
Video: PowerCert — DNS explained · NetworkChuck — Build your own DNS server