fix(a7-auth): persist dummy key in owner-only sidecar

The store-wide dummy key was regenerated on every credentials_load, so an
unknown user's dummy salt changed across daemon restarts while a real user's
stored salt stayed stable -- a restart-gated username-enumeration oracle.

Persist the 32-byte key in a 0600 <store>.dummykey sidecar next to the
credential store.  An absent sidecar is created with O_EXCL and fsynced; a
present sidecar is read only when it is an owner-only regular file of exactly
32 bytes (otherwise the load fails closed).  If the sidecar cannot be created
(read-only mount, missing directory) fall back to a transient per-run key with
a warning.  A NULL store path keeps the key ephemeral.
This commit is contained in:
2026-09-12 18:40:21 +02:00
parent 2489d422e5
commit 42f01c0968
5 changed files with 359 additions and 19 deletions
+7 -5
View File
@@ -522,11 +522,13 @@ deterministic per-username dummy challenge, so probing the daemon cannot
enumerate users. Store lines are generated with
`fastsync-server --hash-credentials <plaintext-file>` (see `RSYNC_COMPAT.md`);
redirect that output to an owner-only (mode 0600) file, and note that legacy
`user:SHA256HEX` stores are rejected. Two residuals are accepted: the dummy salt
is stable within one daemon lifetime but changes across restarts, so a
restart-gated enumeration channel remains (persisting a dummy key is out of
scope); and the store iteration count is observable pre-auth by design, since
the miss path must match a hit.
`user:SHA256HEX` stores are rejected. FastSync also maintains an owner-only
(mode 0600) `<store>.dummykey` sidecar next to the store: it holds the store-wide
dummy key, is auto-created on first load, and must be preserved across daemon
restarts so the dummy challenge for an unknown user stays stable (the key is
never regenerated while the sidecar exists). One residual is accepted: the store
iteration count is observable pre-auth by design, since the miss path must match
a hit.
TLS provides encrypted TCP transport. Supplying `--ca` enables certificate
verification; without it, traffic is encrypted but peer identity is not