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
+11 -13
View File
@@ -1,8 +1,8 @@
# Command reference
UART0 and authenticated `admin` SSH sessions use the same registered command implementations through one serialized dispatcher. Admin SSH exposes the full operational registry, including interactive prompts, recovery-secret display, network diagnostics, reboot, and HTTPS/SSH material mutation. Initial administrator bootstrap and explicit recovery of an unavailable user database remain physically bound to UART0. Admin SSH also rejects generating a replacement password for its own account so the one-time value cannot be lost when the session is revoked. Run `help` for root commands and `<group> help` for a group summary. Configuration changes are RAM-only unless explicitly saved.
UART0 and authenticated `admin` SSH sessions use the same registered command implementations through one serialized dispatcher. Admin SSH exposes the full operational registry, including interactive prompts, network diagnostics, reboot, and HTTPS/SSH material mutation. Create the first administrator through normal `user add <username> admin` on physical UART0; explicit recovery of an unavailable database is UART0-only. Admin SSH also rejects generating a replacement password for its own account so the one-time value cannot be lost when the session is revoked. Run `help` for root commands and `<group> help` for a group summary. Configuration changes are RAM-only unless explicitly saved.
Browser-admin uses the same dispatcher with temporary 8D.7 restrictions. It now supports deferred **`reboot`** and **`web stop`** in addition to admin-only `exit`. The control task waits up to ten seconds for application output drain plus 200 ms; acknowledgement delivery is best effort, not confirmed receipt. Input observed during deferral is discarded. `web stop` closes both browser routes; restart HTTPS via UART0/admin SSH `web start` and sign in again. Reboot affects all transports and loses RAM-only changes. The second slice also permits exact parsed **`web certificate rotate --force`** (equivalent quoting accepted, no missing force or extra arguments). After drain/delay it queues a typed action on the existing dispatcher; pending input remains discarded until completion. This does not bound dispatcher wait or certificate/NVS/lifecycle execution time. Certificate commit precedes HTTPS stop/start: early generation/persistence errors skip restart, stop failure retains ownership and skips start, and later lifecycle failure does not undo the new persisted identity. Success closes both browser routes and any web writer lease; verify/trust the new certificate through a trusted channel and sign in again with unchanged account credentials. USB/UART0/SSH remain usable; use UART0/admin SSH for stop/start recovery if needed. Other `web` forms besides `web status`, `web stop` and this exact rotation remain blocked (including certificate info, credential display/rotation and reset). User mutations, Wi-Fi/mDNS mutations and restricted SSH lifecycle commands remain unavailable from browser admin. See `phase8d7_implementation.md` for the exact remaining policy and target-validation status.
Browser-admin uses the same dispatcher with temporary 8D.7 restrictions. It now supports deferred **`reboot`** and **`web stop`** in addition to admin-only `exit`. The control task waits up to ten seconds for application output drain plus 200 ms; acknowledgement delivery is best effort, not confirmed receipt. Input observed during deferral is discarded. `web stop` closes both browser routes; restart HTTPS via UART0/admin SSH `web start` and sign in again. Reboot affects all transports and loses RAM-only changes. The second slice also permits exact parsed **`web certificate rotate --force`** (equivalent quoting accepted, no missing force or extra arguments). After drain/delay it queues a typed action on the existing dispatcher; pending input remains discarded until completion. This does not bound dispatcher wait or certificate/NVS/lifecycle execution time. Certificate commit precedes HTTPS stop/start: early generation/persistence errors skip restart, stop failure retains ownership and skips start, and later lifecycle failure does not undo the new persisted identity. Success closes both browser routes and any web writer lease; verify/trust the new certificate through a trusted channel and sign in again with unchanged account credentials. USB/UART0/SSH remain usable; use UART0/admin SSH for stop/start recovery if needed. Other `web` forms besides `web status`, `web stop` and this exact rotation remain blocked (including certificate info and reset; legacy credential commands are removed). User mutations, Wi-Fi/mDNS mutations and restricted SSH lifecycle commands remain unavailable from browser admin. See `phase8d7_implementation.md` for the exact remaining policy and target-validation status.
## System
@@ -16,13 +16,11 @@ Browser-admin uses the same dispatcher with temporary 8D.7 restrictions. It now
| Command | Description |
|---|---|
| `user status` / `user list` | Show database generation, capacity, administrator/bootstrap state, and all secret-free account summaries. |
| `user status` / `user list` | Show database generation, capacity, administrator count, and all secret-free account summaries. |
| `user show <username>` | Show one account's role, ID, authentication generation, and SSH-key fingerprints. |
| `user bootstrap` | Set and confirm the `admin` password without echo, then promote the migrated account to `admin`. |
| `user bootstrap --generate` | Bootstrap `admin` with a generated 24-character password displayed once. |
| `user add <username> <user|admin>` | Create an account using a bounded no-echo password and confirmation prompt. |
| `user add <username> <user|admin> --generate` | Create an account with a generated password displayed once. |
| `user delete <username> --force` | Delete an account; the pre-bootstrap migrated `admin` and final administrator are protected. |
| `user delete <username> --force` | Delete an account; the final administrator is protected. |
| `user role <username> <user|admin> --force` | Change a role; the final administrator cannot be demoted. |
| `user password <username>` | Set and confirm a new password without echo. |
| `user password <username> --generate` | Replace a password with a generated value displayed once. |
@@ -30,13 +28,13 @@ Browser-admin uses the same dispatcher with temporary 8D.7 restrictions. It now
| `user key add <username> <type> <base64>` | Import a key non-interactively; intended for authenticated admin SSH and also accepted on UART0. |
| `user key delete <username> <0..2> --force` | Delete one key by the index shown by `user show`. |
| `user key clear <username> --force` | Delete all public keys for an account. |
| `user recover --force` | When normal user-database initialization failed, explicitly replace its blob from the current legacy network credential. |
| `user recover --force` | When normal user-database initialization failed, explicitly replace only its blob with an empty database; UART0-only, refuses a healthy database. |
Usernames must match `[a-z][a-z0-9_-]{0,15}`. Passwords contain 1264 printable ASCII characters. The fixed database supports eight users and three SSH keys per user; initial key types are `ssh-ed25519` and `ecdsa-sha2-nistp256`. A key may be assigned to multiple accounts but cannot be duplicated within one account. Password verifiers, salts, raw key blobs, and passwords are absent from ordinary status output. `Ctrl-C` cancels a password or key prompt, and generated passwords are shown once.
On the first Phase 8A boot, the old shared `admin` credential is imported as a role-`user` account, not silently granted administrator rights. Run `user bootstrap` from physical UART0 to establish the administrator. Phase 8B now authenticates HTTPS and SSH passwords through this database and enables stored SSH public keys. Before bootstrap, `web credentials rotate --force` and `web reset --force` synchronize the migrated verifier; after bootstrap, that legacy credential is recovery-only and does not authenticate or alter role-based users.
Missing `user_db/database` storage is committed empty. On UART0 run `user add <username> admin`, optionally with `--generate`, to create the first administrator. There is no bootstrap command, imported shared credential, or synchronization with HTTPS material. Existing valid v1 user databases load unchanged, including previously migrated role-`user` accounts; no account is silently promoted.
`user recover --force` is a destructive physical recovery operation and succeeds only while the database is unavailable. It replaces the user blob with one role-`user` account derived from the current legacy credential; run `user bootstrap` afterward. It does not erase unrelated NVS data. Successful password, role, key, bootstrap, and delete operations invalidate only that username's outstanding WebSocket tickets and active WebSocket/SSH sessions; unrelated users remain connected.
`user recover --force` is destructive and succeeds only while the database is unavailable. It rebuilds only the user blob empty, importing no credentials; then create an administrator with normal `user add` on UART0. It refuses a healthy initialized database, including a healthy empty one, and does not erase unrelated NVS data. Successful password, role, key and delete operations invalidate only that username's outstanding tickets and active web/SSH sessions; unrelated users remain connected.
## Local display
@@ -129,11 +127,9 @@ When the Wi-Fi station receives an IPv4 address, the Wi-Fi manager announces `sa
| `web status` | Show HTTPS and WebSocket state. |
| `web start` / `web stop` | Start or stop HTTPS service. |
| `web counters` / `web clear-counters` | Show or clear web counters. |
| `web credentials show` | Display the legacy migration/recovery credential on UART0 or authenticated admin SSH; it is not a role-based network login. |
| `web credentials rotate --force` | Replace the legacy recovery credential and synchronize the migrated pre-bootstrap account only. |
| `web certificate info` | Display certificate identity and fingerprint. |
| `web certificate rotate --force` | Replace the HTTPS certificate and private key. Browser admin defers commit and HTTPS restart; both browser routes close and new certificate trust/relogin is required. UART0/admin SSH behavior is unchanged. |
| `web reset --force` | Explicitly replace missing, incompatible, or damaged legacy credentials and web material. |
| `web reset --force` | Explicitly replace HTTPS certificate/private key only, including missing, incompatible or damaged material; never changes users. |
HTTPS listens on port 443 only. Authenticate with any current user-database username/password; both `user` and `admin` roles receive the existing status and browser-terminal interface. The device serves vendored xterm.js without Internet access. Browser sessions use one-time account-bound tickets, binary WebSocket frames, and the broker's one-writer rule. The combined **Connect**/**Disconnect** control closes the current WebSocket and pauses automatic reconnect when active; after a user-paused disconnect, it changes to **Connect** to resume connection attempts. Account mutations revoke only that account's tickets and sessions.
@@ -157,7 +153,7 @@ UART0 and admin SSH submit to one bounded queue, and one dispatcher task is the
UART0 and admin SSH use shared whole-line Tab completion. A unique/common prefix expands inline; a Tab that cannot extend an ambiguous prefix prints the matching candidates and redraws the unchanged input line instead of cycling candidates. Admin SSH additionally supports four-entry per-session command history with Up/Down, inline cursor editing with Left/Right, Home/End (including Pos1/Ende terminal sequences), Backspace/Delete, Ctrl-C, and visible or no-echo interactive prompts. Its history is RAM-only, private to the session, and wiped on disconnect. Ping callbacks enqueue bounded typed results so all formatting remains on the dispatcher task.
`exit`, `reboot`, `ssh stop`, session disconnect, and SSH host-key reset/rotation use bounded deferred control. The firmware waits on a best-effort basis for the administration output ring and transport TX buffer to drain before acting; this is not confirmation that the peer received the acknowledgement. The shell stops accepting another command while such an action is pending. SSH host-key replacement or service stop closes all SSH sessions; reconnect and verify the new fingerprint where applicable. Web recovery credentials/certificates, Wi-Fi secrets, and interactive user passwords/keys are available to authenticated administrators and must therefore be treated as remotely accessible administrative material. `user bootstrap` and `user recover --force` remain UART0-only. A connected administrator also cannot generate its own replacement password remotely, preventing the one-time password from being lost during self-revocation. SSH does not provide `exec`, SFTP, SCP, forwarding, or subsystems.
`exit`, `reboot`, `ssh stop`, session disconnect, and SSH host-key reset/rotation use bounded deferred control. The firmware waits on a best-effort basis for the administration output ring and transport TX buffer to drain before acting; this is not confirmation that the peer received the acknowledgement. The shell stops accepting another command while such an action is pending. SSH host-key replacement or service stop closes all SSH sessions; reconnect and verify the new fingerprint where applicable. TLS certificate management, Wi-Fi secrets, and interactive user passwords/keys are available to authenticated administrators and must therefore be treated as remotely accessible administrative material. `user recover --force` remains UART0-only; `user bootstrap` and all `web credentials` commands are removed. A connected administrator also cannot generate its own replacement password remotely, preventing the one-time password from being lost during self-revocation. SSH does not provide `exec`, SFTP, SCP, forwarding, or subsystems.
## Hardware diagnostics
@@ -184,3 +180,5 @@ UART0 and admin SSH use shared whole-line Tab completion. A unique/common prefix
| `debug buttons test [seconds]` | Run the bounded button event test for 130 seconds; the default is 10 seconds. |
Follow the exact wiring in [Electrical tests](electrical_tests.md) before invoking diagnostics. The OLED must be powered from 3.3 V because module I²C pull-ups may connect to `VCC`; verify that all external pull-ups also terminate at 3.3 V. Display diagnostics probe the standard SSD1315-compatible 7-bit `0x3c`/`0x3d` addresses. The currently tested module acknowledges at `0x3c`, whose 8-bit write/read forms are `0x78`/`0x79`; an explicit `scan --force` is available only for the dedicated local-UI bus. Diagnostics initially run at 100 kHz and treat an absent display as nonfatal. RS-232 diagnostics that require UART1 refuse to use it until `serial stop` releases it. The RGB LED shows test state: blue idle, yellow/orange running, green passed, red failed.
HTTPS storage migration preserves the exact TLS identity and commits TLS-only v2 before publication. Older v1-only firmware cannot read v2. Logical NVS replacement is not secure flash erasure; no factory erase is required. See [legacy removal](legacy_credential_removal.md).