
Self-Hosted DNS: NSD, DNSSEC and DANE
I run my own authoritative DNS for gnpk.zip on the same VPS as the rest of AS218248. NSD serves the zone, Hurricane Electric runs the secondary, and the zone is signed with DNSSEC, with DANE and MTA-STS on top. It is a small zone — a handful of records — but every record is there on purpose, and the whole setup is verifiable from the outside.
Why this exists
I run DNS to learn about DNS. Reading the RFCs is one thing; operating a signed zone — generating keys, re-signing, watching validators reject a bad record — is how the details actually stick. It is also, almost, a role model for what a secure website and DNS server looks like: DNSSEC, DANE, strict transport security, and a clean bill from the public testers, all running on one small box. If you are setting up your own zone, this page is a working example you can look at.
The setup
| Authoritative | NSD on the VPS (gnpk-as) |
|---|---|
| Secondary | ns1.he.net (Hurricane Electric), via AXFR |
| Public NS | ns1.gnpk.zip + ns1.he.net |
| Fallback | ns2 Namecheap glue left as a revert option |
NSD serves the unsigned zone from /etc/nsd/gnpk.zip.zone and the signed zone from /etc/nsd/gnpk.zip.zone.signed. HE pulls the signed zone by AXFR. The zone is small and hand-edited — every record in it was added deliberately.
DNSSEC
DNSSEC went live on Nov 8, 2026, at about 23:19 CST. DNSViz shows the zone all Secure.
| KSK | 38585 |
|---|---|
| ZSK | 35382 |
| Algorithm | ECDSAP256SHA256 (alg 13) |
| NSEC3 | Yes, salt 9b2436ee12ed48bb |
| DS record | gnpk.zip. 3600 IN DS 38585 13 2 e3d34c2afa05b02a97ff798f1cc1bfe9f6f0534c7d273bd4a68d27eb0ce8ec8c |
| RRSIGs expire | Dec 7, 2026 |
Re-signing is scripted: /usr/local/bin/resign.sh bumps the SOA serial in the unsigned zone, signs it, and restarts NSD. A root cron runs it every Sunday at 03:00 UTC. Every unsigned zone edit should be followed by a re-sign; HTML-only changes do not need one. Do not generate a new KSK — the DS record in the parent is tied to the current one.
DANE and TLSA
Three TLSA records pin the site and mail certificates to the DNS:
_443._tcp.gnpk.zip | 3 1 1 (web) |
|---|---|
_25._tcp.mail.gnpk.zip | 3 1 1 (mail) |
_25._tcp.mail.gnpk.zip | 2 1 1 (mail) |
The Postfix certificate is a Let’s Encrypt ECDSA (secp384r1) certificate for mail.gnpk.zip. A certbot renewal hook (/etc/letsencrypt/renewal-hooks/deploy/update-tlsa.sh) recomputes the TLSA records from the renewed certificate, appends them to the zone only if not already present, re-signs, and reloads Postfix and nginx — so a renewal cannot silently break DANE.
MTA-STS and TLS-RPT
Mail transport security is published too: an _mta-sts TXT record (v=STSv1; id=20261113), a _smtp._tls TXT record (v=TLSRPTv1; rua=mailto:me@gnpk.zip), and a policy at mta-sts.gnpk.zip in enforce mode with max_age 604800 and mx: mail.gnpk.zip. The flip happened Nov 13, 2026, ahead of the planned Nov 16–17 date, after two clean TLS-RPT reports from Google (Nov 9: 5 sessions, 0 failures; Nov 10: 2 sessions, 0 failures).
HTTPS record and DNS over TLS
Two more DNS standards went live Nov 13, 2026:
- HTTPS record (TYPE65, RFC 9460) — the apex carries
gnpk.zip. 3600 IN HTTPS 1 . alpn="h3,h2" ipv4hint=45.154.99.220 ipv6hint=2a0c:b640:10::6:220, advertising HTTP/3 and HTTP/2 support and the server addresses. DNSSEC-validated. - DNS over TLS (DoT, RFC 7858) — NSD serves encrypted DNS on port 853, with a Let’s Encrypt certificate for
ns1.gnpk.zip(expires 2027-02-11). The certificate was obtained by manual DNS-01, so it will not auto-renew — it must be renewed by hand before expiry, then NSD restarted. Verified withdig @45.154.99.220 gnpk.zip +tls +tls-host=ns1.gnpk.zip.
DNS over HTTPS (DoH) and DNS over QUIC (DoQ) do not apply here: both are stub-to-recursive protocols, and NSD is authoritative-only. DoT is the encrypted transport for an authoritative server, and it is the one in use.
ZONEMD: tried and removed
ZONEMD (TYPE63, RFC 8976) was added Nov 13, 2026, and removed the same day. The record itself worked — the primary served it, DNSSEC-validated — but Hurricane Electric’s secondary could not handle it: ns1.he.net returned SERVFAIL for every query (SOA, A, DNSKEY) whenever the zone contained a ZONEMD record, and served normally whenever it did not. An A/B test across three re-signs proved it (serial 2026110821 without ZONEMD: NOERROR; serial 2026110822 with ZONEMD: SERVFAIL on everything; serial 2026110823 without ZONEMD: NOERROR again). HE’s NSD cannot serve a zone containing TYPE63. So ZONEMD stays off while HE is the secondary — the re-sign script no longer passes the -z flag to ldns-signzone. If the secondary ever changes, it is worth retrying.
The same incident showed the value of a secondary: while HE was SERVFAILing, the primary kept answering, and the fix was a fresh zone pull from HE’s panel (Validate!) followed by the ZONEMD removal.
Extras in the zone
- LOC — the server’s location, city-level: Düsseldorf,
51 13 39.720 N 6 46 24.600 E. - CERT — type 3 (PGP), pointing at my OpenPGP key, so the key is discoverable from DNS.
- security.txt — clearsigned, at
/.well-known/security.txt, expiring 2027-05-08.
How it scores
| internet.nl website | 100% — Hall of Fame |
|---|---|
| internet.nl email | 100% — Hall of Fame |
| Mozilla Observatory | A+, 155/100 |
| HE IPv6 Certification | Guru, earned Nov 2, 2026 |
Find AS218248 around the internet: PeeringDB · bgp.tools · RIPEstat · Hurricane Electric · Network · Peering policy