You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #1638 (SigV4 credential re-signing) adds AWS-specific fields (credential_signing, signing_service) threaded through the entire L7 proxy stack — policy YAML, proto, OPA data, L7EndpointConfig, RelayRequestOptions, and inline branching in relay_http_request_with_options_guarded. This works, but the approach doesn't scale: every future service-specific behavior (Azure token exchange, GCP IAM, rate limiting, custom header injection) would require adding more fields to the same structs and more if branches to the same relay function.
The review discussion on #1638 (comment) noted that a middleware pattern would fit more cleanly, aligning with the broader provider profile vision in #896.
A middleware is a named, configurable transform that intercepts an HTTP request between L7 policy evaluation and upstream forwarding. Middlewares operate on parsed HTTP requests — they see headers, body framing, and the resolved credential context, and can modify headers, buffer/re-sign bodies, or inject new headers before the request goes upstream.
Middleware configurations are defined as a top-level block (middleware_configs) parallel to network_policies. Each entry declares its middleware type and type-specific settings. Endpoints reference middleware configs by name.
middleware_configs: top-level map of config name → config object. Each entry has a middleware field naming the middleware type, plus type-specific fields. Validated at policy load time by the middleware type's own validation function.
middleware (on endpoint): ordered list of middleware config names to apply. Each name must resolve to an entry in middleware_configs. Validated at policy load time.
This design provides three advantages over nesting configs inside each policy:
Shared configs — multiple endpoints across different policies reference the same middleware config by name without duplication.
Multiple instances of the same type — e.g. sigv4_bedrock and sigv4_s3 are both sigv4 middlewares with different signing_service values.
Clean indirection — the config name carries intent (sigv4_bedrock) while the middleware field inside it identifies the type.
In relay_http_request_with_options_guarded, replace the SigV4 inline block with a middleware chain:
for mw in&options.middleware{match mw.process_request(&mw_ctx,&req,&headers, client).await? {MiddlewareAction::Forward{ data } => {
upstream.write_all(&data).await?;
forwarded = true;break;}MiddlewareAction::Passthrough => continue,}}
Move existing SigV4 logic from the inline block in rest.rs into SigV4Middleware. The sigv4.rs utility module stays as-is — pure signing functions used by the middleware.
L7EndpointConfig and RelayRequestOptions drop credential_signing/signing_service/host and gain middleware: Vec<Arc<dyn L7Middleware>>
Middleware instances are constructed at policy load time: parse middleware_configs top-level block, then resolve each endpoint's middleware list against it
Proto: top-level middleware_configs map + repeated string middleware on NetworkEndpoint
Backward compat: credential_signing: sigv4 on an endpoint expands to a synthetic middleware_configs entry and middleware: [sigv4_<host>] during policy loading
Per-policy middleware_config (original proposal in this issue) — nests middleware config inside each network_policy. Works but forces duplication when multiple policies need the same middleware config. The top-level approach suggested in comments is strictly better.
Provider-level middleware only (tie to Enhanced Provider Management #896 provider profiles) — cleaner for providers that know their endpoints, but some middleware (rate limiting, custom headers) is per-endpoint, not per-provider. The endpoint-level middleware list handles both cases.
Tower-style middleware — more generic but heavier. The L7 proxy already has its own request/response lifecycle (parsed HTTP over raw streams, not hyper Request/Response objects). A simple trait matching the existing relay flow is more practical.
Explored the L7 proxy architecture across these files:
l7/mod.rs: L7EndpointConfig struct, parse_l7_config, CredentialSigning enum, policy validation. The PR adds credential_signing and signing_service fields here, parsed from OPA regorus values.
l7/rest.rs: relay_http_request_with_options_guarded is the core relay function. The PR adds ~175 lines of inline SigV4 branching here (detect payload mode, handle Expect: 100-continue, buffer body, sign, forward). This is the function that would gain the middleware loop.
l7/relay.rs: relay_rest and relay_with_route_selection thread RelayRequestOptions (including the new SigV4 fields) into the rest.rs relay function.
l7/provider.rs: L7Provider trait — parse/relay/deny. Middleware sits between parse+evaluate and relay, not inside the provider.
proxy.rs: CONNECT tunnel handling, TLS termination, routes to relay_with_inspection. Threads CredentialSigning::None through options in ~6 locations.
sigv4.rs: Pure utility functions (extract region, strip AWS headers, apply signing). No policy coupling — stays as-is.
openshell-policy/src/lib.rs: NetworkEndpointDef with serde, proto conversion, policy validation. PR adds credential_signing/signing_service string fields.
proto/sandbox.proto: NetworkEndpoint message. PR adds fields 19-20.
The PR review comments explicitly discuss the awkwardness of AWS-specific fields in the generic policy schema and reference Enhanced Provider Management #896 as the broader solution space.
PR #1638 (SigV4 credential re-signing) adds AWS-specific fields (
credential_signing,signing_service) threaded through the entire L7 proxy stack — policy YAML, proto, OPA data,L7EndpointConfig,RelayRequestOptions, and inline branching inrelay_http_request_with_options_guarded. This works, but the approach doesn't scale: every future service-specific behavior (Azure token exchange, GCP IAM, rate limiting, custom header injection) would require adding more fields to the same structs and moreifbranches to the same relay function.The review discussion on #1638 (comment) noted that a middleware pattern would fit more cleanly, aligning with the broader provider profile vision in #896.
A middleware is a named, configurable transform that intercepts an HTTP request between L7 policy evaluation and upstream forwarding. Middlewares operate on parsed HTTP requests — they see headers, body framing, and the resolved credential context, and can modify headers, buffer/re-sign bodies, or inject new headers before the request goes upstream.
Middleware configurations are defined as a top-level block (
middleware_configs) parallel tonetwork_policies. Each entry declares its middleware type and type-specific settings. Endpoints reference middleware configs by name.middleware_configs: top-level map of config name → config object. Each entry has amiddlewarefield naming the middleware type, plus type-specific fields. Validated at policy load time by the middleware type's own validation function.middleware(on endpoint): ordered list of middleware config names to apply. Each name must resolve to an entry inmiddleware_configs. Validated at policy load time.This design provides three advantages over nesting configs inside each policy:
sigv4_bedrockandsigv4_s3are bothsigv4middlewares with differentsigning_servicevalues.sigv4_bedrock) while themiddlewarefield inside it identifies the type.In
relay_http_request_with_options_guarded, replace the SigV4 inline block with a middleware chain:Move existing SigV4 logic from the inline block in
rest.rsintoSigV4Middleware. Thesigv4.rsutility module stays as-is — pure signing functions used by the middleware.L7EndpointConfigandRelayRequestOptionsdropcredential_signing/signing_service/hostand gainmiddleware: Vec<Arc<dyn L7Middleware>>middleware_configstop-level block, then resolve each endpoint'smiddlewarelist against itmiddleware_configsmap +repeated string middlewareonNetworkEndpointcredential_signing: sigv4on an endpoint expands to a syntheticmiddleware_configsentry andmiddleware: [sigv4_<host>]during policy loadingmiddleware_config(original proposal in this issue) — nests middleware config inside eachnetwork_policy. Works but forces duplication when multiple policies need the same middleware config. The top-level approach suggested in comments is strictly better.middlewarelist handles both cases.Explored the L7 proxy architecture across these files:
l7/mod.rs:L7EndpointConfigstruct,parse_l7_config,CredentialSigningenum, policy validation. The PR addscredential_signingandsigning_servicefields here, parsed from OPA regorus values.l7/rest.rs:relay_http_request_with_options_guardedis the core relay function. The PR adds ~175 lines of inline SigV4 branching here (detect payload mode, handle Expect: 100-continue, buffer body, sign, forward). This is the function that would gain the middleware loop.l7/relay.rs:relay_restandrelay_with_route_selectionthreadRelayRequestOptions(including the new SigV4 fields) into the rest.rs relay function.l7/provider.rs:L7Providertrait — parse/relay/deny. Middleware sits between parse+evaluate and relay, not inside the provider.proxy.rs: CONNECT tunnel handling, TLS termination, routes torelay_with_inspection. ThreadsCredentialSigning::Nonethrough options in ~6 locations.sigv4.rs: Pure utility functions (extract region, strip AWS headers, apply signing). No policy coupling — stays as-is.openshell-policy/src/lib.rs:NetworkEndpointDefwith serde, proto conversion, policy validation. PR addscredential_signing/signing_servicestring fields.proto/sandbox.proto:NetworkEndpointmessage. PR adds fields 19-20.The PR review comments explicitly discuss the awkwardness of AWS-specific fields in the generic policy schema and reference Enhanced Provider Management #896 as the broader solution space.
L7Middlewaretrait defined withprocess_requestreturningMiddlewareActionSigV4Middlewareimplements the trait, moves logic from rest.rs inline blockrelay_http_request_with_options_guardeduses middleware chain instead of inline SigV4 branchingmiddleware_configsblock and per-endpointmiddlewarelist referencing configs by namecredential_signing: sigv4policies expand to middleware form during loadingmise run pre-commitandmise run testpass