diff --git a/crates/openshell-sandbox/data/sandbox-policy.rego b/crates/openshell-sandbox/data/sandbox-policy.rego index afcd28863..bc32abd2a 100644 --- a/crates/openshell-sandbox/data/sandbox-policy.rego +++ b/crates/openshell-sandbox/data/sandbox-policy.rego @@ -161,6 +161,13 @@ binary_allowed(policy, exec) if { glob.match(b.path, ["/"], p) } +# Binary matching: empty binaries list means no binary restriction. +# When a policy declares endpoints without specifying which binaries may +# access them, any binary is permitted (the endpoint itself is the gate). +binary_allowed(policy, _) if { + count(object.get(policy, "binaries", [])) == 0 +} + user_declared_binary_allowed(policy, exec) if { some b b := policy.binaries[_] @@ -187,6 +194,10 @@ user_declared_binary_allowed(policy, exec) if { glob.match(b.path, ["/"], p) } +user_declared_binary_allowed(policy, _) if { + count(object.get(policy, "binaries", [])) == 0 +} + # --- Network action (allow / deny) --- # # These rules are mutually exclusive by construction: diff --git a/crates/openshell-sandbox/src/opa.rs b/crates/openshell-sandbox/src/opa.rs index f73f3bc14..f5ff5923b 100644 --- a/crates/openshell-sandbox/src/opa.rs +++ b/crates/openshell-sandbox/src/opa.rs @@ -5165,4 +5165,61 @@ network_policies: let input = l7_input("h.test", 80, "HEAD", "/protected"); assert!(!eval_l7(&engine, &input)); } + + #[test] + fn empty_binaries_allows_any_binary() { + let engine = test_engine(); + let input = NetworkInput { + host: "open.example.com".into(), + port: 443, + binary_path: PathBuf::from("/any/random/binary"), + binary_sha256: "unused".into(), + ancestors: vec![], + cmdline_paths: vec![], + }; + let decision = engine.evaluate_network(&input).unwrap(); + assert!( + decision.allowed, + "Expected allow with empty binaries list, got deny: {}", + decision.reason + ); + assert_eq!(decision.matched_policy.as_deref(), Some("open_endpoint")); + } + + #[test] + fn unresolved_identity_allowed_with_empty_binaries() { + let engine = test_engine(); + let input = NetworkInput { + host: "open.example.com".into(), + port: 443, + binary_path: PathBuf::from(""), + binary_sha256: String::new(), + ancestors: vec![], + cmdline_paths: vec![], + }; + let decision = engine.evaluate_network(&input).unwrap(); + assert!( + decision.allowed, + "Expected allow for unresolved identity with empty binaries, got deny: {}", + decision.reason + ); + } + + #[test] + fn unresolved_identity_denied_with_specific_binaries() { + let engine = test_engine(); + let input = NetworkInput { + host: "api.anthropic.com".into(), + port: 443, + binary_path: PathBuf::from(""), + binary_sha256: String::new(), + ancestors: vec![], + cmdline_paths: vec![], + }; + let decision = engine.evaluate_network(&input).unwrap(); + assert!( + !decision.allowed, + "Expected deny for unresolved identity when policy requires specific binary" + ); + } } diff --git a/crates/openshell-sandbox/src/proxy.rs b/crates/openshell-sandbox/src/proxy.rs index a72f42e2a..c6126041a 100644 --- a/crates/openshell-sandbox/src/proxy.rs +++ b/crates/openshell-sandbox/src/proxy.rs @@ -1400,13 +1400,25 @@ fn evaluate_opa_tcp( let identity = match resolve_process_identity(pid, peer_port, identity_cache) { Ok(id) => id, Err(err) => { - return deny( - err.reason, - err.binary, - err.binary_pid, - err.ancestors, - vec![], + // In container/K8s deployments, network namespace setup can cause + // ephemeral port mismatches between peer_addr and /proc/net/tcp. + // Fall through to OPA with an unresolved identity — policies without + // binary restrictions (empty binaries list) will still allow. + warn!( + port = peer_port, + reason = %err.reason, + binary = ?err.binary, + binary_pid = ?err.binary_pid, + ancestors = ?err.ancestors, + "identity resolution failed; using unresolved identity", ); + ResolvedIdentity { + bin_path: PathBuf::from(""), + binary_pid: 0, + ancestors: vec![], + cmdline_paths: vec![], + bin_hash: String::new(), + } } }; diff --git a/crates/openshell-sandbox/testdata/sandbox-policy.yaml b/crates/openshell-sandbox/testdata/sandbox-policy.yaml index 297face21..f4941332b 100644 --- a/crates/openshell-sandbox/testdata/sandbox-policy.yaml +++ b/crates/openshell-sandbox/testdata/sandbox-policy.yaml @@ -82,3 +82,8 @@ network_policies: - { host: gitlab.com, port: 443 } binaries: - { path: /usr/bin/glab } + + open_endpoint: + name: open_endpoint + endpoints: + - { host: open.example.com, port: 443 }