Add Authenticated HTTPS Admin Foundation
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
/* SPDX-License-Identifier: GPL-3.0-only */
|
||||
/* UART0 HTTPS lifecycle, credentials, certificate, and recovery commands. */
|
||||
|
||||
#include "web_console.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_console.h"
|
||||
#include "secure_random.h"
|
||||
#include "web_security.h"
|
||||
#include "web_server.h"
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" web status|start|stop\n");
|
||||
printf(" web counters|clear-counters\n");
|
||||
printf(" web credentials show\n");
|
||||
printf(" web credentials rotate --force\n");
|
||||
printf(" web certificate info\n");
|
||||
printf(" web certificate rotate --force\n");
|
||||
printf(" web reset --force\n");
|
||||
}
|
||||
|
||||
static void print_fingerprint(const uint8_t fingerprint[WEB_SECURITY_SHA256_LENGTH])
|
||||
{
|
||||
for (size_t index = 0U; index < WEB_SECURITY_SHA256_LENGTH; ++index) {
|
||||
printf(index == 0U ? "%02X" : ":%02X", (unsigned int)fingerprint[index]);
|
||||
}
|
||||
}
|
||||
|
||||
static int show_status(void)
|
||||
{
|
||||
web_server_snapshot_t snapshot;
|
||||
esp_err_t error = web_server_get_snapshot(&snapshot);
|
||||
if (error != ESP_OK) {
|
||||
printf("HTTPS runtime unavailable: %s\n", esp_err_to_name(error));
|
||||
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);
|
||||
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, material=ready\n",
|
||||
(int)username_length, username);
|
||||
} else {
|
||||
printf("Authentication material unavailable: %s; use 'web reset --force' to replace it.\n",
|
||||
esp_err_to_name(security_error));
|
||||
}
|
||||
printf("Endpoints: GET / and GET /api/status (authentication required)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_counters(void)
|
||||
{
|
||||
web_server_snapshot_t snapshot;
|
||||
esp_err_t error = web_server_get_snapshot(&snapshot);
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not read HTTPS counters: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const web_server_counters_t *counter = &snapshot.counters;
|
||||
printf("Lifecycle: starts=%" PRIu64 " start-failures=%" PRIu64
|
||||
" stops=%" PRIu64 "\n",
|
||||
counter->starts, counter->start_failures, counter->stops);
|
||||
printf("Requests: total=%" PRIu64 " authenticated=%" PRIu64
|
||||
" auth-failures=%" PRIu64 " root=%" PRIu64
|
||||
" status=%" PRIu64 " response-errors=%" PRIu64 "\n",
|
||||
counter->requests, counter->authenticated_requests,
|
||||
counter->authentication_failures, counter->root_requests,
|
||||
counter->status_requests, counter->response_errors);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_credentials(void)
|
||||
{
|
||||
web_security_credentials_t credentials;
|
||||
esp_err_t error = web_security_show_credentials(&credentials);
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not read web credentials: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Username: %.*s\n", (int)credentials.username_length,
|
||||
credentials.username);
|
||||
printf("Password: %.*s\n", (int)credentials.password_length,
|
||||
credentials.password);
|
||||
printf("These credentials protect HTTPS only. Keep them private.\n");
|
||||
secure_wipe(&credentials, sizeof(credentials));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_certificate(void)
|
||||
{
|
||||
web_security_certificate_metadata_t metadata;
|
||||
esp_err_t error = web_security_get_certificate_metadata(&metadata);
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not read certificate information: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Security material generation=%" PRIu32 " type=ECDSA-P256 signature=SHA-256\n",
|
||||
metadata.material_generation);
|
||||
printf("Subject/issuer CN: %s\n", metadata.common_name);
|
||||
printf("Validity: %sZ to %sZ\n", metadata.not_before, metadata.not_after);
|
||||
printf("SAN: DNS:%s, IP:%u.%u.%u.%u\n",
|
||||
metadata.dns_name,
|
||||
(unsigned int)metadata.ipv4_address[0],
|
||||
(unsigned int)metadata.ipv4_address[1],
|
||||
(unsigned int)metadata.ipv4_address[2],
|
||||
(unsigned int)metadata.ipv4_address[3]);
|
||||
printf("SHA-256 fingerprint: ");
|
||||
print_fingerprint(metadata.sha256_fingerprint);
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool force_is_present(int argc, char **argv, int expected_argc)
|
||||
{
|
||||
return argc == expected_argc && strcmp(argv[expected_argc - 1], "--force") == 0;
|
||||
}
|
||||
|
||||
static int restart_if_running(bool was_running)
|
||||
{
|
||||
if (!was_running) {
|
||||
return 0;
|
||||
}
|
||||
esp_err_t error = web_server_stop();
|
||||
if (error != ESP_OK) {
|
||||
printf("Material changed, but the old TLS server could not stop: %s\n",
|
||||
esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
error = web_server_start();
|
||||
if (error != ESP_OK) {
|
||||
printf("Material changed, but HTTPS could not restart: %s\n",
|
||||
esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rotate_credentials(void)
|
||||
{
|
||||
web_security_credentials_t credentials;
|
||||
esp_err_t error = web_security_rotate_credentials(&credentials);
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not rotate web credentials: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Web credentials rotated and persisted. Existing Basic credentials are now invalid.\n");
|
||||
printf("Username: %.*s\nPassword: %.*s\n",
|
||||
(int)credentials.username_length, credentials.username,
|
||||
(int)credentials.password_length, credentials.password);
|
||||
secure_wipe(&credentials, sizeof(credentials));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rotate_certificate(void)
|
||||
{
|
||||
web_server_snapshot_t snapshot;
|
||||
esp_err_t error = web_server_get_snapshot(&snapshot);
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not inspect HTTPS runtime: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
error = web_security_rotate_certificate();
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not rotate web certificate: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
printf("Web certificate and private key rotated and persisted.\n");
|
||||
return restart_if_running(snapshot.running);
|
||||
}
|
||||
|
||||
static int reset_material(void)
|
||||
{
|
||||
web_server_snapshot_t snapshot;
|
||||
bool was_running = web_server_get_snapshot(&snapshot) == ESP_OK && snapshot.running;
|
||||
web_security_credentials_t credentials;
|
||||
esp_err_t error = web_security_reset_all(&credentials);
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not reset web security material: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Web credentials, certificate, and private key replaced and persisted.\n");
|
||||
printf("Username: %.*s\nPassword: %.*s\n",
|
||||
(int)credentials.username_length, credentials.username,
|
||||
(int)credentials.password_length, credentials.password);
|
||||
secure_wipe(&credentials, sizeof(credentials));
|
||||
if (was_running) {
|
||||
return restart_if_running(true);
|
||||
}
|
||||
|
||||
error = web_server_start();
|
||||
if (error != ESP_OK) {
|
||||
printf("Security material recovered, but HTTPS could not start: %s\n",
|
||||
esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
printf("HTTPS started with the recovered security material.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int command_web(int argc, char **argv)
|
||||
{
|
||||
if (argc == 1 || (argc == 2 && strcmp(argv[1], "help") == 0)) {
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && strcmp(argv[1], "status") == 0) {
|
||||
return show_status();
|
||||
}
|
||||
if (argc == 2 && strcmp(argv[1], "start") == 0) {
|
||||
esp_err_t error = web_server_start();
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not start HTTPS: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
printf("HTTPS started on TCP port 443.\n");
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && strcmp(argv[1], "stop") == 0) {
|
||||
esp_err_t error = web_server_stop();
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not stop HTTPS: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
printf("HTTPS stopped.\n");
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && strcmp(argv[1], "counters") == 0) {
|
||||
return show_counters();
|
||||
}
|
||||
if (argc == 2 && strcmp(argv[1], "clear-counters") == 0) {
|
||||
esp_err_t error = web_server_clear_counters();
|
||||
if (error != ESP_OK) {
|
||||
printf("Could not clear HTTPS counters: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
printf("HTTPS counters cleared.\n");
|
||||
return 0;
|
||||
}
|
||||
if (argc == 3 && strcmp(argv[1], "credentials") == 0 &&
|
||||
strcmp(argv[2], "show") == 0) {
|
||||
return show_credentials();
|
||||
}
|
||||
if (strcmp(argv[1], "credentials") == 0 && argc >= 3 &&
|
||||
strcmp(argv[2], "rotate") == 0) {
|
||||
if (!force_is_present(argc, argv, 4)) {
|
||||
printf("Credential rotation requires: web credentials rotate --force\n");
|
||||
return 1;
|
||||
}
|
||||
return rotate_credentials();
|
||||
}
|
||||
if (argc == 3 && strcmp(argv[1], "certificate") == 0 &&
|
||||
strcmp(argv[2], "info") == 0) {
|
||||
return show_certificate();
|
||||
}
|
||||
if (strcmp(argv[1], "certificate") == 0 && argc >= 3 &&
|
||||
strcmp(argv[2], "rotate") == 0) {
|
||||
if (!force_is_present(argc, argv, 4)) {
|
||||
printf("Certificate rotation requires: web certificate rotate --force\n");
|
||||
return 1;
|
||||
}
|
||||
return rotate_certificate();
|
||||
}
|
||||
if (strcmp(argv[1], "reset") == 0) {
|
||||
if (!force_is_present(argc, argv, 3)) {
|
||||
printf("Full material replacement requires: web reset --force\n");
|
||||
return 1;
|
||||
}
|
||||
return reset_material();
|
||||
}
|
||||
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
esp_err_t web_console_register_commands(void)
|
||||
{
|
||||
const esp_console_cmd_t command = {
|
||||
.command = "web",
|
||||
.help = "Manage authenticated HTTPS and recover web credentials/certificate",
|
||||
.hint = NULL,
|
||||
.func = &command_web,
|
||||
.argtable = NULL,
|
||||
};
|
||||
return esp_console_cmd_register(&command);
|
||||
}
|
||||
Reference in New Issue
Block a user