Replaces the Phase-1.3 refuseUpdate() stub with a real RFC 2136 handler. Caddy via caddy-dns/rfc2136 can now inject and remove records. UPDATE message handling (update.go): - Zone section validation: must be exactly one SOA-typed record naming a zone we're authoritative for. Returns FORMERR/NOTAUTH otherwise. - Prerequisites (§3.2): name-exists, RRset-exists, name-NOT-exists, RRset-NOT-exists semantics implemented. First failure short-circuits with the spec's rcode (NXDOMAIN/NXRRSET/YXDOMAIN/YXRRSET). - Updates (§3.4.2): add RR, delete RRset (CLASS=ANY+RDLEN=0), delete all RRsets at name (CLASS=ANY+TYPE=ANY), delete specific RR (CLASS= NONE). - Apex SOA/NS protected: synthetic and cannot be added or removed via UPDATE. Apex wipe (TYPE=ANY at apex) also refused. - Default TTL applied to incoming records with TTL=0. TSIG (tsig.go + setup.go): - setup() now populates dnsserver.Config.TsigSecret so the underlying dns.Server auto-verifies signatures via miekg/dns. - checkTSIG() in ServeDNS gates UPDATEs: rejects if no TSIG, unknown key name, algorithm-downgrade attempt, or w.TsigStatus() != nil. - No TSIG keys configured → all UPDATEs refused (safety default). - Algorithm pinning prevents downgrade attacks (e.g. forced HMAC-MD5). Tests (update_test.go): 11 new cases covering happy paths and every error rcode. Total: 35 top-level test passes, 0 failures. ServeDNS dispatch now calls handleUpdate after auth gate. The refuseUpdate() stub is gone. UPDATE end-to-end via nsupdate requires the custom CoreDNS image (Phase 2) to verify TSIG plumbing on the dns.Server side.
coredns-rfc2136
A CoreDNS plugin that accepts RFC 2136 dynamic DNS updates (TSIG-authenticated), filling a gap in the official plugin set.
CoreDNS as-shipped has no plugin for accepting dynamic updates — its
plugin model treats authoritative data as read-only (loaded from auto,
file, secondary, etc.). This plugin adds the missing piece.
Primary use case: self-hosted ACME DNS-01
The motivating problem: automate Let's Encrypt cert issuance for many domains without depending on registrar APIs (Vultr/Route53/Cloudflare). The architecture:
_acme-challenge.example.com CNAME <uuid>.auth.supported.systems
│
│ delegated NS to your CoreDNS host
▼
CoreDNS + rfc2136 plugin
│
│ accepts TSIG UPDATEs from Caddy
│ (caddy-dns/rfc2136) or any other
│ ACME client
▼
Let's Encrypt validates
One-time per protected domain: add a CNAME glue line in your static
zones. After that, all cert issuance + renewal happens via UPDATE
messages — zero static zone-file churn.
Status
Phase 1 (skeleton): compiles, registers with CoreDNS, parses the
Corefile directive. Does not yet handle UPDATE messages or serve any
records. ServeDNS is a pass-through. See phases.md for the roadmap.
Configuration
rfc2136 <zone> [<zone>...] {
tsig-key <key-name> <algorithm> <base64-secret>
ttl <seconds>
persist <path>
}
Example:
.:53 auth.example.com {
rfc2136 auth.example.com {
tsig-key acme-key. hmac-sha256 BASE64SECRET==
ttl 60
}
errors
log
}
Building
This plugin is consumed by a custom CoreDNS build via plugin.cfg:
# In CoreDNS source's plugin.cfg, BEFORE the `cache` plugin:
rfc2136:git.supported.systems/rsp2k/coredns-rfc2136
Then go get git.supported.systems/rsp2k/coredns-rfc2136 && make.
License
MIT (TODO: add LICENSE file).