Add owner-rooted node management

This commit is contained in:
Eric Wendland 2026-05-21 11:29:29 +02:00
commit b941037652
9 changed files with 1001 additions and 52 deletions

View file

@ -71,6 +71,19 @@ fn geth_init_in_temp_home() {
);
}
#[test]
fn owner_init_requires_admin_key_and_signing_key() {
let home = tempfile::tempdir().expect("tempdir");
let output = run_geth(home.path(), &["init", "--node-name", "laptop"]);
assert!(!output.status.success());
assert!(
String::from_utf8_lossy(&output.stderr).contains("owner init requires --admin-key"),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(!home.path().join("geth.sqlite").exists());
}
#[test]
fn geth_status_against_running_daemon() {
let home = tempfile::tempdir().expect("tempdir");
@ -929,6 +942,99 @@ fn keychain_init_can_record_openssh_signatures() {
}
}
#[test]
fn init_owned_node_records_signed_owner_device_and_node() {
if Command::new("ssh-keygen").arg("-?").output().is_err() {
return;
}
let home = tempfile::tempdir().expect("tempdir");
let paths = geth_config::GethPaths::from_home(home.path());
let admin_key_path = home.path().join("owner_ed25519");
let status = Command::new("ssh-keygen")
.arg("-q")
.arg("-t")
.arg("ed25519")
.arg("-N")
.arg("")
.arg("-f")
.arg(&admin_key_path)
.status()
.expect("generate owner ssh key");
assert!(status.success());
let node = geth_node::init_owned_node(
&paths,
geth_node::InitOwnerOptions {
admin_key_path: Some(admin_key_path.with_extension("pub")),
signing_key_path: Some(admin_key_path.clone()),
owner_name: "Eric".to_owned(),
node_name: "laptop".to_owned(),
capabilities: vec!["resource:ssh-proxy:local=ssh_proxy.admin_shell".to_owned()],
},
)
.expect("init owned node");
match geth_node::handle_request(&node, geth_control::ControlRequest::KeychainStatus)
.expect("keychain status")
{
geth_control::ControlResponse::KeychainStatus(status) => {
assert!(status.initialized);
assert_eq!(status.admin_keys, 1);
assert_eq!(status.users, 1);
assert_eq!(status.devices, 1);
assert_eq!(status.nodes, 1);
assert_eq!(status.verified_signatures, status.signatures);
assert!(status.signatures >= 6);
}
other => panic!("unexpected response: {other:?}"),
}
match geth_node::handle_request(&node, geth_control::ControlRequest::NodeList)
.expect("node list")
{
geth_control::ControlResponse::NodeList { nodes, .. } => {
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].name, "laptop");
assert_eq!(nodes[0].id.as_str(), node.node_id);
}
other => panic!("unexpected response: {other:?}"),
}
let renamed = geth_node::handle_request(
&node,
geth_control::ControlRequest::NodeRename {
node: "laptop".to_owned(),
name: "work-laptop".to_owned(),
signing_key_path: Some(admin_key_path),
},
)
.expect("rename node");
match renamed {
geth_control::ControlResponse::NodeKeychainUpdated { signatures, .. } => {
assert_eq!(signatures.len(), 1);
}
other => panic!("unexpected response: {other:?}"),
}
let explained = geth_node::handle_request(
&node,
geth_control::ControlRequest::AuthExplain {
subject: node.node_id.clone(),
resource: "resource:ssh-proxy:local".to_owned(),
capability: "ssh_proxy.admin_shell".to_owned(),
},
)
.expect("explain init grant");
match explained {
geth_control::ControlResponse::AuthExplain(explanation) => {
assert!(explanation.allowed);
assert!(explanation.reason.contains("direct grant"));
}
other => panic!("unexpected response: {other:?}"),
}
}
#[test]
fn db_add_and_status_register_local_db_metadata() {
let home = tempfile::tempdir().expect("tempdir");