Add remote authorization audit matrix

This commit is contained in:
Eric Wendland 2026-05-22 16:55:33 +02:00
commit cab0584656
2 changed files with 196 additions and 2 deletions

View file

@ -10695,6 +10695,196 @@ mod tests {
use super::*;
use tokio::io::AsyncReadExt;
#[derive(Debug, PartialEq, Eq)]
enum RemoteGuardKind {
Capability,
SignedLog,
OwnerReview,
CandidateOnly,
}
#[derive(Debug)]
struct RemoteGuardAudit {
operation: &'static str,
mutates_or_opens_service: bool,
guard: RemoteGuardKind,
resource: Option<&'static str>,
capability: Option<&'static str>,
}
fn remote_guard_audit_matrix() -> Vec<RemoteGuardAudit> {
vec![
RemoteGuardAudit {
operation: "peer.ping",
mutates_or_opens_service: false,
guard: RemoteGuardKind::CandidateOnly,
resource: None,
capability: None,
},
RemoteGuardAudit {
operation: "sync.status",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("per-stream"),
capability: Some("per-stream"),
},
RemoteGuardAudit {
operation: "keychain.sync",
mutates_or_opens_service: true,
guard: RemoteGuardKind::SignedLog,
resource: Some("keychain"),
capability: None,
},
RemoteGuardAudit {
operation: "auth.sync",
mutates_or_opens_service: true,
guard: RemoteGuardKind::SignedLog,
resource: Some("resource-auth"),
capability: None,
},
RemoteGuardAudit {
operation: "node.enrollment.submit",
mutates_or_opens_service: true,
guard: RemoteGuardKind::OwnerReview,
resource: Some("node-enrollment"),
capability: None,
},
RemoteGuardAudit {
operation: "cas.fetch",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:cas:local"),
capability: Some("cas.fetch"),
},
RemoteGuardAudit {
operation: "cas.root.sync",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:cas-tree:<name>"),
capability: Some("cas.fetch"),
},
RemoteGuardAudit {
operation: "ssh.cert.sync",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:ssh:certs"),
capability: Some("ssh_cert.sync"),
},
RemoteGuardAudit {
operation: "ssh.revocation.sync",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:ssh:revocations"),
capability: Some("ssh_revocation.sync"),
},
RemoteGuardAudit {
operation: "kv.sync",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:kv:<name>"),
capability: Some("kv.read"),
},
RemoteGuardAudit {
operation: "document.sync",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:document:<name>"),
capability: Some("document.read"),
},
RemoteGuardAudit {
operation: "db.sync",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:db:<name>"),
capability: Some("db.sync"),
},
RemoteGuardAudit {
operation: "pubsub.publish",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:pubsub:<topic>"),
capability: Some("pubsub.publish"),
},
RemoteGuardAudit {
operation: "pubsub.subscribe",
mutates_or_opens_service: false,
guard: RemoteGuardKind::Capability,
resource: Some("resource:pubsub:<topic>"),
capability: Some("pubsub.subscribe"),
},
RemoteGuardAudit {
operation: "pipe.connect",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:pipe:<name>"),
capability: Some("pipe.connect"),
},
RemoteGuardAudit {
operation: "pipe.listen",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:pipe:<name>"),
capability: Some("pipe.listen"),
},
RemoteGuardAudit {
operation: "pipe.send",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:pipe:<name>"),
capability: Some("pipe.connect"),
},
RemoteGuardAudit {
operation: "pipe.tcp.forward",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:pipe-tcp:<target>"),
capability: Some("pipe.forward"),
},
RemoteGuardAudit {
operation: "pipe.unix.forward",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:pipe-unix:<target>"),
capability: Some("pipe.forward"),
},
RemoteGuardAudit {
operation: "ssh.proxy.connect",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:ssh-proxy:local"),
capability: Some("ssh_proxy.connect"),
},
RemoteGuardAudit {
operation: "ssh.admin-shell",
mutates_or_opens_service: true,
guard: RemoteGuardKind::Capability,
resource: Some("resource:ssh-proxy:local"),
capability: Some("ssh_proxy.admin_shell"),
},
]
}
#[test]
fn remote_authorization_audit_matrix_covers_mutating_and_service_operations() {
let matrix = remote_guard_audit_matrix();
assert_eq!(matrix.len(), 21);
for item in matrix.iter().filter(|item| item.mutates_or_opens_service) {
assert_ne!(
item.guard,
RemoteGuardKind::CandidateOnly,
"{} must not rely on peer-card discovery alone",
item.operation
);
if item.guard == RemoteGuardKind::Capability {
assert!(
item.resource.is_some() && item.capability.is_some(),
"{} capability guard must document resource and capability",
item.operation
);
}
}
}
fn write_offline_iroh_config(paths: &GethPaths) {
std::fs::write(
paths.config_file(),

View file

@ -82,8 +82,12 @@ Implementation order:
- `[~]` Remote authorization enforcement audit.
Acceptance criteria:
- `[ ]` Every remote mutable operation has an explicit resource capability
check before mutating local state or opening a host service.
- `[x]` Every remote mutable operation has an explicit resource capability
check before mutating local state or opening a host service, or is
documented as a signed-log import / owner-reviewed enrollment exception.
- `[x]` A test-backed remote guard matrix documents the expected guard for
each remote operation and fails if mutating/service-opening operations rely
on discovery alone.
- `[ ]` Tests cover denied and allowed paths for CAS, KV, DB, document,
pubsub, pipe, SSH proxy/admin shell, SSH cert metadata, and revocations.
- `[x]` Initial two-daemon denied-mutation coverage exists for remote pubsub