Skip to content

feat(sandbox): proxy-side AWS SigV4 credential signing for CONNECT tunnels#1638

Open
jhjaggars wants to merge 2 commits into
NVIDIA:mainfrom
jhjaggars:sigv4-credential-signing
Open

feat(sandbox): proxy-side AWS SigV4 credential signing for CONNECT tunnels#1638
jhjaggars wants to merge 2 commits into
NVIDIA:mainfrom
jhjaggars:sigv4-credential-signing

Conversation

@jhjaggars
Copy link
Copy Markdown
Contributor

@jhjaggars jhjaggars commented May 29, 2026

Add proxy-side AWS SigV4 credential signing for CONNECT tunnel requests. When credential_signing: sigv4 is set on a REST endpoint in the policy YAML, the sandbox proxy strips the client's invalid SigV4 signature (computed with placeholder credentials), re-signs the request with real AWS credentials from the SecretResolver using the official aws-sigv4 crate, and forwards the properly signed request upstream. This enables AWS services like Bedrock to work through OpenShell's credential proxy without exposing real secrets to the sandbox.
Closes #1631
Related: #1576 (STS refresh strategy — sandbox-side session token support is included here), #1568 (Google Vertex AI — complementary, no conflicts)

  • Proto: add credential_signing (field 18) and signing_service (field 19) to NetworkEndpoint
  • Policy lib: add both fields to NetworkEndpointDef serde struct and proto conversions
  • New sigv4.rs module: SigV4 signing via aws-sigv4 crate, AWS header stripping, region extraction from hostname
  • L7 config: add CredentialSigning enum and signing_service field, parse from OPA policy data
  • CONNECT tunnel relay (l7/rest.rs): hook SigV4 into relay_http_request_with_options_guarded — strip AWS headers before fail-closed placeholder scan, buffer body, re-sign after credential rewrite
  • OPA/relay plumbing: pass credential_signing and signing_service through OPA data and RelayRequestOptions
  • Session token support: resolve optional AWS_SESSION_TOKEN for STS temporary credentials
  • Dependencies: add aws-sigv4, aws-credential-types, aws-smithy-runtime-api; remove hmac
  • Integration test: standalone test against real Bedrock exercising the full strip → re-sign → send flow (#[ignore]'d, requires AWS credentials)
endpoints:
  - host: bedrock-runtime.us-east-2.amazonaws.com
    port: 443
    protocol: rest
    credential_signing: sigv4
    signing_service: bedrock
    access: read-write
    enforcement: enforce
  • Minimal signed headers: only host, content-type, and content-length are included in the signature. Signing all headers causes 403 InvalidSignatureException because the proxy modifies headers like Connection and Accept-Encoding between signing and delivery.
  • Explicit signing_service field: AWS signing service names don't always match hostname prefixes (e.g. bedrock-runtimebedrock). The aws-sigv4 crate is just signing math — hostname-to-signing-name resolution lives in per-service SDK crates with no lightweight alternative. Making the policy author specify it is correct and extensible.
  • Payload checksum: PayloadChecksumKind::XAmzSha256 is enabled to include x-amz-content-sha256, which Bedrock requires.
  • Unit tests for region extraction, signing output format, header stripping, session token inclusion, and request rewriting (6 tests)
  • OPA integration test verifying credential_signing and signing_service round-trip through proto → OPA data → L7 config
  • Integration test against real Bedrock (cargo test -p openshell-sandbox --test sigv4_signing -- --ignored)
  • End-to-end: Claude Code → OpenShell sandbox (OpenShift) → Bedrock us-east-2 with proxy-side SigV4 re-signing
  • mise run pre-commit passes
  • Unit tests added/updated
  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented May 29, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 29, 2026

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@jhjaggars jhjaggars marked this pull request as draft May 29, 2026 19:45
@jhjaggars
Copy link
Copy Markdown
Contributor Author

I have read the DCO document and I hereby sign the DCO.

Comment thread crates/openshell-sandbox/src/l7/rest.rs
Comment on lines +138 to +141
#[serde(default, skip_serializing_if = "String::is_empty")]
credential_signing: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
signing_service: String,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there practical extensions to the values for these fields? Is there ever a non-AWS use case? Just trying to understand how AWS-specific these settings are vs the names of the settings that imply they could map to other service provider use cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These feel pretty specific to AWS. It's awkward, but I didn't see an obviously better way to have a special case like this today. credential_signing might be used in other services, but the signing_service almost certainly wouldn't.

Copy link
Copy Markdown
Contributor Author

@jhjaggars jhjaggars Jun 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could imagine a cleaner way of expressing these things aligning with your issue here: #896. It does seem like a middleware pattern would fit nicely as well.

@jhjaggars jhjaggars force-pushed the sigv4-credential-signing branch from bec3afc to e2f78da Compare June 1, 2026 15:49
@russellb
Copy link
Copy Markdown
Contributor

russellb commented Jun 1, 2026

just for visibility - I started working on AWS STS credential support (#1576), using access to S3 as a test case.

This depends on sigv4 support, so I pulled that in to my branch, or at least a version of it from the end of last week.

main...russellb:OpenShell:feat/1576-aws-sts-with-sigv4

@jhjaggars jhjaggars force-pushed the sigv4-credential-signing branch from 1ee1ee5 to 84c5c3d Compare June 1, 2026 21:40
@jhjaggars jhjaggars marked this pull request as ready for review June 1, 2026 21:41
let mut full_request = rewrite_result.rewritten.clone();
full_request.extend_from_slice(overflow);
// Read remaining body based on content-length
if let BodyLength::ContentLength(body_len) = parse_body_length(header_str)? {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue that we can address in a follow-up is that this doesn't support chunked transfer (no content length for the whole body). When I was trying to test S3 access using the python boto library, it was doing chunked transfer. I started working on this in my branch, so maybe I'll get it worked out, but it's worth noting the limitation I think.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave this a shot, but it turned out to be a little harder than I thought. I think most aws services support skipping body signatures which is probably what we should do, but I think there are some edge cases that make it a little tricky.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I didn't get it working, either. Skipping sounds good if supported!

@jhjaggars jhjaggars force-pushed the sigv4-credential-signing branch from 84c5c3d to 54e0f39 Compare June 1, 2026 23:08
jhjaggars added 2 commits June 1, 2026 19:39
…nnels

Add proxy-side AWS SigV4 re-signing so sandbox clients can reach AWS
services (Bedrock) through the CONNECT tunnel using placeholder
credentials. The proxy strips the invalid signature, resolves real
credentials from the SecretResolver, re-signs with the aws-sigv4 crate,
and forwards. Configuration is policy-driven via two new fields
(credential_signing, signing_service).
Policy YAML example:
    credential_signing: sigv4
    signing_service: bedrock
Implementation:
- sigv4.rs: strip_aws_headers removes old auth headers before the
  fail-closed placeholder scan; apply_sigv4_to_request re-signs using
  the aws-sigv4 SDK with PayloadChecksumKind::XAmzSha256 enabled.
  Returns Result instead of panicking. Non-signed headers (Accept,
  User-Agent, etc.) are preserved in the output.
- rest.rs: SigV4 path buffers body (capped at MAX_REWRITE_BODY_BYTES)
  for signing, then forwards the re-signed request upstream.
- Proto: credential_signing (field 19), signing_service (field 20)
  on NetworkEndpoint.
- Policy/OPA: plumbed through serde, proto conversion, and Rego data.
- Supports AWS session tokens (STS temporary credentials).
- Integration test against real Bedrock (ignored, requires AWS creds).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reject policies where credential_signing is set but signing_service is
empty during validate_sandbox_policy() instead of failing at connection
time. The runtime check in rest.rs is kept as defense-in-depth.
@jhjaggars jhjaggars force-pushed the sigv4-credential-signing branch from 54e0f39 to 7141a81 Compare June 1, 2026 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(sandbox): proxy-side AWS SigV4 credential signing for CONNECT tunnels

3 participants