feat: add bounded admin WebSocket backend (Phase 8D.5)
- Require current admin cookie sessions, Origin checks and single-use tickets - Reuse the shared console with session-aware authorization and slot allocation - Add HTTPD-owned I/O, bounded buffering and revocation cleanup - Prevent LRU eviction of serial clients and stale admin socket closure - Reject unsupported web-shell mutations before side effects - Add host regressions, a smoke client and resource accounting Validated by user sign-off after a 15-minute full-client soak at 230400 baud, with a few broker drops under heavy output. Browser UI remains for Phase 8D.6; numeric memory reserves remain open.
This commit is contained in:
+23
-3
@@ -17,6 +17,9 @@ extern "C" {
|
||||
/* Fits the longest supported ECDSA P-256 OpenSSH key import command. */
|
||||
#define ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY 256U
|
||||
|
||||
#define ADMIN_CONSOLE_TRANSPORT_SSH 0U
|
||||
#define ADMIN_CONSOLE_TRANSPORT_WEB 1U
|
||||
|
||||
typedef struct {
|
||||
uint8_t slot_index;
|
||||
uint32_t session_id;
|
||||
@@ -36,12 +39,15 @@ typedef enum {
|
||||
|
||||
/* Small owner boundary; module/API names are retained for existing SSH callers.
|
||||
* Exactly two shared console slots, not two per transport. slot_index addresses
|
||||
* this pool; owners coordinate admission and must not reuse an identity while
|
||||
* this pool; open_available atomically selects a free slot. Owners must not reuse an identity while
|
||||
* old work can exist. transport is a firmware-assigned namespace (0 = SSH).
|
||||
* An occupied or still-executing slot cannot be replaced by open_owned().
|
||||
*
|
||||
* The immutable adapter lives for firmware lifetime. Callbacks run on the
|
||||
* control task OUTSIDE console locks, never on the dispatcher or socket owner.
|
||||
* control task OUTSIDE console locks for drained/perform. Required is_current
|
||||
* runs on the dispatcher outside console locks; it must be bounded and validate
|
||||
* full transport identity, originating-session liveness and principal binding,
|
||||
* without calling socket libraries or handlers. Core separately checks accounts.
|
||||
* drained must be nonblocking, validate the full identity and include pending
|
||||
* owner output. perform must revalidate identity and marshal lifecycle work to
|
||||
* its owner, never call socket libraries here. Neither callback may call console
|
||||
@@ -53,7 +59,10 @@ typedef enum {
|
||||
* parallel. Shared completion scratch is nonblocking/serialized by the core.
|
||||
* The owner alone consumes output, maintains authentication/session liveness,
|
||||
* and calls close on disconnect/revocation. Core copies/rechecks principals at
|
||||
* admission and dispatch, but does not implement transport-specific expiry.
|
||||
* admission and dispatch. Dispatch and prompts also check owner currentness;
|
||||
* blocked prompts recheck every 250ms (plus check/scheduling latency). This does
|
||||
* not cancel or roll back arbitrary executing handlers. Admission remains the
|
||||
* owner's responsibility; is_current need not accept unpublished admission.
|
||||
* Close wakes prompts; executing state is retained until the handler returns.
|
||||
* Output remains bounded (5s write backpressure); deferred work waits at most
|
||||
* 10s for application drain plus 200ms, NOT peer-delivery confirmation.
|
||||
@@ -61,11 +70,22 @@ typedef enum {
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t supported_actions;
|
||||
bool (*is_current)(const admin_ssh_console_token_t *token,
|
||||
const user_principal_t *principal);
|
||||
bool (*drained)(const admin_ssh_console_token_t *token);
|
||||
esp_err_t (*perform)(const admin_ssh_console_token_t *token,
|
||||
admin_ssh_deferred_action_type_t action, uint32_t argument);
|
||||
} admin_console_owner_t;
|
||||
|
||||
/* Selects any inactive, nonexecuting slot from the shared two-slot pool.
|
||||
* Input slot_index is ignored; only slot_index changes, and only on success.
|
||||
* Caller supplies transport/session_id/slot_generation and must retain the
|
||||
* returned token. Full pool returns ESP_ERR_INVALID_STATE, like open_owned.
|
||||
*/
|
||||
esp_err_t admin_ssh_console_open_available(admin_ssh_console_token_t *token,
|
||||
const user_principal_t *principal,
|
||||
const admin_console_owner_t *owner);
|
||||
|
||||
esp_err_t admin_ssh_console_open_owned(const admin_ssh_console_token_t *token,
|
||||
const user_principal_t *principal,
|
||||
const admin_console_owner_t *owner);
|
||||
|
||||
Reference in New Issue
Block a user