Commit Graph

2 Commits

Author SHA1 Message Date
unkinben c3d8de5060 dns: fix dns-update fqdn() double-appending zone to FQDN records
ci/woodpecker/pr/ruby-validate Pipeline was successful
ci/woodpecker/pr/puppet-lint Pipeline was successful
ci/woodpecker/pr/yamllint Pipeline was successful
ci/woodpecker/pr/bolt-validate Pipeline was successful
ci/woodpecker/pr/erb-validate Pipeline was successful
ci/woodpecker/pr/epp-validate Pipeline was successful
ci/woodpecker/pr/puppet-validate Pipeline was successful
ci/woodpecker/pr/ruby-check Pipeline was successful
profiles::dns::record publishes some records with an already-qualified name
(trailing dot), e.g. au-syd1-pve.main.unkin.net., cobbler.main.unkin.net.,
dashboard.ceph.unkin.net., and the halb service CNAMEs. The dns-update
script's fqdn() unconditionally appended the zone, producing a '..' empty
label; nsupdate rejects the whole per-zone update with 'invalid owner name:
empty label' + 'syntax error'. The reverse-PTR send (sorted first, always
relative) still applied, so affected hosts ended up with a PTR but no A.

Handle the already-FQDN case (name ends in '.') by using it verbatim; keep
the apex ('@'/empty) and relative cases as before.

Fixes hosts stuck with PTR-but-no-A (e.g. ausyd1nxvm2069-2073, 2098) and
repopulates the unkin.net service records (git/grafana/auth/fafflix).
2026-07-17 22:45:17 +10:00
unkinben 88fcb97ad1 dns: nsupdate host records to the authoritative server (#475)
Replaces the exported-resources → puppet DNS master zone-file flow with per-host RFC2136 dynamic updates against the k8s **bind-authoritative** write endpoint (198.18.200.9). The master no longer manages zone files.

## Design
Each node assembles its DNS records into a local concat file; a systemd `.path` unit watches it and runs `dns-update` (nsupdate) on change — exactly the watch-a-file model requested.

## Changes
- **profiles::dns::updater** (new): concat records file + TSIG key file + `dns-update` script + `dns-update.service` (oneshot) + `dns-update.path` (watcher). The script sends only the delta since last run and deletes removed records, grouped per zone.
- **profiles::dns::record**: writes a local concat fragment (`zone|name|type|ttl|value`) instead of exporting `@@concat::fragment` to the master.
- **profiles::dns::base**: includes `profiles::dns::updater` (all nodes).
- **hiera**: `profiles::dns::updater` server/key_name/algorithm in common.yaml.

## Inert until keyed
The updater does nothing until `profiles::dns::updater::key_secret` (TSIG) is set in eyaml — records are assembled but not applied, so nodes are safe before the key exists.

## Prerequisites (k8s side, separate)
1. The `bind-authoritative` zones must set `dynamicUpdate: true` + an `updateKeyRef` (a client-update BindTSIGKey) so they accept these updates.
2. The TSIG key must be shared: the operator-generated key value goes into eyaml here (or the planned Vault-sync feature bridges it).

## Validated
puppet parser/epp validate, puppet-lint, and a functional test of the generated per-zone nsupdate message (replace + delete-removed).

Reviewed-on: #475
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-12 22:23:18 +10:00