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
672 changes: 586 additions & 86 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ apollo-parser = "0.8.5"
# HTTP client
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls-native-roots"] }

# AWS SDK
aws-config = { version = "1", default-features = false, features = ["rustls", "rt-tokio", "behavior-version-latest"] }
aws-sdk-sts = { version = "1", default-features = false, features = ["rustls", "rt-tokio", "behavior-version-latest"] }

# WebSocket
tokio-tungstenite = { version = "0.26", features = ["rustls-tls-native-roots"] }

Expand Down
19 changes: 19 additions & 0 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ Credential placeholders in proxied HTTP requests can be resolved by the proxy
when policy allows the target endpoint. Secrets must not be logged in OCSF or
plain tracing output.

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
credentials from the provider, and forwards the request upstream. The signing
mode is auto-detected from the client SDK's `x-amz-content-sha256` header:

- **Signed body** (hex hash): buffers the request body, computes its SHA-256,
and includes the hash in the signature. Used by Bedrock and most AWS services.
- **Streaming unsigned** (`STREAMING-UNSIGNED-PAYLOAD-TRAILER`): signs headers
only and streams the body through without buffering. Used by S3 uploads with
`aws-chunked` encoding.
- **Unsigned payload** (`UNSIGNED-PAYLOAD`): signs headers only with no body
hash. Used by S3 over HTTPS for non-chunked requests.

Two explicit overrides are available: `credential_signing: sigv4:body` (always
buffer and hash) and `sigv4:no_body` (always unsigned). The `Expect:
100-continue` header is handled within the SigV4 path so clients like boto3
transmit the body before the proxy forwards to upstream.

## Connect and Logs

The supervisor runs an SSH server on a Unix socket inside the sandbox. The
Expand Down
11 changes: 7 additions & 4 deletions architecture/security-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,21 @@ request is denied.
## Host Wildcards

Network endpoint `host` patterns accept a `*` wildcard inside the first DNS
label only. The OPA runtime matches with a `.` label boundary, so a wildcard
never spans dots. The validator enforces the same boundary so that policy load
fails fast instead of silently mismatching at the proxy.
label and as an entire middle DNS label. The OPA runtime matches with a `.`
label boundary, so a wildcard never spans dots. The validator enforces the same
boundary so that policy load fails fast instead of silently mismatching at the
proxy.

| Pattern | Accepted | Example match | Notes |
|---|---|---|---|
| `*.example.com` | Yes | `api.example.com` | Single first label of any value. |
| `**.example.com` | Yes | `a.b.example.com` | Recursive wildcard as the entire first label. |
| `*-aiplatform.googleapis.com` | Yes | `us-central1-aiplatform.googleapis.com` | Intra-label wildcard inside the first DNS label. |
| `*.s3.*.amazonaws.com` | Yes | `bucket.s3.us-east-1.amazonaws.com` | Middle-label `*` matches exactly one DNS label. |
| `*` or `**` | No | — | Matches every host. |
| `*.com`, `**.com` | No | — | TLD wildcards (`labels <= 2`). |
| `foo.*.example.com` | No | — | Wildcard outside the first DNS label. |
| `foo.us-*.example.com` | No | — | Partial middle-label wildcards are not allowed. |
| `foo.**.example.com` | No | — | Recursive wildcard outside the first label is not allowed. |
| `foo**.example.com` | No | — | Recursive `**` mixed inside a label; allowed only as the entire first label. |

Validation rejects the disallowed patterns at policy load time with a message
Expand Down
2 changes: 2 additions & 0 deletions crates/openshell-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ enum CliProviderRefreshStrategy {
Oauth2RefreshToken,
Oauth2ClientCredentials,
GoogleServiceAccountJwt,
AwsStsAssumeRole,
}

impl CliProviderRefreshStrategy {
Expand All @@ -669,6 +670,7 @@ impl CliProviderRefreshStrategy {
Self::Oauth2RefreshToken => "oauth2_refresh_token",
Self::Oauth2ClientCredentials => "oauth2_client_credentials",
Self::GoogleServiceAccountJwt => "google_service_account_jwt",
Self::AwsStsAssumeRole => "aws_sts_assume_role",
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/openshell-cli/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5039,6 +5039,7 @@ fn provider_refresh_strategy(strategy: &str) -> Result<ProviderCredentialRefresh
"google_service_account_jwt" => {
Ok(ProviderCredentialRefreshStrategy::GoogleServiceAccountJwt)
}
"aws_sts_assume_role" => Ok(ProviderCredentialRefreshStrategy::AwsStsAssumeRole),
_ => Err(miette!("unsupported provider refresh strategy: {strategy}")),
}
}
Expand Down Expand Up @@ -5091,6 +5092,7 @@ fn provider_refresh_strategy_name(strategy: ProviderCredentialRefreshStrategy) -
ProviderCredentialRefreshStrategy::Oauth2RefreshToken => "oauth2_refresh_token",
ProviderCredentialRefreshStrategy::Oauth2ClientCredentials => "oauth2_client_credentials",
ProviderCredentialRefreshStrategy::GoogleServiceAccountJwt => "google_service_account_jwt",
ProviderCredentialRefreshStrategy::AwsStsAssumeRole => "aws_sts_assume_role",
ProviderCredentialRefreshStrategy::Unspecified => "unspecified",
}
}
Expand Down
Loading
Loading