Add role-based user database administration
This commit is contained in:
@@ -10,6 +10,31 @@ Use these commands from the UART0 `serial-tool>` administration console. Run `he
|
||||
| `reboot` | Drain console output briefly and restart the ESP32. |
|
||||
| `status` | Show quick MAX3243 signal state. |
|
||||
|
||||
## Role-based users (Phase 8A)
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `user status` / `user list` | Show database generation, capacity, administrator/bootstrap state, 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 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. |
|
||||
| `user key add <username>` | Prompt for one bounded OpenSSH public-key line. |
|
||||
| `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. |
|
||||
|
||||
Usernames must match `[a-z][a-z0-9_-]{0,15}`. Passwords contain 12–64 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 belong to only 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 existing shared `admin` HTTPS/SSH credential is imported as a role-`user` account, not silently granted administrator rights. Run `user bootstrap` from physical UART0 to establish the administrator. Before bootstrap, `web credentials rotate --force` and `web reset --force` synchronize the migrated verifier; reboot reconciliation retries an interrupted synchronization. After bootstrap, the legacy HTTPS/SSH credential and user-database passwords are intentionally separate until Phase 8B performs the authentication cutover. SSH public-key login is likewise enabled in Phase 8B, not Phase 8A.
|
||||
|
||||
`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.
|
||||
|
||||
## Local display
|
||||
|
||||
| Command | Description |
|
||||
@@ -90,13 +115,13 @@ Opening `/dev/ttyACM*` with DTR asserted creates the `usb-cdc` broker client, st
|
||||
| `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 physical-console-only shared admin credentials. |
|
||||
| `web credentials rotate --force` | Replace credentials and disconnect web and SSH sessions. |
|
||||
| `web credentials show` | Display the physical-console-only legacy Phase 8A network credential. |
|
||||
| `web credentials rotate --force` | Replace the legacy credential, apply pre-bootstrap migration synchronization, and disconnect web and SSH sessions. |
|
||||
| `web certificate info` | Display certificate identity and fingerprint. |
|
||||
| `web certificate rotate --force` | Replace the HTTPS certificate and private key. |
|
||||
| `web reset --force` | Explicitly replace missing, incompatible, or damaged web material. |
|
||||
| `web reset --force` | Explicitly replace missing, incompatible, or damaged legacy credentials and web material. |
|
||||
|
||||
HTTPS listens on port 443 only. Authenticate to `https://<device-address>/` as `admin` with the credential shown on UART0. The device serves its vendored xterm.js terminal without Internet access. Browser sessions use binary WebSocket frames and follow the broker's one-writer rule.
|
||||
HTTPS listens on port 443 only. During Phase 8A, authenticate to `https://<device-address>/` as `admin` with the legacy credential shown on UART0; role-aware user-database authentication begins in Phase 8B. The device serves its vendored xterm.js terminal without Internet access. Browser sessions use binary WebSocket frames and follow the broker's one-writer rule.
|
||||
|
||||
## SSH serial transport
|
||||
|
||||
@@ -112,7 +137,7 @@ HTTPS listens on port 443 only. Authenticate to `https://<device-address>/` as `
|
||||
| `ssh host-key rotate --force` | Replace the persistent SSH host key. |
|
||||
| `ssh reset --force` | Explicitly replace invalid or missing SSH material. |
|
||||
|
||||
SSH listens on port 22 and uses the same `admin` credentials as HTTPS, but a separate host key. It accepts password-authenticated interactive shell/PTY sessions only; it does not provide public-key authentication, `exec`, SFTP, SCP, forwarding, or subsystems. Verify the fingerprint from `ssh host-key info` before accepting an SSH host key.
|
||||
During Phase 8A, SSH listens on port 22 and uses the same legacy `admin` credential as HTTPS, but a separate host key. It accepts password-authenticated interactive shell/PTY serial sessions only. Stored per-user public keys and roles become active in Phase 8B/8C; Phase 8A does not yet provide public-key login or the administrative SSH shell. SSH does not provide `exec`, SFTP, SCP, forwarding, or subsystems. Verify the fingerprint from `ssh host-key info` before accepting an SSH host key.
|
||||
|
||||
## Hardware diagnostics
|
||||
|
||||
|
||||
@@ -186,6 +186,89 @@ Exercise the following fault matrix while keeping UART0 available:
|
||||
|
||||
For the final concurrency regression, connect USB CDC, one WebSocket terminal, and one SSH terminal while UART1 carries sustained bidirectional traffic. Navigate the UI, run the persistence checks, exercise service stop/start and writer revocation, and inject the display/stuck-button faults above. Confirm UART0 remains responsive, the UI never appears as a broker client or injects serial bytes, writer ownership is always the expected client or none, and serial/broker drop counters do not increase unexpectedly. Record `memory`, transport counters, `wifi counters`, and `debug display status` before and after the run.
|
||||
|
||||
## Phase 8A role-based user database and UART0 administration
|
||||
|
||||
Keep UART0 physically attached throughout these tests. Phase 8A deliberately does not change HTTPS or SSH authentication: existing network sessions still use the legacy shared credential, while the new database and public keys are prepared for the Phase 8B cutover. Do not include generated or entered passwords in test logs.
|
||||
|
||||
### 1. First-boot migration and persistence
|
||||
|
||||
Upgrade a device that already has working HTTPS/SSH credentials and no `user_db` blob. After boot:
|
||||
|
||||
1. Run `user status` and `user show admin`.
|
||||
2. Confirm exactly one `admin`-named account exists with role `user`, `bootstrapped=no`, no displayed password/salt/verifier, and no public keys.
|
||||
3. Confirm the pre-upgrade credential still authenticates to HTTPS and SSH and that serial terminal behavior is unchanged.
|
||||
4. Reboot and confirm `user status` reports the same account ID and authentication generation.
|
||||
5. Confirm routine boot logs, `status`, `web status`, and `ssh status` contain no user password, verifier, salt, or raw public-key blob.
|
||||
|
||||
A transient failure reading the legacy web credential must not create or commit an empty user database. If a valid `user_db` blob already exists, it must still load without the legacy credential being readable.
|
||||
|
||||
### 2. Administrator bootstrap
|
||||
|
||||
Run `user bootstrap`, verify that typed password characters are not echoed, enter a 12–64 character printable password twice, and confirm `user show admin` now reports role `admin` and a higher authentication generation. Reboot and confirm `bootstrapped=yes` and one administrator persist. On a separate resettable test image, exercise `user bootstrap --generate`; record the generated password securely for the test, verify it is displayed once, then clear the terminal scrollback after use.
|
||||
|
||||
During Phase 8A, verify the newly bootstrapped password does **not** replace the legacy HTTPS/SSH login yet and the existing network credential continues to work. This separation is intentional until Phase 8B. A second `user bootstrap` must be rejected without changing the account.
|
||||
|
||||
### 3. Account CRUD, capacity, and invariants
|
||||
|
||||
Use generated test passwords where practical:
|
||||
|
||||
```text
|
||||
user add operator user --generate
|
||||
user add maint admin --generate
|
||||
user list
|
||||
user show operator
|
||||
user role operator admin --force
|
||||
user role operator user --force
|
||||
user password operator --generate
|
||||
```
|
||||
|
||||
Confirm unique valid usernames are accepted; uppercase, empty, oversized, or punctuation-invalid usernames are rejected. Confirm duplicate users are rejected. Fill all eight slots and verify a ninth account is rejected without altering existing records; delete a non-final account and confirm its slot can be reused with a new account ID.
|
||||
|
||||
With two administrators, demote or delete one and confirm the operation persists. Then attempt both deletion and demotion of the final administrator and confirm both are rejected. Before bootstrap on a separate migration run, verify `user delete admin --force` is rejected so legacy synchronization remains recoverable. `Ctrl-C` during either hidden password prompt must cancel without changing the account or leaving stale input in the next console command.
|
||||
|
||||
### 4. SSH public-key import and fingerprints
|
||||
|
||||
Generate disposable Ed25519 and ECDSA P-256 OpenSSH keys on a trusted host. For each supported type, run `user key add <username>`, paste the single `.pub` line, and confirm `user show <username>` reports the type, stable slot index, and expected `SHA256:` fingerprint without displaying the Base64 blob or comment.
|
||||
|
||||
Verify all of the following fail without changing the account generation or key list:
|
||||
|
||||
- Unsupported RSA, certificate, or unknown key types.
|
||||
- Truncated/invalid Base64, mismatched outer and embedded types, trailing blob data, malformed Ed25519 lengths, and an invalid/off-curve ECDSA point.
|
||||
- Adding the same key again to the same account or assigning it to a different account.
|
||||
- Adding a fourth key to an account that already has three.
|
||||
- Deleting an empty/out-of-range slot.
|
||||
|
||||
Then exercise `user key delete <username> <index> --force` and `user key clear <username> --force`, reboot, and confirm the exact remaining fingerprints persist. Phase 8A stores these keys but does not yet accept SSH key login.
|
||||
|
||||
### 5. Legacy credential rotation boundary and reboot reconciliation
|
||||
|
||||
Before administrator bootstrap on a resettable test image:
|
||||
|
||||
1. Run `web credentials rotate --force`.
|
||||
2. Confirm the console reports that the pre-bootstrap migrated verifier was synchronized.
|
||||
3. Verify the new legacy credential works for HTTPS and SSH, reboot, and confirm `user show admin` retains the synchronized authentication generation.
|
||||
4. Where NVS fault injection or controlled power interruption is available, interrupt after the web credential commits but before the user blob commits. On the next boot, confirm reconciliation updates the migrated verifier rather than committing an empty database or losing network recovery.
|
||||
|
||||
After bootstrap, rotate the legacy credential again. Confirm the console explicitly says it remains separate from user passwords, existing network sessions are revoked, the new legacy credential works, and the bootstrapped administrator generation/password are not silently replaced. Apply the same checks to `web reset --force`, additionally confirming HTTPS certificate recovery/restart behavior remains intact.
|
||||
|
||||
### 6. Explicit database recovery and storage failures
|
||||
|
||||
Using a disposable device or test NVS image, inject a wrong-size/version or malformed `user_db` blob and reboot. Confirm normal database initialization fails without erasing shared NVS, but the `user` command is still registered. `user status` must report the database unavailable. Run:
|
||||
|
||||
```text
|
||||
user recover --force
|
||||
user status
|
||||
user bootstrap --generate
|
||||
```
|
||||
|
||||
Confirm recovery explicitly replaces only `user_db`, imports the current legacy credential as role `user`, and leaves serial, Wi-Fi, HTTPS identity, SSH host key, and legacy network login intact. Confirm recovery is rejected while a valid database is initialized. Simulate NVS write/commit exhaustion or failure during create, password, role, key, and recovery operations; after reboot, each operation must expose either the complete old blob or complete new blob, never a partially live mutation.
|
||||
|
||||
Because NVS is not yet encrypted, this test validates logical verifier storage rather than resistance to physical extraction. PBKDF2 verifiers remain susceptible to offline guessing, and append-oriented NVS may retain historical plaintext legacy credentials until a full partition/flash erase.
|
||||
|
||||
### 7. Phase 8A regression
|
||||
|
||||
With the maximum supported mix of USB CDC, WebSocket, and SSH serial clients active, repeat user list/show and several UART0 mutations. Confirm password prompts remain physical-UART-only, no user operation acquires a broker client or writer lease, existing Phase 8A network authentication and revocation behavior remains functional, and serial/broker counters show no unexplained loss. Record `memory`, `web counters`, and `ssh counters` before and after the run.
|
||||
|
||||
## Configuration A: data and handshake pairs
|
||||
|
||||
Connect the following pairs:
|
||||
|
||||
+10
-7
@@ -38,7 +38,7 @@ These constraints apply across all phases:
|
||||
| 5B | Offline xterm.js WebSocket serial terminal | **Complete** |
|
||||
| 6 | Authenticated SSH serial transport | **Complete** |
|
||||
| 7 | Local display and button interface | **In progress (7E validation)** |
|
||||
| 8 | Role-based users and administrative access | **Planned** |
|
||||
| 8 | Role-based users and administrative access | **In progress (8A validation)** |
|
||||
| 9 | Security and production hardening | **Planned** |
|
||||
| 10 | Authenticated, rollback-capable OTA | **Planned** |
|
||||
| 11 | BLE serial transport and provisioning evaluation | **Planned** |
|
||||
@@ -179,7 +179,7 @@ The software-crypto build no longer reproduces the HTTPD watchdog stall. This va
|
||||
|
||||
## Current and planned phases
|
||||
|
||||
The order below is the current plan. Phase 7 is in progress; later phases remain planned or under evaluation. Detailed requirements should be finalized at the start of each phase, and optional features must not weaken the completed serial and recovery paths.
|
||||
The order below is the current plan. Phase 7 is in progress, Phase 8A is implemented and awaiting target-hardware validation, and later work remains planned or under evaluation. Detailed requirements should be finalized at the start of each phase, and optional features must not weaken the completed serial and recovery paths.
|
||||
|
||||
### Phase 7 — Local display and buttons
|
||||
|
||||
@@ -244,11 +244,14 @@ Replace the single shared network credential with a bounded, persistent user sys
|
||||
|
||||
Implementation sequence:
|
||||
|
||||
1. **Phase 8A — User database and UART0 administration — Planned**
|
||||
- Add a versioned, fixed-capacity user database with at most eight accounts, unique lowercase usernames, `user`/`admin` roles, per-account generations, and an invariant that prevents deletion or demotion of the final administrator.
|
||||
- Store salted password verifiers rather than recoverable passwords. Support bounded hidden password entry and one-time generated passwords through UART0 commands for add/delete, role changes, password changes, and account inspection.
|
||||
- Permit up to three SSH public keys per account. UART0 imports bounded OpenSSH public-key lines and lists fingerprints without exposing password verifiers or raw secret material.
|
||||
- Migrate the existing shared credential transactionally so network recovery is preserved across power loss; document that append-oriented unencrypted NVS cannot guarantee physical erasure of historical plaintext entries.
|
||||
1. **Phase 8A — User database and UART0 administration — Implemented; validation pending**
|
||||
- A versioned `user_db` NVS blob holds at most eight accounts with unique lowercase usernames, `user`/`admin` roles, random account IDs, per-account authentication generations, and fixed storage bounds. The final administrator cannot be deleted or demoted.
|
||||
- Passwords use random 16-byte salts and PBKDF2-HMAC-SHA256 verifiers rather than recoverable plaintext. UART0 provides bounded no-echo entry, confirmation, and one-time 24-character generated passwords for bootstrap, create, and password-change operations. Valid authentication attempts perform one real or dummy PBKDF2 outside the database mutex and revalidate the account generation before returning a principal.
|
||||
- Each account accepts up to three bounded OpenSSH public keys. Initial import supports Ed25519 and ECDSA P-256, validates key structure and P-256 points, rejects a key already assigned to any account, and exposes only key type plus SHA-256 fingerprint in snapshots and status output.
|
||||
- On first boot after upgrade, the existing shared `admin` network credential is transactionally imported as a role-`user` account; it is never silently elevated. `user bootstrap` sets a new password and promotes that account to `admin`. Until bootstrap, legacy web credential rotation/reset synchronizes the imported verifier and boot reconciliation repairs an interrupted second NVS commit. After bootstrap, Phase 8A deliberately leaves the legacy HTTPS/SSH credential separate until Phase 8B switches network authentication to the common database.
|
||||
- `user recover --force` remains registered even when a malformed or incompatible user blob prevents normal initialization. From physical UART0 it explicitly replaces only the user-database blob with a role-`user` account derived from the current legacy network credential; it never automatically erases shared NVS.
|
||||
- NVS remains unencrypted. Verifiers improve password-at-rest handling, but offline guessing remains possible and append-oriented flash may retain historical plaintext legacy credentials until the partition or flash is physically erased.
|
||||
- Pending target-hardware validation covers migration and reboot persistence, CRUD/capacity/final-admin invariants, hidden and generated passwords, malformed/duplicate/capacity-limited key imports, legacy-rotation reconciliation, explicit recovery, NVS failures, and regression of existing HTTPS/SSH access.
|
||||
2. **Phase 8B — Role-aware HTTPS and SSH authentication — Planned**
|
||||
- Authenticate HTTPS and SSH passwords through the common user database and retain a secret-free principal containing account ID, role, authentication method, and credential generation.
|
||||
- Add SSH public-key authentication while retaining password authentication. wolfSSH authorizes the stored username/key pair and remains responsible for cryptographic signature verification; unsigned key probes never count as completed authentication.
|
||||
|
||||
Reference in New Issue
Block a user