Skip to content

Commit c00ea7b

Browse files
authored
Merge pull request #10 from kagenti/fix/identity-resolution-fallback
fix(proxy): fall through to OPA on identity resolution failure
2 parents 3a1bbea + 1c3e2c4 commit c00ea7b

4 files changed

Lines changed: 91 additions & 6 deletions

File tree

crates/openshell-sandbox/data/sandbox-policy.rego

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ binary_allowed(policy, exec) if {
161161
glob.match(b.path, ["/"], p)
162162
}
163163

164+
# Binary matching: empty binaries list means no binary restriction.
165+
# When a policy declares endpoints without specifying which binaries may
166+
# access them, any binary is permitted (the endpoint itself is the gate).
167+
binary_allowed(policy, _) if {
168+
count(object.get(policy, "binaries", [])) == 0
169+
}
170+
164171
user_declared_binary_allowed(policy, exec) if {
165172
some b
166173
b := policy.binaries[_]
@@ -187,6 +194,10 @@ user_declared_binary_allowed(policy, exec) if {
187194
glob.match(b.path, ["/"], p)
188195
}
189196

197+
user_declared_binary_allowed(policy, _) if {
198+
count(object.get(policy, "binaries", [])) == 0
199+
}
200+
190201
# --- Network action (allow / deny) ---
191202
#
192203
# These rules are mutually exclusive by construction:

crates/openshell-sandbox/src/opa.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5165,4 +5165,61 @@ network_policies:
51655165
let input = l7_input("h.test", 80, "HEAD", "/protected");
51665166
assert!(!eval_l7(&engine, &input));
51675167
}
5168+
5169+
#[test]
5170+
fn empty_binaries_allows_any_binary() {
5171+
let engine = test_engine();
5172+
let input = NetworkInput {
5173+
host: "open.example.com".into(),
5174+
port: 443,
5175+
binary_path: PathBuf::from("/any/random/binary"),
5176+
binary_sha256: "unused".into(),
5177+
ancestors: vec![],
5178+
cmdline_paths: vec![],
5179+
};
5180+
let decision = engine.evaluate_network(&input).unwrap();
5181+
assert!(
5182+
decision.allowed,
5183+
"Expected allow with empty binaries list, got deny: {}",
5184+
decision.reason
5185+
);
5186+
assert_eq!(decision.matched_policy.as_deref(), Some("open_endpoint"));
5187+
}
5188+
5189+
#[test]
5190+
fn unresolved_identity_allowed_with_empty_binaries() {
5191+
let engine = test_engine();
5192+
let input = NetworkInput {
5193+
host: "open.example.com".into(),
5194+
port: 443,
5195+
binary_path: PathBuf::from("<unresolved>"),
5196+
binary_sha256: String::new(),
5197+
ancestors: vec![],
5198+
cmdline_paths: vec![],
5199+
};
5200+
let decision = engine.evaluate_network(&input).unwrap();
5201+
assert!(
5202+
decision.allowed,
5203+
"Expected allow for unresolved identity with empty binaries, got deny: {}",
5204+
decision.reason
5205+
);
5206+
}
5207+
5208+
#[test]
5209+
fn unresolved_identity_denied_with_specific_binaries() {
5210+
let engine = test_engine();
5211+
let input = NetworkInput {
5212+
host: "api.anthropic.com".into(),
5213+
port: 443,
5214+
binary_path: PathBuf::from("<unresolved>"),
5215+
binary_sha256: String::new(),
5216+
ancestors: vec![],
5217+
cmdline_paths: vec![],
5218+
};
5219+
let decision = engine.evaluate_network(&input).unwrap();
5220+
assert!(
5221+
!decision.allowed,
5222+
"Expected deny for unresolved identity when policy requires specific binary"
5223+
);
5224+
}
51685225
}

crates/openshell-sandbox/src/proxy.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,13 +1400,25 @@ fn evaluate_opa_tcp(
14001400
let identity = match resolve_process_identity(pid, peer_port, identity_cache) {
14011401
Ok(id) => id,
14021402
Err(err) => {
1403-
return deny(
1404-
err.reason,
1405-
err.binary,
1406-
err.binary_pid,
1407-
err.ancestors,
1408-
vec![],
1403+
// In container/K8s deployments, network namespace setup can cause
1404+
// ephemeral port mismatches between peer_addr and /proc/net/tcp.
1405+
// Fall through to OPA with an unresolved identity — policies without
1406+
// binary restrictions (empty binaries list) will still allow.
1407+
warn!(
1408+
port = peer_port,
1409+
reason = %err.reason,
1410+
binary = ?err.binary,
1411+
binary_pid = ?err.binary_pid,
1412+
ancestors = ?err.ancestors,
1413+
"identity resolution failed; using unresolved identity",
14091414
);
1415+
ResolvedIdentity {
1416+
bin_path: PathBuf::from("<unresolved>"),
1417+
binary_pid: 0,
1418+
ancestors: vec![],
1419+
cmdline_paths: vec![],
1420+
bin_hash: String::new(),
1421+
}
14101422
}
14111423
};
14121424

crates/openshell-sandbox/testdata/sandbox-policy.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,8 @@ network_policies:
8282
- { host: gitlab.com, port: 443 }
8383
binaries:
8484
- { path: /usr/bin/glab }
85+
86+
open_endpoint:
87+
name: open_endpoint
88+
endpoints:
89+
- { host: open.example.com, port: 443 }

0 commit comments

Comments
 (0)