Add SSH host identity rotation controls
This commit is contained in:
@@ -253,7 +253,7 @@ with tempfile.TemporaryDirectory(prefix="web-cookie-auth-") as directory:
|
||||
*(["-DHOST_BROKER"] if broker else []),
|
||||
*(["-DHOST_SSH_SETTINGS"] if ssh_settings else []),
|
||||
*(["-DHOST_LIFECYCLE"] if lifecycle else []),
|
||||
"-I" + str(tmp), "-I" + str(ROOT / "src"), *map(str, sources), "-lcrypto",
|
||||
"-I" + str(tmp), "-I" + str(ROOT / "src"), *map(str, sources), "-lcrypto", *(["-lmbedcrypto"] if ssh_settings else []),
|
||||
"-o", str(tmp / "test")], check=True, timeout=30)
|
||||
subprocess.run([str(tmp / "test")], check=True, timeout=20)
|
||||
if lifecycle:
|
||||
|
||||
@@ -5,6 +5,15 @@ static uint32_t queued_id;
|
||||
static unsigned mutations, snapshots;
|
||||
static esp_err_t owner_error;
|
||||
static ssh_transport_management_snapshot_t owner_snapshot;
|
||||
esp_err_t ssh_security_get_identity_snapshot(ssh_security_identity_snapshot_t *out) {
|
||||
assert(!host_lock_depth && !on_dispatcher);
|
||||
memset(out, 0, sizeof(*out)); out->metadata.generation = 3;
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t ssh_transport_replace_identity(uint32_t service, uint32_t identity, bool reset, bool *committed) {
|
||||
assert(on_dispatcher && !host_lock_depth && service == 7 && identity == 3 && !reset);
|
||||
++mutations; *committed = owner_error == ESP_OK; return owner_error;
|
||||
}
|
||||
esp_err_t ssh_transport_get_management_snapshot(ssh_transport_management_snapshot_t *out) {
|
||||
assert(!host_lock_depth && !on_dispatcher); ++snapshots;
|
||||
*out = owner_snapshot; return owner_error;
|
||||
@@ -132,6 +141,28 @@ static void ssh_settings_tests(void) {
|
||||
puts("PASS SSH queue deadline, expiry/revocation/currentness failure, HTTPS auth lifecycle fencing");
|
||||
send_fail = true; submit(&admin); send_fail = false; execute(); assert(s_operation.state == OK);
|
||||
operation_begin(&admin, NULL); expect_ssh("200 OK", false); assert(strstr(output, "ok"));
|
||||
const char *rotate = "{\"action\":\"rotate\",\"generation\":7,\"target\":0,\"identity_generation\":3}";
|
||||
const char *bad_rotations[] = {
|
||||
"{\"action\":\"rotate\",\"generation\":7,\"target\":0}",
|
||||
"{\"action\":\"rotate\",\"generation\":7,\"target\":0,\"identity_generation\":0}",
|
||||
"{\"action\":\"rotate\",\"generation\":7,\"target\":9,\"identity_generation\":3}",
|
||||
"{\"action\":\"stop\",\"generation\":7,\"target\":0,\"identity_generation\":3}",
|
||||
"{\"action\":\"rotate\",\"generation\":7,\"target\":0,\"identity_generation\":4294967295}"};
|
||||
for (unsigned i=0;i<sizeof(bad_rotations)/sizeof(*bad_rotations);++i) {
|
||||
operation_begin(&admin,bad_rotations[i]); expect_ssh("400 Bad Request",false);
|
||||
}
|
||||
for (unsigned i=0;i<2;++i) {
|
||||
owner_error=i ? ESP_FAIL : ESP_OK;
|
||||
operation_begin(&admin,rotate); expect_ssh("202 Accepted",false);
|
||||
before=mutations; execute(); assert(mutations==before+1 && s_operation.state==(i ? FAILED : OK));
|
||||
execute(); assert(mutations==before+1);
|
||||
operation_begin(&other,NULL); expect_ssh("401 Unauthorized",false);
|
||||
}
|
||||
owner_error=ESP_OK;
|
||||
operation_begin(&admin,rotate);expect_ssh("202 Accepted",false);
|
||||
before=mutations;web_session_store_invalidate(admin.view.id);execute();assert(s_operation.state==CANCELLED&&mutations==before);
|
||||
admin=mint(&alice);
|
||||
puts("PASS SSH rotation requires both generations, rejects extras/targets/exhaustion, executes once, preserves login isolation and cancels revoked queued work");
|
||||
s_next_id = UINT32_MAX; operation_begin(&admin, disconnect_body); expect_ssh("503 Service Unavailable", false);
|
||||
puts("PASS SSH lost acknowledgement retained result and nonwrapping operation IDs");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user