Phase 1.2: wire parser → typed config + 13 unit tests
The Corefile parser now fully populates typed fields on RFC2136 instead of just recognising directives. Validation happens at parse-time so configuration errors fail loud at CoreDNS startup rather than silent at request time. Added: - config.go: tsigKey type, TSIG algorithm allowlist (rejects HMAC-MD5 deliberately), base64 secret decoder with 8-byte minimum length check, canonical-key-name normalisation (lowercase + trailing dot). - plugin.go: RFC2136 struct now carries TSIGKeys map, TTL uint32, PersistPath string. DefaultTTL=60. - setup.go: parse() validates and stores tsig-key/ttl/persist directives. Duplicate key names rejected. Multiple TSIG keys allowed (for rotation). At-least-one-zone is enforced. - setup_test.go: 13 table-driven cases (5 happy + 8 error paths) using caddy.NewTestController. All pass. ServeDNS still passes through — UPDATE handling lands in Phase 1.4. Module path: git.supported.systems/rsp2k/coredns-rfc2136
This commit is contained in:
parent
e9d37f483c
commit
eba6313ec0
6 changed files with 364 additions and 29 deletions
41
plugin.go
41
plugin.go
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// Scope:
|
||||
// - Handles UPDATE messages (OPCODE=5) for configured zones.
|
||||
// - Verifies TSIG signatures (HMAC-SHA256 today; algorithm-pluggable).
|
||||
// - Verifies TSIG signatures (HMAC-SHA family; algorithm-pluggable).
|
||||
// - Stores records in memory; optional periodic snapshot to disk.
|
||||
// - Serves queries (SOA, NS, A, AAAA, TXT) for the configured zone
|
||||
// from the in-memory store plus a synthetic SOA/NS apex.
|
||||
|
|
@ -15,10 +15,11 @@
|
|||
// - General-purpose authoritative DNS (use `auto`/`file` for that).
|
||||
// - DNSSEC signing (add later via the `dnssec` plugin in front).
|
||||
//
|
||||
// Phase 1 status: skeleton only. ServeDNS passes through to the next
|
||||
// plugin; setup parses the Corefile but does not yet do anything with
|
||||
// the parsed configuration. See plan at
|
||||
// ~/.claude/plans/dood-does-coredns-offer-enumerated-piglet.md
|
||||
// Phase 1.2 status: parser fully wires Corefile into typed config.
|
||||
// ServeDNS still passes through to the next plugin — UPDATE handling
|
||||
// and zone-serving land in Phase 1.3/1.4. See plan at
|
||||
//
|
||||
// ~/.claude/plans/dood-does-coredns-offer-enumerated-piglet.md
|
||||
package rfc2136
|
||||
|
||||
import (
|
||||
|
|
@ -28,6 +29,12 @@ import (
|
|||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// DefaultTTL is the TTL applied to dynamically-added records when the
|
||||
// Corefile doesn't specify one. 60s matches the short-lived nature of
|
||||
// ACME challenge TXT records and keeps stale answers from lingering in
|
||||
// resolver caches.
|
||||
const DefaultTTL uint32 = 60
|
||||
|
||||
// RFC2136 is the plugin handler. One instance per Corefile server block.
|
||||
type RFC2136 struct {
|
||||
// Next is the downstream plugin in the chain.
|
||||
|
|
@ -38,18 +45,28 @@ type RFC2136 struct {
|
|||
// zones pass through to Next.
|
||||
Zones []string
|
||||
|
||||
// Phase 2 will add:
|
||||
// - tsigKeys map[string]tsigKey
|
||||
// - store *recordStore
|
||||
// - ttl uint32
|
||||
// TSIGKeys is keyed by canonical key name (lowercased, trailing
|
||||
// dot). Empty means TSIG is disabled — UPDATEs without TSIG would
|
||||
// be rejected unconditionally in Phase 1.4.
|
||||
TSIGKeys map[string]tsigKey
|
||||
|
||||
// TTL is applied to dynamically-injected records that don't carry
|
||||
// an explicit TTL in the UPDATE message.
|
||||
TTL uint32
|
||||
|
||||
// PersistPath, when non-empty, names a file the plugin writes a
|
||||
// JSON snapshot of its in-memory store to on a periodic schedule.
|
||||
// Empty means in-memory only (acceptable for ACME challenges,
|
||||
// which are seconds-to-minutes lived and re-issued on restart).
|
||||
PersistPath string
|
||||
}
|
||||
|
||||
// Name implements plugin.Handler.
|
||||
func (p *RFC2136) Name() string { return "rfc2136" }
|
||||
|
||||
// ServeDNS implements plugin.Handler. Phase 1 is a pass-through so the
|
||||
// plugin can register, parse config, and live in the chain without
|
||||
// changing behavior. Phase 2 wires the UPDATE handler and zone serving.
|
||||
// ServeDNS implements plugin.Handler. Phase 1.x is a pass-through so
|
||||
// the plugin can register, parse config, and live in the chain without
|
||||
// changing behavior. Phase 1.3 wires UPDATE handling + query serving.
|
||||
func (p *RFC2136) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
return plugin.NextOrFailure(p.Name(), p.Next, ctx, w, r)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue