fix(auth): hand off privileges before state creation; fail closed on store errors

Fresh boots opened the owner/token/session stores before EnsureDataRoot
dropped to the host user, leaving a root-owned maestro/ state dir whose
EACCES surfaced as a bogus ERR_INVALID_CREDENTIALS on /api/auth/setup.
main() now runs the handoff first; EnsureDataRoot heals pre-existing
root-owned state dirs while still root (healOwnerState).

Storage failures from the owner store now answer 500 ERR_INTERNAL
everywhere (status/setup/login/change-password/RequireSession/
GateWebSocket) instead of blaming credentials or re-advertising setup
mode; SetupRequired returns the store error instead of swallowing it,
and FileOwnerStore.Verify propagates read errors rather than masking
them as unknown-user.
This commit is contained in:
2026-08-23 12:10:15 +01:00
parent 707fe619f7
commit c014b2abfb
7 changed files with 228 additions and 23 deletions
+8 -3
View File
@@ -75,6 +75,14 @@ func main() {
if err != nil {
log.Fatalf("Failed to resolve data root: %v", err)
}
// Drop to the host user BEFORE any state is created: the stores below
// mkdir/write under <dataRoot>/maestro, and files made during the root
// phase stay root-owned after the handoff, locking the owner gate out of
// its own store (EACCES surfaced as a bogus ERR_INVALID_CREDENTIALS on
// /api/auth/setup).
if err := libcontainers.EnsureDataRoot(); err != nil {
log.Fatalf("Data root check failed: %v", err)
}
maestroDir := filepath.Join(dataRoot, "maestro")
ownerStore, err := owner.NewFileOwnerStore(maestroDir)
if err != nil {
@@ -100,9 +108,6 @@ func main() {
log.Fatalf("Failed to init docker client: %v", err)
}
defer dc.Close()
if err := libcontainers.EnsureDataRoot(); err != nil {
log.Fatalf("Data root check failed: %v", err)
}
// Provision the instance CA on first run (security theme M1). The CA is
// the trust anchor for every peer edge on dcntrlized_net; services get
// their identity certs from it at start/install.