Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion crates/openshell-sandbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,25 @@ pub async fn run_sandbox(
std::collections::HashMap::new()
};

let (provider_env, secret_resolver) = SecretResolver::from_provider_env(provider_env);
// Use the placeholder mechanism only when the policy has at least one
// `tls: terminate` endpoint. Without TLS termination the proxy cannot
// intercept HTTPS traffic to rewrite credential placeholders in request
// headers, so placeholder values would reach upstream APIs verbatim and
// cause 401 errors. When no such endpoint exists, pass real credentials
// directly so API calls succeed.
let (provider_env, secret_resolver) = if policy.has_tls_terminate_endpoints {
SecretResolver::from_provider_env(provider_env)
} else {
if !provider_env.is_empty() {
warn!(
"Sandbox policy has no `tls: terminate` endpoints; \
provider credentials are passed directly to the child process. \
Add `protocol: rest` and `tls: terminate` to HTTPS endpoints \
that use provider credentials to enable secure credential rewriting."
);
}
(provider_env, None)
};
let secret_resolver = secret_resolver.map(Arc::new);

// Create identity cache for SHA256 TOFU when OPA is active
Expand Down Expand Up @@ -981,6 +999,9 @@ async fn load_policy(
},
landlock: config.landlock,
process: config.process,
// File-mode is a dev/operator override — assume the operator has
// configured `tls: terminate` where needed.
has_tls_terminate_endpoints: true,
};
enrich_sandbox_baseline_paths(&mut policy);
return Ok((policy, Some(Arc::new(engine))));
Expand Down
14 changes: 14 additions & 0 deletions crates/openshell-sandbox/src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ pub struct SandboxPolicy {
pub network: NetworkPolicy,
pub landlock: LandlockPolicy,
pub process: ProcessPolicy,
/// True when at least one network endpoint has `tls: terminate` configured.
///
/// When false, the proxy cannot rewrite credential placeholder values in
/// HTTP headers (TLS MITM is required for that). Provider credentials are
/// passed directly to the child process instead of using the placeholder
/// mechanism so that API calls succeed.
pub has_tls_terminate_endpoints: bool,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -106,6 +113,12 @@ impl TryFrom<ProtoSandboxPolicy> for SandboxPolicy {
proxy: Some(ProxyPolicy { http_addr: None }),
};

let has_tls_terminate_endpoints = proto
.network_policies
.values()
.flat_map(|r| r.endpoints.iter())
.any(|ep| ep.tls == "terminate");

Ok(Self {
version: proto.version,
filesystem: proto
Expand All @@ -115,6 +128,7 @@ impl TryFrom<ProtoSandboxPolicy> for SandboxPolicy {
network,
landlock: proto.landlock.map(LandlockPolicy::from).unwrap_or_default(),
process: proto.process.map(ProcessPolicy::from).unwrap_or_default(),
has_tls_terminate_endpoints,
})
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/openshell-sandbox/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ mod tests {
network: NetworkPolicy::default(),
landlock: LandlockPolicy::default(),
process,
has_tls_terminate_endpoints: false,
}
}

Expand Down
7 changes: 6 additions & 1 deletion crates/openshell-sandbox/testdata/sandbox-policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ network_policies:
claude_code:
name: claude_code
endpoints:
- { host: api.anthropic.com, port: 443 }
- host: api.anthropic.com
port: 443
protocol: rest
tls: terminate
enforcement: enforce
access: full
- { host: statsig.anthropic.com, port: 443 }
binaries:
- { path: /usr/local/bin/claude }
Expand Down
Loading