Skip to content

RTFM · Networking

Build authoritative DNS from nothing

Build a two-server, IPv4-and-IPv6 authoritative DNS service on Saphira: delegation, zones, transfers, DNSSEC, mail records, firewalling, monitoring, backups, and recovery.

Saphira Linux dragon mascot

Draw the finished service before installing BIND

An authoritative nameserver answers only for zones it owns. A resolver answers arbitrary questions by following the DNS hierarchy for a client. They are different roles. This design uses two Saphira servers in distinct failure domains: losing ns0, its network, or its provider must not make example.net disappear.

Two authoritative Saphira servers
Registrar / parent zone
        │
        ├── NS ns0.example.net. with glue where required
        └── NS ns1.example.net. with glue where required
                 │
      ┌──────────┴──────────┐
      ▼                     ▼
Saphira ns0              Saphira ns1
primary authority        secondary authority
203.0.113.53             198.51.100.53
2001:db8:53::53          2001:db8:54::53
      │ NOTIFY and IXFR/AXFR │
      └──── signed example.net zone ────┘
                 │
          DS published at parent
The four DNS roles
RoleWhat it ownsWhat it must not be mistaken for
PrimaryEditable source zone and a controlled transfer policy.The only Internet authority.
SecondaryTransferred copy and independent public answers.A backup file on the same host.
Registrar / parentDelegation NS, in-bailiwick glue, and DS record.Something BIND can update by itself.
ResolverRecursive answers for trusted LAN/VPN clients.A public authority listener.

Prove it works: Real availability

Use separate hosts, networks, power paths, and preferably sites/providers. Two containers on one host, or two VMs behind one home router, can be useful for testing but are one failure domain.

Authoritative-only prevents an open resolver

A public authoritative server should answer DNS for its own zones, not resolve arbitrary Internet names for strangers. An accidentally open resolver can be abused for reflected traffic and makes the service harder to secure. Set the named policy and firewall deliberately; do not rely on a firewall alone to make a recursive configuration safe.

Small public-authority configuration shape
options {
    directory "/var/bind";
    recursion no;
    allow-query { any; };
    listen-on { 203.0.113.53; };
    listen-on-v6 { 2001:db8:53::53; };
};

zone "example.net" {
    type primary;
    file "/var/bind/primary/example.net.zone";
};
Prove the role boundary
# Your zone should return an authoritative answer: look for the aa flag.
dig @203.0.113.53 example.net SOA +noall +comments +answer

# An unrelated Internet name must not be served from cache by this host.
dig @203.0.113.53 www.iana.org A +recurse

# Healthy authority: NOERROR and aa for example.net.
# Healthy unrelated query: REFUSED, referral, or no recursive cache answer.

If you deliberately run a resolver too, use a separate listener or separate host and restrict recursion to named internal subnets. Never publish that recursive listener to the Internet.

Install, bind, and firewall UDP plus TCP 53

DNS is not UDP-only. UDP handles many ordinary queries, but TCP is required for zone transfers and fallback after a truncated answer. EDNS and DNSSEC make larger responses normal. Block TCP/53 and some resolvers will fail even though a simple UDP test looked healthy.

Install by the Saphira package and init model
# Inspect Saphira packages, then install the authority and diagnostics.
apk update
apk search -v bind
apk info -a bind
apk add bind bind-tools nftables

# Normal Saphira/OpenRC operation after configuration validation.
rc-service named status
rc-update add named default

# Systemd-selected Saphira only: prove the unit exists first.
systemctl status named.service
Minimal public DNS firewall
table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;
    ct state established,related accept
    iifname "lo" accept
    iifname "wan0" udp dport 53 accept
    iifname "wan0" tcp dport 53 accept
  }
}

# Replace wan0 with the actual public interface; inet applies to IPv4 and IPv6.
Prove listening and TCP fallback
ss -lntup '( sport = :53 )'
dig @ns0.example.net example.net SOA +tcp +noall +comments +answer

# Healthy: named owns UDP and TCP on intended IPv4 and IPv6 addresses.
# Bad: only loopback is unreachable from the Internet; an unintended wildcard
# listener can expose a management interface.

Create a zone: SOA, records, and serial lifecycle

A zone is a database. The SOA is its version and timing contract with secondaries and caches. Every served edit must increase the serial. A date-based serial such as 2026082702 is readable, but the essential rule is that it always increases.

Working forward-zone source
$TTL 3600
@ IN SOA ns0.example.net. hostmaster.example.net. (
  2026082701 ; serial: increase on every published change
  3600       ; refresh: secondary checks the primary
  600        ; retry: wait after a failed refresh
  1209600    ; expire: secondary stops serving an isolated stale copy
  3600       ; negative-cache TTL for NXDOMAIN or NODATA
)
  IN NS ns0.example.net.
  IN NS ns1.example.net.

ns0  IN A     203.0.113.53
ns0  IN AAAA  2001:db8:53::53
ns1  IN A     198.51.100.53
ns1  IN AAAA  2001:db8:54::53
www  IN A     203.0.113.10
www  IN AAAA  2001:db8:100::10
mail IN A     203.0.113.25
mail IN AAAA  2001:db8:100::25
@    IN MX 10 mail.example.net.
_alias IN CNAME www.example.net.
_autodiscover._tcp IN SRV 0 5 443 www.example.net.
@    IN CAA 0 issue "letsencrypt.org"
@    IN TXT "v=spf1 mx -all"
Every ordinary public record
TypePurposeImportant constraint
AName to IPv4 address.Does not supply IPv6.
AAAAName to IPv6 address.Test the full IPv6 path before publishing.
CNAMEAlias one name to another.Cannot coexist with other data at its owner.
MXMail destination with priority.Target needs A/AAAA; do not target a CNAME.
TXTSPF, DKIM, DMARC, BIMI, verification.Preserve generated text exactly.
SRVService priority, weight, port, target.Target is a fully qualified hostname.
CAAWhich CA may issue certificates.Can intentionally block ACME when wrong.
PTRAddress to hostname in reverse DNS.Address owner controls the parent reverse delegation.
NSNames authoritative servers.Must agree with parent delegation.
SOAPrimary, serial, transfer and negative-cache timing.Serial change is mandatory for propagation.

The final SOA value means negative answers are cached too. If a client asks for a not-yet-created name, then you add it, the client can still see NXDOMAIN until the negative TTL expires. That is normal cache behaviour, not evidence that the new zone file failed.

Delegate at the parent and understand glue

Loading a zone on ns0 does not make it Internet-visible. The registrar must publish a parent delegation to your NS names. If an NS name is inside the delegated child domain, such as ns0.example.net for example.net, the parent must also provide its address as glue. Otherwise a resolver needs example.net DNS to locate ns0.example.net, while needing ns0.example.net to resolve example.net.

Delegation and glue proof
# Follow the hierarchy from root to child.
dig +trace example.net NS

# After +trace identifies the parent, inspect delegation and glue there.
dig @a.gtld-servers.net example.net NS +noall +authority +additional

# Both child authorities must give the same SOA serial and NS set.
dig @ns0.example.net example.net SOA +noall +comments +answer
dig @ns1.example.net example.net SOA +noall +comments +answer
Delegation failures
SymptomLikely layerRecovery
Parent points to old nameservers.Registrar/parent delegation.Correct parent NS; editing the child cannot override it.
In-bailiwick NS has missing or wrong glue.Registrar/parent glue.Set matching public IPv4 and IPv6 glue.
Parent and child NS differ.Inconsistent delegation or stale authority.Make the intended NS set agree at both layers.
One nameserver times out.Firewall, binding, route, or provider.Test UDP/TCP 53 and IPv4/IPv6 directly.

Operate primary and secondary DNS safely

The primary owns editable source. The secondary serves a transferred copy. After a higher SOA serial, the primary sends NOTIFY; the secondary checks for a newer serial and transfers the whole zone with AXFR or changes with IXFR. NOTIFY makes changes prompt, while refresh polling means a secondary still learns changes if NOTIFY is lost.

Authenticated transfer pair
// Primary ns0. Keep the actual TSIG secret in a protected local file.
key "ns1-transfer" {
    algorithm hmac-sha256;
    secret "REPLACE-WITH-A-PROTECTED-LOCAL-SECRET";
};
zone "example.net" {
    type primary;
    file "/var/bind/primary/example.net.zone";
    allow-transfer { key ns1-transfer; };
    also-notify { 198.51.100.53 key ns1-transfer; 2001:db8:54::53 key ns1-transfer; };
    notify yes;
};

// Secondary ns1.
server 203.0.113.53 { keys { ns1-transfer; }; };
zone "example.net" {
    type secondary;
    primaries { 203.0.113.53 key ns1-transfer; 2001:db8:53::53 key ns1-transfer; };
    file "/var/bind/secondary/example.net.zone";
};

TSIG authenticates transfer and notification peers. It is not public DNSSEC. Restrict AXFR/IXFR even with TSIG. The shared secret must never be in Git, a support ticket, or a public zone; keep file ownership and mode restricted to the account that runs named and the administrator.

Validate transfer and convergence
named-checkzone example.net /var/bind/primary/example.net.zone
named-checkconf /etc/bind/named.conf
rndc reload example.net
rndc zonestatus example.net

dig @ns0.example.net example.net SOA +short
dig @ns1.example.net example.net SOA +short
dig @ns1.example.net example.net SOA +tcp +short

# From the explicitly authorised secondary only, an AXFR may succeed.
# From everywhere else it should be refused.
dig @ns0.example.net example.net AXFR

Make changes, inspect errors, and recover

  1. 1. Edit source and increase serial

    Change the primary source file, increase the SOA serial once, and keep the previous known-good version.

  2. 2. Validate before loading

    Run named-checkzone for the precise file and named-checkconf for the complete configuration. Correct the reported line rather than restarting repeatedly.

  3. 3. Reload the zone and inspect its state

    Use rndc reload example.net followed by rndc zonestatus example.net. Use rc-service named reload for the daemon only after validation; systemd-selected Saphira uses systemctl reload named.service after unit verification.

  4. 4. Prove both authorities and both transports

    Query ns0 and ns1 directly for SOA and changed data over UDP and TCP, then test externally over IPv4 and IPv6.

DNS answers are diagnostic evidence
ResponseWhat it meansFirst check
NOERROR with answerRequested record exists.Answer, aa flag, serial, and authority agreement.
NODATAName exists but this type does not.Expected record type and negative-cache TTL.
NXDOMAINAuthoritative zone says name does not exist.Spelling, origin, serial, and cache lifetime.
REFUSEDServer intentionally declined.Correct authority and ACL/recursion/transfer policy.
SERVFAILServer could not complete processing.Direct authority, logs, transfers, DNSSEC and parent DS.
TimeoutNo response arrived.Route, firewall, binding, UDP/TCP, IPv4/IPv6, glue.

SERVFAIL is not one fault. If a direct query to ns0 fails, inspect zone load, permissions, transfer, and signing logs. If direct authorities are healthy but a validating resolver gives SERVFAIL, compare DNSKEY and parent DS, signature expiry, delegation, and IPv6 reachability. Query with dig plus trace to identify where the hierarchy stops, and use delv where installed to expose DNSSEC validation.

Find authoritative evidence
rndc status
rndc zonestatus example.net
dig @ns0.example.net example.net SOA +dnssec +noall +comments +answer
dig +trace example.net SOA
delv example.net SOA

# OpenRC logging depends on the configured syslog daemon.
rc-service named status
tail -f /var/log/messages

# Systemd-selected Saphira.
journalctl -u named.service -n 100 --no-pager

Reverse DNS, mail, DNSSEC, and disaster recovery

Forward DNS is your zone. Reverse DNS belongs to the public-address owner. For IPv4, that owner may set a PTR or delegate an in-addr.arpa zone. For IPv6 it may delegate a nibble-aligned ip6.arpa zone, commonly /48, /56, or /64. For MailDragon, test the whole chain: public IP to PTR to mail hostname to A/AAAA back to the same public IP.

Mail, DNSSEC, and external monitoring
# Mail record chain. MailDragon generates DKIM material and guidance;
# the operator publishes it at the chosen authority.
dig example.net MX +short
dig mail.example.net A +short
dig mail.example.net AAAA +short
dig -x 203.0.113.25 +short
dig example.net TXT +short
dig selector._domainkey.example.net TXT +short
dig _dmarc.example.net TXT +short
dig default._bimi.example.net TXT +short

# DNSSEC chain and independent monitor.
dig +dnssec example.net DNSKEY +multi
dig +dnssec example.net DS +multi
delv example.net SOA
dig @ns0.example.net example.net SOA +time=2 +tries=1 +tcp +short
dig @ns1.example.net example.net SOA +time=2 +tries=1 +tcp +short

DNSSEC is a complete lifecycle: active signing keys produce DNSKEY and RRSIG records; the active key produces a DS; the parent publishes that DS; validating resolvers prove the chain. A wrong or stale DS often appears as recursive SERVFAIL. During rollover, keep old and new key material according to the selected signing model until the parent DS and cache lifetimes are safely aligned. To disable DNSSEC, remove DS at the parent first, wait for propagation, then stop signing. Never unsign first while a DS remains.

Back up configuration, editable primary zones, generated secondary zones where useful, DNSSEC private keys and managed signing state, TSIG secrets, service ownership/modes, registrar delegation, glue, DS values, and restoration instructions. Encrypt and restrict backups containing secrets. Rehearse restore in an isolated Saphira VM: validate config and zones, restore signing state, compare SOA/DNSKEY, then test queries before any public cutover.

To migrate from hosted DNS, lower the old TTLs in advance, inventory every record including CAA and verification TXT, reproduce the zone on both new authorities, validate direct IPv4/IPv6 UDP and TCP answers, then change parent delegation. Keep the old provider until independent checks prove delegation, glue, DNSSEC, mail records, and both new servers. Split DNS is a separate design: private names or private answers belong only on internal LAN/VPN resolvers, with matching routes and firewall policy. Never publish private addresses or management topology in the public zone.