Implement role-aware HTTPS and SSH authentication

This commit is contained in:
2026-08-30 01:31:05 +02:00
parent cd235445c7
commit 0c058b6a8f
16 changed files with 707 additions and 331 deletions
+30 -36
View File
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0-only */
/* UART0 HTTPS lifecycle, shared credentials, certificate, and recovery commands. */
/* UART0 HTTPS lifecycle, legacy recovery credential, and certificate commands. */
#include "web_console.h"
@@ -43,22 +43,20 @@ static int show_status(void)
return 1;
}
char username[WEB_SECURITY_USERNAME_CAPACITY + 1U] = {0};
size_t username_length = 0U;
esp_err_t security_error = web_security_copy_username(
username, sizeof(username), &username_length);
user_database_snapshot_t users;
esp_err_t users_error = user_database_get_snapshot(&users);
printf("HTTPS: initialized=%s running=%s transitioning=%s port=%u last-error=%s\n",
snapshot.initialized ? "yes" : "no",
snapshot.running ? "yes" : "no",
snapshot.transitioning ? "yes" : "no",
(unsigned int)snapshot.port,
esp_err_to_name(snapshot.last_error));
if (security_error == ESP_OK) {
printf("Authentication: HTTP Basic over TLS, username=%.*s, shared with SSH\n",
(int)username_length, username);
if (users_error == ESP_OK) {
printf("Authentication: HTTP Basic over TLS via user database, users=%u admins=%u\n",
(unsigned int)users.user_count, (unsigned int)users.admin_count);
} else {
printf("Authentication material unavailable: %s; use 'web reset --force' to replace it.\n",
esp_err_to_name(security_error));
printf("Authentication database unavailable: %s; use 'user recover --force'.\n",
esp_err_to_name(users_error));
}
printf("Endpoints: GET /, GET /api/status, POST /api/ws-ticket, WSS /ws/serial\n");
@@ -81,11 +79,20 @@ static int show_status(void)
if (!session->active) {
continue;
}
printf(" slot=%u fd=%d generation=%" PRIu32 " broker=%" PRIu32
" role=%s tx-pending=%s closing=%s\n",
printf(" slot=%u fd=%d generation=%" PRIu32 " account=%s user-role=%s"
" method=%s broker=%" PRIu32
" broker-role=%s tx-pending=%s closing=%s\n",
(unsigned int)index,
session->socket_fd,
session->generation,
session->principal_valid ? session->username : "-",
session->principal_valid
? user_role_to_string(session->user_role)
: "-",
session->principal_valid &&
session->auth_method == USER_AUTH_METHOD_PASSWORD
? "password"
: "unknown",
session->broker_client_id,
session->writer ? "writer" : "observer",
session->tx_pending ? "yes" : "no",
@@ -169,7 +176,8 @@ static int show_credentials(void)
credentials.username);
printf("Password: %.*s\n", (int)credentials.password_length,
credentials.password);
printf("These credentials protect HTTPS and SSH. Keep them private.\n");
printf("Phase 8B uses the user database for HTTPS and SSH authentication.\n");
printf("This legacy credential is retained only for migration and physical recovery.\n");
secure_wipe(&credentials, sizeof(credentials));
return 0;
}
@@ -241,6 +249,12 @@ static void synchronize_migrated_user(
return;
}
if (synchronized) {
(void)web_serial_transport_revoke_user(
(const uint8_t *)credentials->username,
credentials->username_length);
(void)ssh_transport_revoke_user(
(const uint8_t *)credentials->username,
credentials->username_length);
printf("The pre-bootstrap migrated user credential was synchronized.\n");
return;
}
@@ -248,7 +262,7 @@ static void synchronize_migrated_user(
user_database_snapshot_t snapshot;
if (user_database_get_snapshot(&snapshot) == ESP_OK &&
snapshot.admin_bootstrapped) {
printf("Phase 8A note: this legacy HTTPS/SSH credential is separate from bootstrapped user passwords until Phase 8B.\n");
printf("This legacy recovery credential is separate from role-based user passwords.\n");
} else {
printf("Warning: no matching pre-bootstrap migrated user was synchronized; establish an administrator with 'user bootstrap'.\n");
}
@@ -264,17 +278,7 @@ static int rotate_credentials(void)
}
synchronize_migrated_user(&credentials);
esp_err_t web_revoke_error = web_serial_transport_revoke_sessions();
esp_err_t ssh_revoke_error = ssh_transport_revoke_sessions();
printf("Administrative credentials rotated and persisted. Existing HTTPS and SSH credentials are now invalid.\n");
if (web_revoke_error != ESP_OK && web_revoke_error != ESP_ERR_INVALID_STATE) {
printf("Warning: existing WebSocket sessions could not be revoked: %s\n",
esp_err_to_name(web_revoke_error));
}
if (ssh_revoke_error != ESP_OK && ssh_revoke_error != ESP_ERR_INVALID_STATE) {
printf("Warning: existing SSH sessions could not be revoked: %s\n",
esp_err_to_name(ssh_revoke_error));
}
printf("Legacy migration/recovery credential rotated and persisted.\n");
printf("Username: %.*s\nPassword: %.*s\n",
(int)credentials.username_length, credentials.username,
(int)credentials.password_length, credentials.password);
@@ -311,17 +315,7 @@ static int reset_material(void)
}
synchronize_migrated_user(&credentials);
esp_err_t web_revoke_error = web_serial_transport_revoke_sessions();
esp_err_t ssh_revoke_error = ssh_transport_revoke_sessions();
printf("Administrative credentials, HTTPS certificate, and HTTPS private key replaced and persisted.\n");
if (web_revoke_error != ESP_OK && web_revoke_error != ESP_ERR_INVALID_STATE) {
printf("Warning: existing WebSocket sessions could not be revoked: %s\n",
esp_err_to_name(web_revoke_error));
}
if (ssh_revoke_error != ESP_OK && ssh_revoke_error != ESP_ERR_INVALID_STATE) {
printf("Warning: existing SSH sessions could not be revoked: %s\n",
esp_err_to_name(ssh_revoke_error));
}
printf("Legacy recovery credential, HTTPS certificate, and HTTPS private key replaced and persisted.\n");
printf("Username: %.*s\nPassword: %.*s\n",
(int)credentials.username_length, credentials.username,
(int)credentials.password_length, credentials.password);