read the live apex NS with a signed query, retract nothing when unreadable
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful

An unsigned localhost query is REFUSED for a zone behind a key-matched view and
answers empty with exit 0; signing it with the update creds reaches the view.
This commit is contained in:
2026-09-26 19:25:06 +10:00
parent dc57ac1b2d
commit 08d46ccce0
9 changed files with 67 additions and 65 deletions
+1
View File
@@ -23,6 +23,7 @@ const (
NamedBin = "/usr/sbin/named"
RndcBin = "/usr/sbin/rndc"
NsupdateBin = "/usr/bin/nsupdate"
DigBin = "/usr/bin/dig"
)
// Config file paths derived from ConfigDir.
+25
View File
@@ -38,6 +38,31 @@ func (e *Executor) NSUpdate(ctx context.Context, namespace, pod, zone string, cr
return nil
}
// ApexNS returns the zone's currently published apex NS names, so the operator
// can converge the RRset rather than append to it. The query is TSIG-signed with
// the same creds as an update: a zone behind a view whose match-clients is a key
// is unreachable to an unsigned query, which named answers REFUSED (with an empty
// body and a zero exit status), and the caller must not read that as "no NS".
func (e *Executor) ApexNS(ctx context.Context, namespace, pod, zone string, creds TSIGCreds) ([]string, error) {
cmd := []string{
DigBin, "-y", fmt.Sprintf("%s:%s:%s", creds.Algorithm, creds.Name, creds.Secret),
"+short", "+time=5", "+tries=1", "@127.0.0.1", dot(zone), "NS",
}
out, err := e.Exec(ctx, namespace, pod, cmd, "")
if err != nil {
return nil, fmt.Errorf("query apex NS of %s: %w (out: %s)", zone, err, out)
}
var ns []string
for _, line := range strings.Split(out, "\n") {
// dig +short prints one fully-qualified name per line; anything without a
// trailing dot is not an answer.
if line = strings.TrimSpace(line); strings.HasSuffix(line, ".") {
ns = append(ns, line)
}
}
return ns, nil
}
// nsupdateScript renders the nsupdate input for a set of changes.
func nsupdateScript(zone string, updates []RecordUpdate) string {
var b strings.Builder