Help us shape the future of Go! Take the 2026 Go Developer Survey

coredns-plugins

module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 24, 2026 License: MIT

README

coredns-plugins

External CoreDNS plugins for encrypted-DNS discovery, multi-certificate TLS, DNS Cookies, RFC 2136 dynamic update and DNS measurement, plus a Dagger pipeline that builds a CoreDNS binary with them compiled in.

Plugin What it does Status
sni_tls Replaces the stock tls directive with SNI-based certificate selection, so one listener can serve several hostnames whose certs come from different issuers Implemented, tested, running in production
radnr Advertises an encrypted DNS resolver on the LAN via the RFC 9463 DNR option in IPv6 Router Advertisements, plus optional RFC 8106 RDNSS Wire/protocol layer complete and RFC-conformant; running in production
cookie DNS Cookies (RFC 7873) with the interoperable SipHash-2-4 server cookie of RFC 9018: validates and issues cookies, BADCOOKIE in enforce mode, three-stage secret rollover, per-case counters Implemented and tested; protocol rules modelled in TLA+ and Lean. Not yet deployed anywhere
dynupdate Serves a zone from memory and accepts RFC 2136 dynamic updates for it, e.g. from an ACME client's DNS-01 solver, with a mutable type allow-list Implemented and tested; update rules proved in Lean. In-memory only, see its README
probe A per-visitor synthetic measurement zone that records what each resolver's query reveals and can answer wrong on purpose (DNSSEC failure modes), plus an RFC 9567 error-report receiver Implemented and tested

sni_tls and radnr run in production in the author's homelab (as coredns-radnr, serving real LAN DNS over DoT/DoH/DoQ). They have real-world mileage now, but only in one deployment and one topology — treat "works here" accordingly. The other plugins are covered by tests, and by the models and proofs in verification/, but a model checks the model, not the deployment.

The Dagger pipeline can also compile in third-party plugins that don't live in this repo, pinned by module version. Currently: upstream's coredns/rrl for response rate limiting — adopted rather than reimplemented, with the verification results and three operational traps written up in docs/rrl-plugin.md.

Why these exist

sni_tls — CoreDNS's stock tls plugin loads exactly one cert/key pair per listener, with no SNI-based selection. If you serve both a public name and an internal-only name over DoT/DoH/DoQ, those two names need certificates from two different issuers (a public ACME CA cannot sign .arpa or .internal), and the stock plugin cannot put both on one tls:// block. The usual workaround is a second CoreDNS instance with its own address, cert, and copy of the zone data. sni_tls collapses that back into one instance. Design rationale, rejected alternatives, and the verified-DDR caveat: docs/sni-tls-plugin.md.

radnr — RFC 9463 defines a DHCP/RA option that hands clients an encrypted resolver's authentication domain name directly, so DoT/DoH/DoQ works with no per-device configuration. Support exists client-side (systemd-resolved ≥ 256, Windows 11) but router-side emitters are scarce. This plugin makes CoreDNS one.

cookie — CoreDNS has no DNS Cookie support: up to v1.14.6 it copies a request's COOKIE option into the response unvalidated, which RFC 7873 §5.2 does not allow, and upstream issue #6187 asking for real support has been open since 2023. This plugin issues and validates RFC 9018 server cookies so a cookie-bearing client can prove its source address; it is a signal and an enforce mode, not access control, because a client that sends no cookie cannot be made to. Design, limits and what each rule is checked by: docs/cookie-plugin.md.

Building

Plugins are compiled into CoreDNS — there is no runtime plugin loading. Each plugin is its own Go module so it can be developed and tested standalone, then pulled into a CoreDNS build with a go.mod replace directive.

Test a plugin on its own:

cd sni_tls   && go test -race ./...
cd radnr     && go test -race ./...
cd cookie    && go test -race ./...
cd dynupdate && go test -race ./...
cd probe     && go test -race ./...

Build a CoreDNS binary with all of them compiled in, via Dagger (from ci/):

dagger call test-plugin   --source=.. --plugin-dir=sni_tls
dagger call lint-plugin   --source=.. --plugin-dir=sni_tls
dagger call build-coredns --source=.. --coredns-version=1.14.6
dagger call containerize  --source=.. --coredns-version=1.14.6 \
  export --path=./coredns-plugins.tar

BuildCoredns clones the pinned upstream CoreDNS tag, patches plugin.cfg (sni_tls replaces stock tls; radnr is inserted next to health; each other plugin gets the slot its README and ci/main.go explain, cookie for instance right after tsig and before cache), runs go generate to regenerate zplugin.go, and builds. Containerize wraps the binary in a distroless base and returns an OCI tarball — it does not push. See ci/main.go for why pushing is left to the caller.

Two ordering constraints the pipeline encodes, both of which were real bugs:

  • go generate must run before go mod tidy. Tidy-first sees nothing importing the replaced modules yet and strips the replace as unused.
  • plugin.cfg order is semantic, not just registration order. It determines the order handlers run in; a plugin in the wrong slot can silently never see a query.

Deploying

The container needs CAP_NET_BIND_SERVICE to bind port 53 as a non-root user (the distroless base has no shell, so upstream's setcap approach isn't available). On Kubernetes:

securityContext:
  capabilities:
    add: ["NET_BIND_SERVICE"]

radnr additionally needs host networking and CAP_NET_RAW to send Router Advertisements.

radnr puts a second RA sender on your LAN. That can disrupt IPv6 for every device on the segment. It defaults to RouterLifetime=0 (explicitly not a default router) and refuses to advertise prefixes, and it has a dry-run mode that logs what it would send and transmits nothing. Start there. Read radnr/README.md before running it live.

Do not deploy sni_tls with more than one certificate on an instance serving RFC 9462 verified DDR. An unmatched-SNI client could be served a cert without the required IP-SAN, silently defeating verified discovery. The caveat is explained in docs/sni-tls-plugin.md.

Testing

Every plugin is covered by unit tests, and sni_tls additionally has:

  • a real loopback TLS 1.3 handshake asserting ML-KEM (post-quantum) negotiation (pqc_test.go)
  • a fuzz target over SNI lookup (fuzz_test.go)
  • partial-cert-set tests: one cert source being absent must not take the other listener down, since certs from different issuers rotate independently

radnr has byte-exact golden tests against the RFC 9463/9460/8415 encoding rules, and covers Router Solicitation responses (RFC 4861 §6.2.6, rate-limited) and interval jitter (§6.2.1).

cookie reproduces every server cookie RFC 9018 Appendix A prints, byte for byte, checks the 1-hour/5-minute window across the 2106 timestamp wrap, runs a request through the handler for each RFC 7873 §5.2 case in both modes and both transports, and carries fuzz targets over option classification, cookie verification and request handling.

Everything runs offline — GOPROXY=off go test ./... passes in every module.

Each plugin also carries benchmarks for its request path (bench_test.go); docs/performance.md records where the time goes, what was changed and what each change bought.

verification/ holds TLA+ models of the lifecycle and concurrency paths (cert reload, caddy restart hooks, RA scheduling, update atomicity, DNS Cookie secret rollout and client bootstrap) and Lean proofs of the pure parts (the RFC 9463 option codec, SNI wildcard selection, RFC 1982 serials, RFC 2136 update rules, the DNS Cookie option shapes, timestamp window and SipHash-2-4). CI runs both: verification/run.sh.

Contributing

See CONTRIBUTING.md.

Licence

MIT. CoreDNS itself is Apache-2.0; these plugins are separate works compiled into it.

Directories

Path Synopsis
Package main is this repository's Dagger CI module: test each plugin standalone, build a custom CoreDNS binary with the plugin(s) wired in via plugin.cfg, and package the resulting image.
Package main is this repository's Dagger CI module: test each plugin standalone, build a custom CoreDNS binary with the plugin(s) wired in via plugin.cfg, and package the resulting image.
cookie module
dynupdate module
probe module
radnr module
sni_tls module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL