Test denied remote operations do not mutate state
This commit is contained in:
parent
61e7c2a1a5
commit
a2935328ce
2 changed files with 164 additions and 1 deletions
|
|
@ -471,6 +471,165 @@ fn sync_now_completes_owner_approved_node_enrollment_flow() {
|
||||||
assert!(sync_status_stdout.contains("auth"));
|
assert!(sync_status_stdout.contains("auth"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn denied_remote_operations_do_not_mutate_serving_node_state() {
|
||||||
|
let left_home = tempfile::tempdir().expect("left tempdir");
|
||||||
|
let right_home = tempfile::tempdir().expect("right tempdir");
|
||||||
|
if !unix_sockets_available(left_home.path()) || !unix_sockets_available(right_home.path()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert!(run_geth(left_home.path(), &["init"]).status.success());
|
||||||
|
assert!(run_geth(right_home.path(), &["init"]).status.success());
|
||||||
|
for home in [left_home.path(), right_home.path()] {
|
||||||
|
std::fs::write(
|
||||||
|
home.join("config.toml"),
|
||||||
|
"[iroh]\nrelay_mode = \"disabled\"\nlocal_discovery = false\n",
|
||||||
|
)
|
||||||
|
.expect("write config");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut left_daemon = spawn_daemon(left_home.path());
|
||||||
|
let mut right_daemon = spawn_daemon(right_home.path());
|
||||||
|
wait_for_socket(&left_home.path().join("run/geth.sock"));
|
||||||
|
wait_for_socket(&right_home.path().join("run/geth.sock"));
|
||||||
|
|
||||||
|
let left_card_path = left_home.path().join("left-peer-card.json");
|
||||||
|
let right_card_path = right_home.path().join("right-peer-card.json");
|
||||||
|
let left_export = run_geth(
|
||||||
|
left_home.path(),
|
||||||
|
&[
|
||||||
|
"peer",
|
||||||
|
"export",
|
||||||
|
"--out",
|
||||||
|
left_card_path.to_str().expect("left card path"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let right_export = run_geth(
|
||||||
|
right_home.path(),
|
||||||
|
&[
|
||||||
|
"peer",
|
||||||
|
"export",
|
||||||
|
"--out",
|
||||||
|
right_card_path.to_str().expect("right card path"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
left_export.status.success(),
|
||||||
|
"left export stderr: {}",
|
||||||
|
String::from_utf8_lossy(&left_export.stderr)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
right_export.status.success(),
|
||||||
|
"right export stderr: {}",
|
||||||
|
String::from_utf8_lossy(&right_export.stderr)
|
||||||
|
);
|
||||||
|
let right_card_json = std::fs::read_to_string(&right_card_path).expect("read right card");
|
||||||
|
let right_card: geth_discovery::PeerCard =
|
||||||
|
serde_json::from_str(&right_card_json).expect("decode right card");
|
||||||
|
|
||||||
|
let import_right = run_geth(
|
||||||
|
left_home.path(),
|
||||||
|
&[
|
||||||
|
"peer",
|
||||||
|
"import",
|
||||||
|
right_card_path.to_str().expect("right card path"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let import_left = run_geth(
|
||||||
|
right_home.path(),
|
||||||
|
&[
|
||||||
|
"peer",
|
||||||
|
"import",
|
||||||
|
left_card_path.to_str().expect("left card path"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
import_right.status.success(),
|
||||||
|
"left import right stderr: {}",
|
||||||
|
String::from_utf8_lossy(&import_right.stderr)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
import_left.status.success(),
|
||||||
|
"right import left stderr: {}",
|
||||||
|
String::from_utf8_lossy(&import_left.stderr)
|
||||||
|
);
|
||||||
|
|
||||||
|
let denied_publish = run_geth(
|
||||||
|
left_home.path(),
|
||||||
|
&[
|
||||||
|
"pubsub",
|
||||||
|
"pub",
|
||||||
|
"ops",
|
||||||
|
"should-not-stick",
|
||||||
|
"--node",
|
||||||
|
right_card.node_id.as_str(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
denied_publish.status.success(),
|
||||||
|
"denied publish stderr: {}",
|
||||||
|
String::from_utf8_lossy(&denied_publish.stderr)
|
||||||
|
);
|
||||||
|
let denied_publish_stdout = String::from_utf8_lossy(&denied_publish.stdout);
|
||||||
|
assert!(denied_publish_stdout.contains("allowed: false"));
|
||||||
|
assert!(denied_publish_stdout.contains("pubsub publish denied"));
|
||||||
|
|
||||||
|
let right_pubsub = run_geth(right_home.path(), &["pubsub", "sub", "ops"]);
|
||||||
|
assert!(
|
||||||
|
right_pubsub.status.success(),
|
||||||
|
"right pubsub sub stderr: {}",
|
||||||
|
String::from_utf8_lossy(&right_pubsub.stderr)
|
||||||
|
);
|
||||||
|
assert!(String::from_utf8_lossy(&right_pubsub.stdout).contains("messages: 0"));
|
||||||
|
|
||||||
|
let denied_pipe_listen = run_geth(
|
||||||
|
left_home.path(),
|
||||||
|
&[
|
||||||
|
"pipe",
|
||||||
|
"listen",
|
||||||
|
"inbox",
|
||||||
|
"--node",
|
||||||
|
right_card.node_id.as_str(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
denied_pipe_listen.status.success(),
|
||||||
|
"denied pipe listen stderr: {}",
|
||||||
|
String::from_utf8_lossy(&denied_pipe_listen.stderr)
|
||||||
|
);
|
||||||
|
let denied_pipe_stdout = String::from_utf8_lossy(&denied_pipe_listen.stdout);
|
||||||
|
assert!(denied_pipe_stdout.contains("allowed: false"));
|
||||||
|
assert!(denied_pipe_stdout.contains("pipe listen denied"));
|
||||||
|
|
||||||
|
let right_pipe_connect = run_geth(right_home.path(), &["pipe", "connect", "inbox"]);
|
||||||
|
assert!(
|
||||||
|
right_pipe_connect.status.success(),
|
||||||
|
"right pipe connect stderr: {}",
|
||||||
|
String::from_utf8_lossy(&right_pipe_connect.stderr)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
String::from_utf8_lossy(&right_pipe_connect.stdout).contains("local_listener_found: false")
|
||||||
|
);
|
||||||
|
|
||||||
|
let denied_admin_shell = run_geth(
|
||||||
|
left_home.path(),
|
||||||
|
&["ssh", "admin-shell", right_card.node_id.as_str(), "status"],
|
||||||
|
);
|
||||||
|
let _ = left_daemon.kill();
|
||||||
|
let _ = right_daemon.kill();
|
||||||
|
let _ = left_daemon.wait();
|
||||||
|
let _ = right_daemon.wait();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
denied_admin_shell.status.success(),
|
||||||
|
"denied admin shell stderr: {}",
|
||||||
|
String::from_utf8_lossy(&denied_admin_shell.stderr)
|
||||||
|
);
|
||||||
|
let denied_admin_stdout = String::from_utf8_lossy(&denied_admin_shell.stdout);
|
||||||
|
assert!(denied_admin_stdout.contains("allowed: false"));
|
||||||
|
assert!(denied_admin_stdout.contains("ssh admin shell denied"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn peer_card_export_import_and_list_are_candidate_only() {
|
fn peer_card_export_import_and_list_are_candidate_only() {
|
||||||
let source_home = tempfile::tempdir().expect("source tempdir");
|
let source_home = tempfile::tempdir().expect("source tempdir");
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ Implementation order:
|
||||||
|
|
||||||
1. `[~]` Close remote authorization and replicated-state safety gaps.
|
1. `[~]` Close remote authorization and replicated-state safety gaps.
|
||||||
Acceptance criteria:
|
Acceptance criteria:
|
||||||
- `[ ]` Add tests proving denied remote operations do not mutate local state.
|
- `[x]` Add initial two-daemon tests proving denied remote pubsub publish,
|
||||||
|
remote pipe listen, and SSH admin shell requests do not mutate serving
|
||||||
|
node state.
|
||||||
- `[ ]` Add tests proving unsigned, invalidly signed, and conflicting
|
- `[ ]` Add tests proving unsigned, invalidly signed, and conflicting
|
||||||
replicated records do not mutate trust/resource state.
|
replicated records do not mutate trust/resource state.
|
||||||
- `[ ]` Improve `auth explain` diagnostics enough for operators to
|
- `[ ]` Improve `auth explain` diagnostics enough for operators to
|
||||||
|
|
@ -74,6 +76,8 @@ Implementation order:
|
||||||
check before mutating local state or opening a host service.
|
check before mutating local state or opening a host service.
|
||||||
- `[ ]` Tests cover denied and allowed paths for CAS, KV, DB, document,
|
- `[ ]` Tests cover denied and allowed paths for CAS, KV, DB, document,
|
||||||
pubsub, pipe, SSH proxy/admin shell, SSH cert metadata, and revocations.
|
pubsub, pipe, SSH proxy/admin shell, SSH cert metadata, and revocations.
|
||||||
|
- `[x]` Initial two-daemon denied-mutation coverage exists for remote pubsub
|
||||||
|
publish, remote pipe listen, and SSH admin shell.
|
||||||
- `[ ]` `auth explain` output can explain discovered-only peers, missing
|
- `[ ]` `auth explain` output can explain discovered-only peers, missing
|
||||||
endpoint bindings, missing grants, matching grants, revocations, and bearer
|
endpoint bindings, missing grants, matching grants, revocations, and bearer
|
||||||
access.
|
access.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue