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:
@@ -243,7 +243,7 @@ Audited 2026-07-20 — which public hostnames require credentials:
|
|||||||
| Host | Requires credentials? | Notes |
|
| Host | Requires credentials? | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `jayfield.org` / `www.jayfield.org` | No (intentionally public static site) | Was accidentally 401'ing every visitor via a stray `.htaccess` — fixed, see `TODO.md` |
|
| `jayfield.org` / `www.jayfield.org` | No (intentionally public static site) | Was accidentally 401'ing every visitor via a stray `.htaccess` — fixed, see `TODO.md` |
|
||||||
| `dyndns.jayfield.org` | Yes — HTTP Basic Auth | Vhost-level `<Directory>` block, intentional. Had an OS command injection in its update scripts (fixed 2026-07-20, see `TODO.md`); the app itself still has no real per-user auth binding (open `TODO.md` item) |
|
| `dyndns.jayfield.org` | Yes — HTTP Basic Auth, one dedicated account per hostname | Vhost-level `<Directory>` block, intentional. Had an OS command injection in its update scripts (fixed 2026-07-20) and no real per-user auth binding (fixed 2026-07-26 with per-host TSIG keys — see `TODO.md`) |
|
||||||
| `mail.jayfield.org` | N/A — root is `403` (no content served there) | |
|
| `mail.jayfield.org` | N/A — root is `403` (no content served there) | |
|
||||||
| `mail.jayfield.org/rspamd` | Yes — rspamd's own login page | |
|
| `mail.jayfield.org/rspamd` | Yes — rspamd's own login page | |
|
||||||
| `cloud.jayfield.org` (Nextcloud) | Yes — redirects to `/login` | |
|
| `cloud.jayfield.org` (Nextcloud) | Yes — redirects to `/login` | |
|
||||||
@@ -385,6 +385,11 @@ broken legitimate dyndns updates — fixed with a POSIX ACL scoped to just
|
|||||||
that key file, not group membership, since the `bind` group also owns
|
that key file, not group membership, since the `bind` group also owns
|
||||||
`rndc.key` — and (b) that access logging for every real vhost on this
|
`rndc.key` — and (b) that access logging for every real vhost on this
|
||||||
host is currently a no-op, and (c) that the dyndns app has no real
|
host is currently a no-op, and (c) that the dyndns app has no real
|
||||||
per-user auth binding. (b) and (c) are left open — see `TODO.md`.)
|
per-user auth binding.
|
||||||
(b) fixed 2026-07-25 — see `TODO.md` for the root cause and the
|
(b) fixed 2026-07-25 — see `TODO.md` for the root cause and the
|
||||||
per-vhost `CustomLog` fix; (c) remains open.
|
per-vhost `CustomLog` fix. (c) fixed 2026-07-26: every dyndns hostname
|
||||||
|
now has its own dedicated `.htpasswd` account and TSIG key, with BIND's
|
||||||
|
`update-policy` (not just an app-level check) refusing any update where
|
||||||
|
the authenticated user doesn't match the requested hostname — see
|
||||||
|
`TODO.md` for the full rollout, including the now-retired shared key
|
||||||
|
and legacy fallback.
|
||||||
|
|||||||
@@ -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)
|
### 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
|
```bash
|
||||||
sudo tsig-keygen -a hmac-sha256 dyndns.jayfield.org. | sudo tee /etc/bind/dyndns.jayfield.org.key
|
for h in <host1> <host2>; do
|
||||||
sudo chmod 640 /etc/bind/dyndns.jayfield.org.key
|
sudo tsig-keygen -a hmac-sha512 "$h.dyndns.jayfield.org" | \
|
||||||
sudo chown root:bind /etc/bind/dyndns.jayfield.org.key # NOT 644 — this key is the only auth for dynamic updates
|
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
|
In `named.conf.default-zones`, `include` each key file and grant it
|
||||||
exact `nsupdate` invocation pattern used by `/var/www/dyndns`.
|
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).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user