Load sandbox policy from local files or gRPC. Priority: 1. If `policy_rules` and `policy_data` are provided, load OPA engine from local files 2. If `sandbox_id` and `openshell_endpoint` are provided, fetch via gRPC 3. If the server returns no policy, discover from disk or use restrictive default 4. Otherwise, return an error Returns the policy, the OPA engine, and (for gRPC mode) the original pr
(
sandbox_id: Option<String>,
sandbox: Option<String>,
openshell_endpoint: Option<String>,
policy_rules: Option<String>,
policy_data: Option<String>,
)
| 1361 | /// policy. The proto is retained so the OPA engine can be rebuilt with symlink |
| 1362 | /// resolution after the container entrypoint starts. |
| 1363 | async fn load_policy( |
| 1364 | sandbox_id: Option<String>, |
| 1365 | sandbox: Option<String>, |
| 1366 | openshell_endpoint: Option<String>, |
| 1367 | policy_rules: Option<String>, |
| 1368 | policy_data: Option<String>, |
| 1369 | ) -> Result<( |
| 1370 | SandboxPolicy, |
| 1371 | Option<Arc<OpaEngine>>, |
| 1372 | Option<openshell_core::proto::SandboxPolicy>, |
| 1373 | )> { |
| 1374 | // File mode: load OPA engine from rego rules + YAML data (dev override) |
| 1375 | if let (Some(policy_file), Some(data_file)) = (&policy_rules, &policy_data) { |
| 1376 | ocsf_emit!(ConfigStateChangeBuilder::new(ocsf_ctx()) |
| 1377 | .severity(SeverityId::Informational) |
| 1378 | .status(StatusId::Success) |
| 1379 | .state(StateId::Other, "loading") |
| 1380 | .unmapped("policy_rules", serde_json::json!(policy_file)) |
| 1381 | .unmapped("policy_data", serde_json::json!(data_file)) |
| 1382 | .message(format!( |
| 1383 | "Loading OPA policy engine from local files [rules:{policy_file} data:{data_file}]" |
| 1384 | )) |
| 1385 | .build()); |
| 1386 | let engine = OpaEngine::from_files( |
| 1387 | std::path::Path::new(policy_file), |
| 1388 | std::path::Path::new(data_file), |
| 1389 | )?; |
| 1390 | let config = engine.query_sandbox_config()?; |
| 1391 | let mut policy = SandboxPolicy { |
| 1392 | version: 1, |
| 1393 | filesystem: config.filesystem, |
| 1394 | network: NetworkPolicy { |
| 1395 | mode: NetworkMode::Proxy, |
| 1396 | proxy: Some(ProxyPolicy { http_addr: None }), |
| 1397 | }, |
| 1398 | landlock: config.landlock, |
| 1399 | process: config.process, |
| 1400 | }; |
| 1401 | enrich_sandbox_baseline_paths(&mut policy); |
| 1402 | return Ok((policy, Some(Arc::new(engine)), None)); |
| 1403 | } |
| 1404 | |
| 1405 | // gRPC mode: fetch typed proto policy, construct OPA engine from baked rules + proto data |
| 1406 | if let (Some(id), Some(endpoint)) = (&sandbox_id, &openshell_endpoint) { |
| 1407 | info!( |
| 1408 | sandbox_id = %id, |
| 1409 | endpoint = %endpoint, |
| 1410 | "Fetching sandbox policy via gRPC" |
| 1411 | ); |
| 1412 | let proto_policy = grpc_retry("Policy fetch", || { |
| 1413 | openshell_core::grpc_client::fetch_policy(endpoint, id) |
| 1414 | }) |
| 1415 | .await?; |
| 1416 | |
| 1417 | let mut proto_policy = if let Some(p) = proto_policy { |
| 1418 | p |
| 1419 | } else { |
| 1420 | // No policy configured on the server. Discover from disk or |
no test coverage detected