DNS Authoritative and Recursive

Updated

October 31, 2026

DNS Authoritative and Recursive

Name resolution fails more often than forwarding. This chapter treats DNS roles—stub, recursive, authoritative—as lab-first architecture: who answers from zone data, who walks the tree, what gets cached, and how operators verify SOA/NS/glue and DNSSEC with dig. Tooling stays free Linux containers in Containerlab (mid-2026 path): CoreDNS for authoritative lab zones, Unbound for recursion/validation, bind-tools / dig for truth.

Learning goals

By the end of this chapter you can:

  • Draw the stub → recursive → authoritative path and predict failure symptoms
  • Distinguish authoritative answers (AA) from referrals and cached recursed answers
  • Explain SOA, NS, glue, and why in-bailiwick names need A/AAAA glue
  • Run a two-node lab: CoreDNS authoritative + Unbound recursive
  • Verify with dig (+trace, +norecurse, DNSSEC flags) and a short checklist
  • State what DNSSEC validates for an operator (and what it does not)

Why roles matter

Role Answers from Typical lab tool
Stub resolver Asks one or two configured servers /etc/resolv.conf
Recursive resolver Cache, else iterative queries toward authority Unbound
Authoritative server Local zone data (primary/secondary) CoreDNS, BIND, Knot, PowerDNS

Mixing roles on one daemon is possible (classic BIND “do everything”) but lab clarity comes from splitting: one box owns lab.example, another recurses for clients.

Application
  → stub (/etc/resolv.conf)
      → recursive (Unbound)
          → root / TLD / child authoritative (or lab fake root)

On the public Internet the recursive walks from the root. In a closed lab you either:

  • Forward Unbound to a reachable upstream recursive, or
  • Serve a private root / stub zones that point at your authoritative.

This chapter uses a private zone plus Unbound stub-zone / forward to the CoreDNS auth IP—no need for a full root mirror.

Concepts — recursive vs authoritative

Authoritative

Configured for a set of zones. Answers from local data without querying others for those names. Flags AA (Authoritative Answer) when the response is from that zone’s data.

Primary vs secondary: secondaries still answer authoritatively once they hold a zone copy (IXFR/AXFR). Delegation in the parent does not by itself make a server authoritative—you must configure the zone.

Recursive

Receives queries from stubs (RD=1). Satisfies from cache when possible; otherwise starts at a configured root or forwarder and follows referrals until an authoritative answer is obtained, then returns it to the client (usually with AA clear on the final client-facing message, AD set if DNSSEC validation succeeded—see below).

Stub

Library or OS resolver: almost no policy. Points at recursive IPs. Failures here look like “DNS is down” even when the path to 8.8.8.8 is fine but resolv.conf points at a black hole inside the namespace.

Concepts — SOA, NS, glue

Record Operator meaning
SOA Apex start-of-authority: primary name, contact, serial, refresh/retry/expire/minimum (negative TTL flavor)
NS Nameserver hostnames for the zone (apex) or delegation (parent)
Glue A/AAAA for NS hostnames that lie inside the child zone (in-bailiwick)—parent must supply them in additional data or resolution cannot start
Parent (.example) referral for lab.example:
  NS ns1.lab.example.
  Additional: ns1.lab.example. A 10.50.0.53   ← glue

Child apex (authoritative):
  lab.example. SOA ...
  lab.example. NS ns1.lab.example.
  ns1.lab.example. A 10.50.0.53

Parent NS/glue and child apex NS should agree. Mismatches are a classic “works from some resolvers” outage (parent-centric vs child-centric resolver behavior differs; treat consistency as hygiene).

Concepts — caching

Recursors cache positive answers and negative answers (NXDOMAIN / NODATA) within TTL / SOA minimum policies. Operator effects:

  • Lower TTL → faster change pickup, more query load on auth
  • Raise TTL carefully on stable records
  • Flush cache after lab zone edits when testing (unbound-control flush or restart)

Authoritative servers may also cache nothing for their own zones—they serve configured data. Do not expect editing a zone file on auth to update a recursive client until TTL expires or cache is flushed.

Concepts — DNSSEC at operator level

DNSSEC adds authenticity and integrity (not confidentiality). Chain of trust:

Trust anchor (root DS/DNSKEY in validator)
  → DS at parent points to child DNSKEY
    → RRSIGs over RRSets in the child

What to verify as an operator:

Check Tooling idea
Validator has a current trust anchor Unbound auto-trust-anchor-file / system anchors
AD bit on answers you care about dig +dnssec example.com from the recursive
Broken chain symptoms SERVFAIL from validating resolver; dig from non-validating still “works”
Delegation consistency Parent DS matches child DNSKEY; NS/glue still required

Lab note: signing a private lab.example needs a trust anchor you install in Unbound, or you practice DNSSEC against public zones from a recursive that can reach the Internet. Do not confuse AD (Authenticated Data from a validator) with AA (Authoritative Answer from a zone server).

Addressing plan (DNS roles lab)

  client ---- (L2) ---- recur ---- (L2) ---- auth
                 10.50.0.0/24
Node IP Role
auth 10.50.0.53/24 CoreDNS authoritative for lab.example
recur 10.50.0.54/24 Unbound recursive; stub-zone → auth
client 10.50.0.10/24 Stub → 10.50.0.54

Topology YAML

name: dns-roles

topology:
  nodes:
    auth:
      kind: linux
      image: alpine:3.20
      exec:
        - apk add --no-cache iproute2 iputils bind-tools
        # Install coredns binary via your preferred lab method
        # (apk package, static binary bind-mount, or distro package).
    recur:
      kind: linux
      image: alpine:3.20
      exec:
        - apk add --no-cache iproute2 iputils bind-tools unbound
    client:
      kind: linux
      image: alpine:3.20
      exec:
        - apk add --no-cache iproute2 iputils bind-tools
  links:
    - endpoints: ["auth:eth1", "recur:eth1"]
    - endpoints: ["recur:eth2", "client:eth1"]

Adjust images if you standardize on a CoreDNS-bearing image. The verification culture below is what matters more than the exact package name.

Authoritative zone (CoreDNS sketch)

Zone file lab.example.db:

$ORIGIN lab.example.
$TTL 60
@   IN SOA ns1.lab.example. hostmaster.lab.example. (
        2026100301 ; serial
        300        ; refresh
        60         ; retry
        86400      ; expire
        60         ; minimum
)
@       IN NS  ns1.lab.example.
ns1     IN A   10.50.0.53
www     IN A   10.50.0.80
api     IN A   10.50.0.81

Corefile:

lab.example:53 {
    file /etc/coredns/lab.example.db
    log
    errors
}

Bring-up sketch:

docker exec clab-dns-roles-auth ip addr add 10.50.0.53/24 dev eth1
docker exec clab-dns-roles-auth ip link set eth1 up
# copy zone + Corefile, then:
docker exec clab-dns-roles-auth coredns -conf /etc/coredns/Corefile

Direct auth checks (no recursion)

dig @10.50.0.53 www.lab.example A +norecurse
dig @10.50.0.53 lab.example SOA +norecurse
dig @10.50.0.53 lab.example NS +norecurse

Expect AA on answers for names in zone. Querying a name outside the zone should fail closed (REFUSED/NXDOMAIN per config)—auth should not recurse unless you deliberately enable it.

Recursive (Unbound) sketch

/etc/unbound/unbound.conf fragment:

server:
  interface: 0.0.0.0
  access-control: 10.50.0.0/24 allow
  do-ip6: no
  # For public DNSSEC labs, keep trust anchors enabled.
  # auto-trust-anchor-file: "/usr/share/dnssec-root/root.key"

stub-zone:
  name: "lab.example"
  stub-addr: 10.50.0.53

forward-zone:
  name: "."
  # Optional: only if auth/private root is not enough and egress exists
  # forward-addr: 1.1.1.1
docker exec clab-dns-roles-recur ip addr add 10.50.0.54/24 dev eth1
docker exec clab-dns-roles-recur ip link set eth1 up
docker exec clab-dns-roles-recur unbound -d -c /etc/unbound/unbound.conf &

Client stub:

docker exec clab-dns-roles-client sh -c 'cat >/etc/resolv.conf <<EOF
nameserver 10.50.0.54
search lab.example
EOF'

dig verification workflows

1. Happy path via recursive

docker exec clab-dns-roles-client dig www.lab.example A +noall +answer +comments

Predict: answer 10.50.0.80, from recursive (AA typically unset on the client-facing message).

2. Prove auth is the source of truth

dig @10.50.0.53 www.lab.example A +norecurse
dig @10.50.0.54 www.lab.example A

Change the zone A record / bump SOA serial / reload CoreDNS, then:

# may still see old data until TTL expires
dig @10.50.0.54 www.lab.example A
# flush and retest (command name varies by package)
docker exec clab-dns-roles-recur unbound-control flush www.lab.example

3. Trace mindset (public names)

When the recursive has real root access:

dig example.com +trace

Shows iterative referrals. In a closed lab, +trace may fail at root—use stub-zone labs instead of forcing a fake public trace.

4. Glue / NS consistency

dig @10.50.0.53 lab.example NS +norecurse
dig @10.50.0.53 ns1.lab.example A +norecurse

If you later add a parent zone, inspect additional section glue on the referral.

5. DNSSEC operator glance (public)

dig @10.50.0.54 cloudflare.com A +dnssec
# Look for ad flag when validation works; SERVFAIL when chain breaks

Compare with a non-validating query path if you temporarily disable validation—difference isolates DNSSEC vs reachability.

Predict → observe → fix

Scenario A — client NXDOMAIN for www.lab.example

  • Predict: wrong stub IP, auth down, or typo in zone
  • Observe: dig @10.50.0.54, then dig @10.50.0.53 +norecurse
  • Fix: restore auth listening; fix zone; fix resolv.conf

Scenario B — auth works, recursive SERVFAIL

  • Predict: stub-zone missing, auth unreachable from recur, or DNSSEC failure
  • Observe: dig @10.50.0.53 OK; dig @10.50.0.54 SERVFAIL; check Unbound log
  • Fix: routing between recur↔︎auth; stub-addr; trust anchor only if validating private zones incorrectly

Scenario C — “update not visible”

  • Predict: recursive cache / TTL
  • Observe: auth shows new A; recur shows old
  • Fix: wait TTL, lower lab TTL, or flush cache

Scenario D — AA vs AD confusion

  • Predict: operator expects AD on auth dig
  • Observe: dig @auth has AA, not AD
  • Fix: AD comes from a validating recursive, not from AA

Verification checklist

Check Intent
dig @auth SOA AA + expected serial
dig @auth NS + glue A Apex NS resolve
dig @recur name Client path works
Cache flush drill TTL behavior understood
resolv.conf Stub points at recur, not auth (unless intentional)
Logs CoreDNS/Unbound query logs on failure
DNSSEC (optional) AD on public name; document trust anchors

Common mistakes

Mistake Symptom
Pointing stubs at authoritative only Public names fail; private names OK
Enabling recursion on Internet-exposed auth Abuse / cache poisoning risk
Missing glue for in-bailiwick NS Delegation black hole
Forgetting SOA serial bump on secondaries Stale secondaries (when you add transfers later)
Assuming AA means DNSSEC-valid Conflates authority with authenticity
Lab resolv.conf → Docker host DNS unreachable Intermittent or total failure inside netns

Summary

  • Authoritative servers answer from zone data (AA); recursives cache and walk or stub toward authority
  • Stubs are configuration—wrong nameserver lines create false “network down” stories
  • SOA/NS/glue consistency is operational hygiene; dig +norecurse against auth is your ground truth
  • DNSSEC: verify AD via a validating recursive and keep DS/DNSKEY aligned—do not confuse with AA
  • Split CoreDNS (auth) and Unbound (recur) in Containerlab until the roles are muscle memory

Next edge labs can reuse this DNS path whenever VIP names and dual-home failovers need human-readable verification.