Skip to content
Draft
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
1 change: 0 additions & 1 deletion crates/openshell-driver-podman/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use openshell_core::{driver_mounts, proto_struct};
use serde::Serialize;
use serde_json::Value;
use std::collections::{BTreeMap, HashSet};
#[cfg(target_os = "linux")]
use std::path::Path;

/// Returns `true` when `SELinux` is enabled (enforcing or permissive).
Expand Down
16 changes: 14 additions & 2 deletions crates/openshell-supervisor-middleware/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ pub enum TransformedBodyPolicy<'a> {
pub struct HttpRequestInput {
pub request_id: String,
pub sandbox_id: String,
pub sandbox_name: String,
pub scheme: String,
pub host: String,
pub port: u16,
Expand Down Expand Up @@ -1581,6 +1582,7 @@ impl ChainRunner {
let HttpRequestInput {
request_id,
sandbox_id,
sandbox_name,
scheme,
host,
port,
Expand All @@ -1597,6 +1599,7 @@ impl ChainRunner {
let context = RequestContext {
request_id,
sandbox_id,
sandbox_name,
originating_process: None,
};
let target = HttpRequestTarget {
Expand Down Expand Up @@ -2047,7 +2050,8 @@ mod tests {
fn input(body: &str) -> HttpRequestInput {
HttpRequestInput {
request_id: "req".into(),
sandbox_id: "sbx".into(),
sandbox_id: "sbx-id".into(),
sandbox_name: "sbx-name".into(),
scheme: "https".into(),
host: "api.example.com".into(),
port: 443,
Expand Down Expand Up @@ -3215,7 +3219,8 @@ mod tests {
assert_eq!(received[0].config.as_ref(), Some(&evaluation_config));
let context = received[0].context.as_ref().expect("request context");
assert_eq!(context.request_id, "req");
assert_eq!(context.sandbox_id, "sbx");
assert_eq!(context.sandbox_id, "sbx-id");
assert_eq!(context.sandbox_name, "sbx-name");
assert!(context.originating_process.is_none());
let target = received[0].target.as_ref().expect("request target");
assert_eq!(target.scheme, "https");
Expand Down Expand Up @@ -4678,6 +4683,7 @@ mod tests {
session_id: session_id.into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down Expand Up @@ -5111,6 +5117,7 @@ mod tests {
session_id: "builtin-regex-session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down Expand Up @@ -5177,6 +5184,7 @@ mod tests {
session_id: "builtin-regex-gap-session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down Expand Up @@ -5239,6 +5247,7 @@ mod tests {
session_id: "in-process-session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down Expand Up @@ -5310,6 +5319,7 @@ mod tests {
session_id: "ws-session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down Expand Up @@ -5394,6 +5404,7 @@ mod tests {
session_id: "ws-session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "ws".into(),
host: "api.openai.com".into(),
port: 80,
Expand Down Expand Up @@ -5689,6 +5700,7 @@ mod tests {
session_id: "session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down
2 changes: 2 additions & 0 deletions crates/openshell-supervisor-middleware/src/websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct WebSocketPreflightInput {
pub session_id: String,
pub request_id: String,
pub sandbox_id: String,
pub sandbox_name: String,
pub scheme: String,
pub host: String,
pub port: u16,
Expand Down Expand Up @@ -920,6 +921,7 @@ async fn open_stage(entry: DescribedChainEntry, input: WebSocketPreflightInput)
context: Some(RequestContext {
request_id: input.request_id,
sandbox_id: input.sandbox_id,
sandbox_name: input.sandbox_name,
originating_process: None,
}),
target: Some(HttpRequestTarget {
Expand Down
52 changes: 51 additions & 1 deletion crates/openshell-supervisor-network/src/l7/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ pub async fn apply_middleware_chain_for_scheme<C: AsyncRead + AsyncWrite + Unpin
// unresolved binding is handled before the body is read) and forward
// the original request unchanged if the chain allows.
let input = middleware_request_input(
openshell_ocsf::ctx::ctx(),
scheme,
&req,
ctx,
Expand Down Expand Up @@ -525,6 +526,7 @@ pub async fn apply_middleware_chain_for_scheme<C: AsyncRead + AsyncWrite + Unpin
let headers = safe_middleware_headers(&buffered.headers)?;
let query = raw_query_from_request_headers(&buffered.headers)?;
let input = middleware_request_input(
openshell_ocsf::ctx::ctx(),
scheme,
&req,
ctx,
Expand Down Expand Up @@ -607,7 +609,9 @@ pub async fn send_middleware_admission_exhausted_response<
.await
}

#[allow(clippy::too_many_arguments)]
pub(super) fn middleware_request_input(
sandbox: &openshell_ocsf::SandboxContext,
scheme: &str,
req: &crate::l7::provider::L7Request,
ctx: &L7EvalContext,
Expand All @@ -618,7 +622,8 @@ pub(super) fn middleware_request_input(
) -> openshell_supervisor_middleware::HttpRequestInput {
openshell_supervisor_middleware::HttpRequestInput {
request_id: uuid::Uuid::new_v4().to_string(),
sandbox_id: openshell_ocsf::ctx::ctx().sandbox_id.clone(),
sandbox_id: sandbox.sandbox_id.clone(),
sandbox_name: sandbox.sandbox_name.clone(),
scheme: scheme.into(),
host: ctx.host.clone(),
port: ctx.port,
Expand Down Expand Up @@ -1079,6 +1084,51 @@ mod tests {
assert!(!body.to_string().contains("secret-value"));
}

#[test]
fn middleware_input_carries_real_sandbox_name() {
let sandbox = openshell_ocsf::SandboxContext {
sandbox_id: "sbx-123".into(),
sandbox_name: "nightly-build".into(),
container_image: String::new(),
hostname: "h".into(),
product_version: "0".into(),
proxy_ip: [127, 0, 0, 1].into(),
proxy_port: 3128,
};

let eval = L7EvalContext {
host: "api.example.test".into(),
port: 443,
policy_name: "api-policy".into(),
binary_path: "/usr/bin/curl".into(),
ancestors: Vec::new(),
cmdline_paths: Vec::new(),
secret_resolver: None,
..Default::default()
};
let req = crate::l7::provider::L7Request {
action: "POST".into(),
target: "/v1/messages".into(),
query_params: std::collections::HashMap::new(),
raw_header: Vec::new(),
body_length: crate::l7::provider::BodyLength::None,
};

let input = super::middleware_request_input(
&sandbox,
"https",
&req,
&eval,
Vec::new(),
Vec::new(),
String::new(),
Vec::new(),
);

assert_eq!(input.sandbox_name, "nightly-build");
assert_eq!(input.sandbox_id, "sbx-123");
}

#[tokio::test]
async fn middleware_failure_uses_platform_response_without_policy_guidance() {
let ctx = L7EvalContext {
Expand Down
2 changes: 2 additions & 0 deletions crates/openshell-supervisor-network/src/l7/relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,7 @@ pub(crate) async fn websocket_middleware_preflight(
session_id: uuid::Uuid::new_v4().to_string(),
request_id: uuid::Uuid::new_v4().to_string(),
sandbox_id: openshell_ocsf::ctx::ctx().sandbox_id.clone(),
sandbox_name: openshell_ocsf::ctx::ctx().sandbox_name.clone(),
scheme: scheme.to_string(),
host: ctx.host.clone(),
port: ctx.port,
Expand Down Expand Up @@ -5870,6 +5871,7 @@ network_policies:
};

let input = middleware_request_input(
openshell_ocsf::ctx::ctx(),
"http",
&req,
&ctx,
Expand Down
4 changes: 4 additions & 0 deletions crates/openshell-supervisor-network/src/l7/websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3598,6 +3598,7 @@ network_policies:
session_id: "session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: scheme.into(),
host: "api.openai.com".into(),
port: if scheme == "wss" { 443 } else { 80 },
Expand Down Expand Up @@ -4482,6 +4483,7 @@ network_policies:
session_id: "session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down Expand Up @@ -4629,6 +4631,7 @@ network_policies:
session_id: "disabled-session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down Expand Up @@ -4748,6 +4751,7 @@ network_policies:
session_id: "builtin-regex-session".into(),
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down
1 change: 1 addition & 0 deletions crates/openshell-supervisor-network/src/opa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7123,6 +7123,7 @@ network_policies:
session_id,
request_id: "request".into(),
sandbox_id: "sandbox".into(),
sandbox_name: "sandbox-name".into(),
scheme: "wss".into(),
host: "api.openai.com".into(),
port: 443,
Expand Down
2 changes: 2 additions & 0 deletions docs/extensibility/supervisor-middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ If post-transformation policy evaluation itself fails, OpenShell denies the requ

Middleware receives the request before credential injection. Operator-run services cannot inspect OpenShell-managed credentials. Middleware-visible request headers are delivered in wire order and repeated header names are preserved as separate entries. OpenShell filters credential, routing, framing, and hop-by-hop headers before invoking middleware. It rejects malformed request headers and unsupported transfer-coding sequences before middleware or policy dispatch. Headers named by a request's `Connection` field are omitted from middleware input and removed before forwarding, except for the validated WebSocket upgrade pair.

The request context identifies the originating sandbox to operator-run services. It carries the sandbox ID (`sandbox_id`) and the sandbox name (`sandbox_name`), letting audit and approval interfaces show a human-readable name instead of an opaque ID. The name is best-effort: a supervisor that cannot resolve it, or an older supervisor that predates the field, sends an empty string. Services should fall back to the sandbox ID when the name is empty.

## Choose a Middleware Type

| Type | Registration | Payload limit | Deployment |
Expand Down
2 changes: 2 additions & 0 deletions proto/supervisor_middleware.proto
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ message RequestContext {
string sandbox_id = 2;
// Workload process that originated the request, when available.
Process originating_process = 3;
// Sandbox name that originated the request.
string sandbox_name = 4;
}

// HttpRequestTarget describes the admitted HTTP destination and request target.
Expand Down
Loading