Remove Legacy Credential Bootstrap Paths

Decouple user provisioning from HTTPS identity storage while retaining
compatible v1 user records and migrating TLS material to the
credential-free
v2 format. Add focused security regression coverage and update operator
documentation.
This commit is contained in:
2026-09-08 19:09:26 +02:00
parent 82f21d6116
commit ac80863d80
26 changed files with 1013 additions and 583 deletions
+5 -1
View File
@@ -4,6 +4,10 @@ This file is working memory. Update it during active work and before handoff; do
## Development state
- **Legacy credential removal (2026-09-08), implemented/host-tested/build-verified; target pending:** New database and TLS module contracts inspected. Missing user storage commits empty; normal UART0 `user add <username> admin [--generate]` provisions the first administrator. Unavailable-only UART0 recovery rebuilds empty and refuses healthy storage. Existing valid user v1 bytes stay unchanged; private derived `v1_admin_marker`, no public bootstrap/migration/sync API. HTTPS private v1 reader migrates `web_sec/material` 1392 → TLS-only v2 1340 bytes, retaining exact DER/fingerprint/generation, committing before publication; failures fail closed without fallback replacement. Legacy credential commands removed; generated user passwords and TLS rotation retained; reset is TLS-only. Older v1-only firmware cannot read v2; logical NVS replacement is not secure flash wiping, and no factory erase is required. Startup integration complete; SSH gate independent of HTTPS identity, obsolete completion/policy entries removed. Parent security15, canonical accounts, policy/startup64-gate combinations, console lifecycle, cookie accounts, idle18+guards and build/diff checks PASS. Integration agent reports broad auth/transport/lifecycle/UI regression PASS. Review no actionable findings; final agent-added sparse populated v1 reload/authorization regression PASS, test-only extension. Parent final production build 3.93 s, 99,260 B RAM / 1,705,781 B flash (56/3,200 vs idle-cleanup baseline). Host NVS doubles do not prove flash/power-loss behavior; no independently captured old-device TLS fixture or hardware validation. Record: `docs/legacy_credential_removal.md`.
- **Scoped user report — HTTPS idle cleanup worked (2026-09-08):** User accepted that the cleanup worked. This supersedes the no-target-acceptance wording for that specific behavior below, not the historical measurements. No additional soak duration, detailed checklist passes, resource reserve approval, broader 8D.11/M3 sign-off or legacy-removal target validation was supplied.
- **User-authorized bounded ordinary HTTPS idle cleanup complete (2026-09-08), host-tested/build-verified; target pending:** Preserved existing 8D.11 key/diagnostic implementation (initial Git status was clean). User capture: post-TLS occupancy 6/6, ordinary4/serial2/admin0, ordinary connection ages 5074 s; admin ticket returned in 14 ms, no TLS/upgrade observed and ticket unconsumed. Installed IDF stops selecting listener at capacity with LRU disabled. `web_httpd_idle.{c,h}` plus sole-private-boundary sweep in `web_httpd_adapter`: six owner-only rows, 15-second observed-idle window (three five-second status polls), one-second ESP timer/at most one queued owner probe, current fd shutdown (no reusable-pointer queued close), all-route SDK request-plus-purge completion marker and input readiness/WS/async checks, TLS-create fd reset, submit fence/nonwrapping restart generations. Diagnostics does not gate policy; socket/timeout/LRU/task/stack/asset settings unchanged. Implementation `pio run` PASS 57.55 s, final confirmation PASS 3.13 s; **99,316 B RAM / 1,708,981 B flash (+160/+1,384 versus diagnostic baseline)**. Rows144 B/module static167 B before placement; timer32 B internal heap before overhead; sweep local frame80 B, runtime margins unmeasured. Final idle **18 + SDK guards**, server **18**, diagnostics **12+1** PASS; cookie accounts/admin/settings/serial-settings, admin transport25/tickets12, store/serial, UI68+CSP and canonical console/account/key/lifecycle/policy suites PASS; diff check PASS. Cookie test-double missing `<stdint.h>` fixed and all variants rerun. Exact contracts/audit/tests/resources/target checklist: `docs/https_idle_cleanup.md`. Ordinary ages are connection ages, not proved idle duration. No hard wall-clock/admission guarantee: owner-blocking handshake/parser/send delays cleanup; continuously active ordinary sockets are not evicted. Reported queue errors retry, but accepted-and-lost nonblocking UDP work stays one reserved probe until successful stop/restart (tested), rather than accumulating unsafe delayed work. Failed stop remains detached/owned until retry. No hardware/upload/erase/commit, target acceptance or reserve approval; next is the documented target reproduction/soak, not capacity changes.
- **Authorized 8D.11 admission diagnostic slice complete (2026-09-08), host-tested/build-verified; target pending:** `web_diagnostics.{c,h}` uses public synchronous HTTPS create/close callbacks and four ticket/upgrade wrappers, six fixed metadata slots and a 32-record opt-in numeric ring. Console `web diagnostics enable|disable|show|clear` via UART0/admin SSH; browser policy unchanged. No close/open override, queue probe/task, capacity/timeout/log-level/assets/SDK change. Post-TLS owner-published occupancy only; TLS failures/in-progress handshakes and preaccept backlog remain invisible. Final `pio run` PASS **10.30 s, 99,156 B RAM / 1,707,597 B flash**, **+3,080/+3,912 B** versus recorded key-slice baseline. Ring/table target symbols 2,816/192 B; diagnostic handler/record/TLS callback/show local frames 144/144/160/528 B excluding callees, runtime margins unmeasured. PASS diagnostic **12+1**, server lifecycle **16**, canonical console lifecycle/policy/boundary, canonical account/key transactions, cookie/admin and cookie/accounts, admin transport **25**/tickets **12**, browser **68 plus renderer/CSP**, store/serial and diff check. Exact files/commands/contracts and reproduction checklist in `docs/phase8d11_implementation.md`. Existing key work preserved; no device/commit/upload/erase, timeout diagnosis, target acceptance or reserve approval claimed. Next is user target capture, not capacity changes or 8D.12.
@@ -127,7 +131,7 @@ Based on checked-in source plus `README.md` and `docs/roadmap.md`:
These observations should be checked when touching the relevant area; they are not automatically bugs requiring unrelated cleanup.
- Some source comments still call shared commands UART0-only or call the current local status/control task read-only.
- `USER_DATABASE_LOAD_EMPTY` is only an initialization/failure sentinel at the checked-in revision: every successful `user_database_init()` path returns `STORED` or `MIGRATED_LEGACY`, so `main.c`'s successful "new empty" log branch is unreachable.
- Legacy-removal module contracts now make `USER_DATABASE_LOAD_EMPTY` a successful persisted-empty initialization result; older migration/sentinel observations are superseded. Final startup integration/build evidence is pending.
- SSH startup is currently gated on successful `web_security` initialization even though SSH uses separate host-key material. **Needs verification:** whether this coupling is intentional recovery policy or an accidental startup dependency.
## Items to verify in future work