Skip to content
Open
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
28 changes: 20 additions & 8 deletions architecture/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,9 @@ their profile payloads.

Each logical gateway request captures the selected sources into one validated,
immutable effective catalog before deriving provider behavior. Policy layers,
credential scope, injected environment material, dynamic token grants, and
provider-environment revisions use that same catalog. Each configured source is
credential scope, injected environment material, dynamic token grants,
proxy-delivered static credential bindings, and provider-environment revisions
use that same catalog. Each configured source is
therefore fetched at most once per request, and a source revision change becomes
visible on the next request instead of partway through the current request. The
capture emits debug diagnostics with the combined catalog revision, source fetch
Expand Down Expand Up @@ -479,12 +480,23 @@ target so an edited export cannot overwrite a different profile. Database
migrations backfill existing rows with version 1.

Provider profile imports, updates, and deletes hold the sandbox synchronization
guard while checking attached-sandbox dynamic token grant ambiguity or in-use
state and writing the profile record. Sandbox creation with initial providers and
sandbox provider attach/detach use the same guard, so one gateway process cannot
interleave a profile mutation with a sandbox provider-set mutation that would
leave an ambiguous final dynamic-token state or a deleted custom profile that is
still referenced by a sandbox.
guard while checking attached-sandbox runtime-injected credential ambiguity or
in-use state and writing the profile record. Runtime-injected credentials are
dynamic token grants and proxy-delivered static credentials. Token grants on
overlapping selectors are ambiguous only at equal specificity; proxy-delivered
credentials are ambiguous on any overlap of the same port, because the workload
sends no credential the proxy could use to disambiguate. Sandbox creation with
initial providers and sandbox provider attach/detach use the same guard, so one
gateway process cannot interleave a profile mutation with a sandbox provider-set
mutation that would leave an ambiguous final runtime-credential state or a
deleted custom profile that is still referenced by a sandbox.

Proxy-delivered credential values are validated against their declared
placement when a provider is created or updated: `bearer` values must be
`token68` and named `header` values must not contain control characters. The
gateway copies the delivery mode, auth style, and header name onto the static
credential binding it sends to the sandbox so the supervisor never needs profile
metadata to build the header.

Policy and runtime settings are delivered together through the effective sandbox
config path. A gateway-global policy can override sandbox-scoped policy. The
Expand Down
17 changes: 17 additions & 0 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@ subject, and gateway SPIFFE subject, and their cache lifetime is capped by the
intermediate token response, stored subject-token expiry, and supervisor SVID
expiry.

Static credentials may instead opt into proxy delivery (`delivery: proxy` on a
`bearer` or named `header` profile credential). The gateway marks the static
credential binding with the delivery mode and placement metadata, and the
supervisor removes the key from the child environment so the workload holds
neither the secret nor a placeholder. For an inspected REST request whose
endpoint matches a proxy-delivered binding, the proxy resolves the value through
the same request-scoped resolver used for placeholders and replaces the complete
header immediately before the upstream write, after network policy, L7 rules,
and middleware have admitted the request. The binding is the only source of
truth: the proxy does not consult profile metadata at request time. Aliases of
one credential collapse into a single header; distinct matching credentials fail
closed because the gateway already rejects that configuration. Injection
requires an inspected REST endpoint without `tls: skip`, so uninspected traffic
forwards whatever header the application sent. Success and failure both emit an
OCSF HTTP activity event naming the environment key and endpoint but never the
value.

For AWS endpoints that require request-level signing, the proxy supports SigV4
re-signing. When `credential_signing: sigv4` is set on an L7 endpoint, the proxy
strips the client's placeholder-based AWS auth headers, re-signs with real
Expand Down
20 changes: 1 addition & 19 deletions crates/openshell-core/src/oauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,25 +239,7 @@ pub fn validate_access_token(token: &str) -> Result<()> {
Ok(())
}

fn is_token68(token: &str) -> bool {
let mut padding_started = false;
let mut saw_value = false;
for byte in token.bytes() {
if byte == b'=' {
padding_started = true;
continue;
}
if padding_started || !is_token68_value_byte(byte) {
return false;
}
saw_value = true;
}
saw_value
}

fn is_token68_value_byte(byte: u8) -> bool {
byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~' | b'+' | b'/')
}
pub use crate::provider_credentials::is_token68;

fn failure_message(status: reqwest::StatusCode, body: &str) -> String {
let Ok(error_response) = serde_json::from_str::<OAuthErrorResponse>(body) else {
Expand Down
Loading
Loading