The deployment shipped its payload but not the thing that installs it.
54a5c09 added README, knot.conf, knsctl, zone.tmpl, secrets.conf.example,
the aliases and the MOTD -- but no deploy.sh, no cloud-init.yml, and no
entry in automations.sh's DEPLOYMENTS. `git log --all` confirms deploy.sh
was never committed and it is not gitignored, yet README.md:96 and :100
tell the operator to run it. So the documented install path did not exist.
Alpine only, native, matching the README: Knot binds :53 directly, needs
real client addresses for RRL and cookies, and keeps its DNSSEC key store on
the host filesystem. The RHEL packaging needs EPEL, which nothing here sets
up, so anything that is not Alpine dies with a clear message rather than
half-installing somewhere untested.
Three decisions worth recording:
The include chain is stubbed. knot.conf include:s seven files the `dns` repo
owns; Knot treats a missing include as a config error, so a node the pipeline
has never delivered to would fail conf-check and never start. deploy.sh
writes a placeholder for each one that is ABSENT -- never over a delivered
file -- so the node comes up healthy serving no zones until the pipeline
lands.
TSIG is generated on a primary and required on a secondary. The keys must
match byte for byte, so a secondary that generated its own would
authenticate nothing; it now refuses to deploy without TSIG_AUTHORITIVE and
TSIG_ADMIN. A primary generates both and prints them once. An existing
secrets.conf is never rewritten, so a re-run cannot rotate a key out from
under a running estate.
PRIMARY_ADDR seeds a minimal remotes.conf on a secondary so it can bootstrap
by AXFR before the pipeline runs -- written only when remotes.conf was
absent, verified by re-running against a delivered file and confirming it is
left untouched.
Re-runs apply changes rather than freezing at first deploy, per the pattern
this repo just adopted elsewhere: knot.conf is re-rendered from .env every
run, env-presence is captured before the ":=" defaults, and values passed to
a re-run are written back to .env with the awk-based set_env from 947c899 --
which matters here because a TSIG secret can contain the characters that
broke the sed-based one.
Two bugs caught while testing this, before it shipped:
- the secrets.conf renderer used `++n` as a gsub argument, which awk
evaluates on every line, not just matching ones -- both keys would have
received the SAME secret, making the read-only admin key identical to the
replication key. Increments on a matching line only now.
- the MOTD is a pre-drawn box, so substituting values of a different width
than their @TOKEN@ shifted the right border on every login. Values are now
padded to the token's span, measured over an ASCII-only region so it holds
under busybox awk in the C locale; an over-long value overflows rather than
being truncated.
Verified: knot.conf renders identity/NSID/listen and leaves the control
socket alone; all seven stubs are created on a fresh node and skipped on a
re-run; the remotes.conf seed fires only for a fresh secondary; .env seeds
every runtime key; the MOTD renders with no leftover tokens and an aligned
border. Not verified: apk, knotc and the service start, which need an actual
Alpine host.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>