Sets up the package layout for a CoreDNS plugin that will accept RFC 2136 dynamic updates with TSIG authentication, primarily targeting self-hosted ACME DNS-01 cert automation. What this commit gives us: - go.mod against coredns/caddy v1.1.4, coredns/coredns v1.14.3, miekg/dns v1.1.72 - plugin.go: RFC2136 struct + Handler interface (ServeDNS is pass-through) - setup.go: init() registration + Corefile parser (skeleton — recognizes tsig-key, ttl, persist directives but doesn't yet wire them) - README.md, .gitignore go build ./... clean. No tests yet — those come with Phase 1.2 alongside the actual UPDATE handler and in-memory store. Plan: ~/.claude/plans/dood-does-coredns-offer-enumerated-piglet.md
93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
package rfc2136
|
|
|
|
import (
|
|
"github.com/coredns/caddy"
|
|
"github.com/coredns/coredns/core/dnsserver"
|
|
"github.com/coredns/coredns/plugin"
|
|
clog "github.com/coredns/coredns/plugin/pkg/log"
|
|
)
|
|
|
|
// log is the package logger, scoped so messages are prefixed `[rfc2136]`.
|
|
var log = clog.NewWithPlugin("rfc2136")
|
|
|
|
func init() {
|
|
plugin.Register("rfc2136", setup)
|
|
}
|
|
|
|
// setup is invoked by the CoreDNS plugin registry once per Corefile
|
|
// `rfc2136` directive. It parses the directive's arguments and block,
|
|
// constructs an RFC2136 handler, and links it into the plugin chain.
|
|
func setup(c *caddy.Controller) error {
|
|
p, err := parse(c)
|
|
if err != nil {
|
|
return plugin.Error("rfc2136", err)
|
|
}
|
|
|
|
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
|
p.Next = next
|
|
return p
|
|
})
|
|
|
|
log.Infof("registered for zones: %v", p.Zones)
|
|
return nil
|
|
}
|
|
|
|
// parse reads a single `rfc2136 <zone> { ... }` block from the Corefile.
|
|
//
|
|
// Phase 1 grammar (only the surface is parsed; sub-directives are
|
|
// accepted but ignored — Phase 2 wires them):
|
|
//
|
|
// rfc2136 <zone> {
|
|
// tsig-key <name> <algorithm> <secret>
|
|
// ttl <seconds>
|
|
// persist <path>
|
|
// }
|
|
func parse(c *caddy.Controller) (*RFC2136, error) {
|
|
p := &RFC2136{}
|
|
|
|
for c.Next() {
|
|
args := c.RemainingArgs()
|
|
if len(args) < 1 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
// Normalize each declared zone to lowercase + trailing dot
|
|
// (CoreDNS canonical form). This makes later zone-membership
|
|
// checks an exact match against r.Question[0].Name.
|
|
for _, z := range args {
|
|
p.Zones = append(p.Zones, plugin.Host(z).NormalizeExact()...)
|
|
}
|
|
|
|
for c.NextBlock() {
|
|
switch c.Val() {
|
|
case "tsig-key":
|
|
kArgs := c.RemainingArgs()
|
|
if len(kArgs) != 3 {
|
|
return nil, c.Errf("tsig-key requires 3 args (name algorithm secret), got %d", len(kArgs))
|
|
}
|
|
// Phase 2: store in p.tsigKeys[name] = tsigKey{algo, secret}
|
|
log.Debugf("tsig-key parsed (storage NYI): name=%s alg=%s", kArgs[0], kArgs[1])
|
|
|
|
case "ttl":
|
|
tArgs := c.RemainingArgs()
|
|
if len(tArgs) != 1 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
// Phase 2: parse uint32, validate range, store in p.ttl
|
|
log.Debugf("ttl parsed (storage NYI): %s", tArgs[0])
|
|
|
|
case "persist":
|
|
pArgs := c.RemainingArgs()
|
|
if len(pArgs) != 1 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
log.Debugf("persist parsed (storage NYI): %s", pArgs[0])
|
|
|
|
default:
|
|
return nil, c.Errf("unknown directive: %s", c.Val())
|
|
}
|
|
}
|
|
}
|
|
|
|
return p, nil
|
|
}
|