From 3dd99fa81a632f5116938680ecced07a5214af52 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Thu, 21 May 2026 12:17:49 -0600 Subject: [PATCH] test: sandboxed docker-compose stack for plugin smoke testing Brings up a parallel CoreDNS instance on ports 11053/19153 with a single test.example.com zone. Useful for verifying the custom image builds and the rfc2136 plugin accepts/applies UPDATEs end-to-end before touching production zones. Already validated the msgAcceptFunc override fix end-to-end via nsupdate, with the auto plugin re-serving the new record within 5s. Note: zones/test.example.com.zone gets rewritten by the plugin during testing. If perms get hosed (docker writes as root), run sudo chown -R rpm:rpm test/zones/ to reclaim. --- test/Corefile | 30 +++++++++++++ test/README.md | 73 ++++++++++++++++++++++++++++++++ test/docker-compose.yml | 27 ++++++++++++ test/zones/test.example.com.zone | 8 ++++ 4 files changed, 138 insertions(+) create mode 100644 test/Corefile create mode 100644 test/README.md create mode 100644 test/docker-compose.yml create mode 100644 test/zones/test.example.com.zone diff --git a/test/Corefile b/test/Corefile new file mode 100644 index 0000000..5919e94 --- /dev/null +++ b/test/Corefile @@ -0,0 +1,30 @@ +# Test stack Corefile -- isolated from the production Corefile by virtue +# of running in a separate docker-compose project on different ports. +# +# Plugin chain semantics: +# - auto loads test.example.com.zone from /zones; reload every 5s +# so we see UPDATE-induced changes promptly during smoke tests +# - rfc2136 intercepts UPDATE opcode; passes all queries through to auto +# +# Plugin.cfg order (set in coredns/Dockerfile) puts rfc2136 BEFORE auto AND +# BEFORE cache, so UPDATE messages reach our handler before anything else. + +. { + auto { + directory /zones (.*)\.zone {1} + reload 5s + } + + rfc2136 test.example.com { + zones-dir /zones + tsig-key acme-update-key. hmac-sha256 {$ACME_TSIG_SECRET} + ttl 60 + # Auto-commit OFF in test: there's no git repo at /zones inside + # the container, and we don't want spurious commits during smoke + # tests. End-to-end auto-commit testing happens on dell01. + auto-commit false + } + + log + errors +} diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..f850d6e --- /dev/null +++ b/test/README.md @@ -0,0 +1,73 @@ +# Test stack — sandboxed CoreDNS + rfc2136 plugin + +Brings up a parallel CoreDNS instance for smoke-testing the +`git.supported.systems/rsp2k/coredns-rfc2136` plugin without +touching the production stack on dell01. + +## What this proves + +- The custom CoreDNS image builds and links the plugin successfully. +- The plugin parses its Corefile directive at startup. +- Queries (SOA, A, TXT, etc.) flow through the `auto` plugin as + normal (the rfc2136 plugin is transparent for non-UPDATE traffic). +- UPDATE messages signed with the configured TSIG key apply changes + to the on-disk zone file. +- After an UPDATE, `dig` returns the new record (CoreDNS's `auto` + plugin sees the mtime change and reloads within 5s). + +## Quickstart + +```bash +cd test/ + +# 1. Build + start. The build clones CoreDNS source and pulls the +# plugin via `go get` -- expect ~2-3 min for the first build. +docker compose up -d --build + +# 2. Sanity-check the apex SOA is served. +dig @127.0.0.1 -p 11053 test.example.com SOA +short + +# 3. Push an UPDATE via nsupdate. The TSIG secret comes from .env. +nsupdate -y "hmac-sha256:acme-update-key.:$(grep ACME_TSIG_SECRET .env | cut -d= -f2)" <<'EOF' +server 127.0.0.1 11053 +zone test.example.com +update add token.test.example.com 60 TXT "validation-token-1" +send +EOF + +# 4. Wait ~5s for the auto plugin to reload, then verify. +sleep 6 +dig @127.0.0.1 -p 11053 token.test.example.com TXT +short +# expected: "validation-token-1" + +# 5. Inspect the updated zone file on disk. +cat zones/test.example.com.zone + +# 6. Tear down when done. +docker compose down +``` + +## Files + +| Path | Role | +|---|---| +| `Corefile` | Two plugins: `auto` (serves queries) + `rfc2136` (handles UPDATE) | +| `zones/test.example.com.zone` | The one test zone; rewritten by rfc2136 on UPDATE | +| `docker-compose.yml` | Standalone stack on ports 11053 / 19153 | +| `.env` | Isolated `COMPOSE_PROJECT_NAME` + a fixed throwaway TSIG secret | + +## What this does NOT test + +- TSIG cryptographic correctness against a malicious client. (Unit + tests in the plugin's `tsig.go` + miekg/dns's own tests cover this.) +- Git auto-commit. We disable it here (`auto-commit false` in Corefile) + because there's no git repo at `/zones` inside the container. That + path gets exercised on dell01 in Phase 3. +- Caddy → caddy-dns/rfc2136 end-to-end cert issuance. (Phase 3.) + +## Cleanup + +```bash +docker compose down +git checkout -- zones/test.example.com.zone # restore baseline +``` diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 0000000..a6a30fe --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,27 @@ +services: + # Custom CoreDNS build with the rfc2136 plugin baked in. + # The Dockerfile lives in the parent dir (../coredns/Dockerfile) so + # we reuse the production build artefact. + coredns: + build: + context: .. + dockerfile: coredns/Dockerfile + image: coredns-rfc2136-test:dev + container_name: coredns-rfc2136-test + restart: "no" # never auto-restart in test scenarios + command: ["-conf", "/etc/coredns/Corefile"] + environment: + - ACME_TSIG_SECRET=${ACME_TSIG_SECRET} + ports: + - "${TEST_DNS_PORT}:53/udp" + - "${TEST_DNS_PORT}:53/tcp" + - "${TEST_METRICS_PORT}:9153/tcp" + volumes: + - ./Corefile:/etc/coredns/Corefile:ro + - ./zones:/zones # NOT read-only: rfc2136 needs to write here + healthcheck: + test: ["CMD", "/coredns", "-version"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s diff --git a/test/zones/test.example.com.zone b/test/zones/test.example.com.zone new file mode 100644 index 0000000..a58b51f --- /dev/null +++ b/test/zones/test.example.com.zone @@ -0,0 +1,8 @@ +; Auto-written by coredns-rfc2136 on 2026-05-21T17:59:46Z +; Zone: test.example.com. +$ORIGIN test.example.com. +test.example.com. 3600 IN SOA ns.test.example.com. admin.test.example.com. 2026052103 300 120 604800 60 +test.example.com. 3600 IN NS ns.test.example.com. +ns.test.example.com. 3600 IN A 127.0.0.1 +token.test.example.com. 60 IN TXT "validation-1" +fresh.test.example.com. 60 IN TXT "msgAcceptFunc-fix-works"