Refresh docs to reflect completed dyndns per-host auth fix

README.md's credential-audit table and command-injection writeup still
described the per-user auth binding gap as open; it's been closed since
2026-07-26. SETUP.md's TSIG section still taught the old single-shared-
key pattern for a fresh install - replaced with the per-host key +
update-policy pattern actually running in production now, plus a
pointer to the phased-rollout approach documented in TODO.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 10:32:21 +02:00
co-authored by Claude Sonnet 5
parent 6ea1b0f5f2
commit 399e3c17a7
2 changed files with 48 additions and 9 deletions
+40 -6
View File
@@ -192,14 +192,48 @@ dig @127.0.0.1 jayfield.org +short # your own zone resolves
### TSIG for dynamic DNS (if you need a dyndns subdomain)
**Don't use one shared key for every hostname in the zone** — this host
originally did, and it meant any dyndns credential could update *any*
hostname, not just its own (found 2026-07-20, fixed 2026-07-26, see
`TODO.md`). Generate one key per hostname instead:
```bash
sudo tsig-keygen -a hmac-sha256 dyndns.jayfield.org. | sudo tee /etc/bind/dyndns.jayfield.org.key
sudo chmod 640 /etc/bind/dyndns.jayfield.org.key
sudo chown root:bind /etc/bind/dyndns.jayfield.org.key # NOT 644 — this key is the only auth for dynamic updates
for h in <host1> <host2>; do
sudo tsig-keygen -a hmac-sha512 "$h.dyndns.jayfield.org" | \
sudo tee "/etc/bind/$h.dyndns.jayfield.org.key"
sudo chown root:bind "/etc/bind/$h.dyndns.jayfield.org.key"
sudo chmod 640 "/etc/bind/$h.dyndns.jayfield.org.key"
sudo setfacl -m u:www-data:r "/etc/bind/$h.dyndns.jayfield.org.key" # nsupdate runs as www-data
done
```
Add a matching `key` block + `update-policy` (or a delegated zone) in
`named.conf.local` — see the actual working config on this host for the
exact `nsupdate` invocation pattern used by `/var/www/dyndns`.
In `named.conf.default-zones`, `include` each key file and grant it
permission for *only* its own name via `update-policy` (not
`allow-update`, which can't be scoped per name):
```
include "/etc/bind/<host1>.dyndns.jayfield.org.key";
include "/etc/bind/<host2>.dyndns.jayfield.org.key";
zone "dyndns.jayfield.org" {
type master;
file "/var/cache/bind/db.dyndns.jayfield.org";
update-policy {
grant "<host1>.dyndns.jayfield.org" name <host1>.dyndns.jayfield.org. A AAAA TXT;
grant "<host2>.dyndns.jayfield.org" name <host2>.dyndns.jayfield.org. A AAAA TXT;
};
};
```
Give each hostname its own `.htpasswd` account too, and have the PHP
select the TSIG key based on `$_SERVER['PHP_AUTH_USER']` (the
*authenticated* identity) rather than whatever hostname the request
claims — that way BIND itself refuses a cross-host update even if the
app-level check were ever bypassed. See the live
`/var/www/dyndns/nsupdate*.php` on this host for the exact pattern, and
`TODO.md`'s 2026-07-26 dyndns entries for the full rollout writeup
(including how to phase in a legacy fallback for devices that need
reconfiguring first, without a hard cutover).
---