Document dyndns command-injection fix and two remaining follow-ups

README's public-access table and "previously resolved" summary now
reflect the dyndns injection fix and the TSIG-permission regression it
uncovered. Adds two open TODO items found during that audit: the dyndns
app's lack of real per-user auth binding, and the fact that access
logging for every real vhost on this host is currently a no-op.
This commit is contained in:
2026-07-20 00:43:18 +02:00
parent 2ee6aad2b7
commit cff15408bf
2 changed files with 47 additions and 2 deletions
+17 -2
View File
@@ -243,7 +243,7 @@ Audited 2026-07-20 — which public hostnames require credentials:
| 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` |
| `dyndns.jayfield.org` | Yes — HTTP Basic Auth | Vhost-level `<Directory>` block, intentional |
| `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) |
| `mail.jayfield.org` | N/A — root is `403` (no content served there) | |
| `mail.jayfield.org/rspamd` | Yes — rspamd's own login page | |
| `cloud.jayfield.org` (Nextcloud) | Yes — redirects to `/login` | |
@@ -364,4 +364,19 @@ not a MITM, a self-inflicted interaction between two otherwise-correct
pieces of config. Fixed 2026-07-20 by moving it behind an Apache reverse
proxy on its own subdomain, `portainer.jayfield.org`, same pattern as
`web.jayfield.org`/`cloud.jayfield.org`; see "Portainer" above and
`TODO.md` for the full details.)
`TODO.md` for the full details.
Also: an audit of which public hostnames require credentials (prompted by
the Portainer/HSTS work above) turned up an OS command injection in the
dyndns update scripts (`/var/www/dyndns/nsupdate*.php` built a shell
command by directly interpolating unsanitized `$_GET` input into
`exec()`) — gated by the vhost's Basic Auth, so not anonymously
exploitable, but a real privilege escalation for anyone with valid dyndns
credentials. Fixed 2026-07-20 by rewriting the update path to use
`proc_open()` with array-form arguments (no shell involved at all) plus
strict hostname/IP validation. That same audit also surfaced (a) a
regression from the 2026-07-19 TSIG key hardening that had silently
broken legitimate dyndns updates — fixed with a POSIX ACL scoped to just
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
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`.)