feat(sandbox): proxy-side AWS SigV4 credential signing for CONNECT tunnels#1638
Draft
jhjaggars wants to merge 7 commits into
Draft
feat(sandbox): proxy-side AWS SigV4 credential signing for CONNECT tunnels#1638jhjaggars wants to merge 7 commits into
jhjaggars wants to merge 7 commits into
Conversation
Add `credential_signing: sigv4` policy field for REST endpoints. When set, the sandbox proxy strips the client's invalid SigV4 Authorization header (computed with placeholder credentials) and re-signs the request using real AWS credentials from the SecretResolver before forwarding upstream. This enables AWS services like Bedrock that use SigV4 auth to work through OpenShell's credential proxy without exposing real secrets to the sandbox. Changes: - proto: add credential_signing field (18) to NetworkEndpoint - l7/mod.rs: add CredentialSigning enum, parse from policy YAML - sigv4.rs: new module — SigV4 signing with AWS service name normalization - l7/rest.rs: hook SigV4 into CONNECT tunnel relay (strip AWS headers before fail-closed scan, re-sign after rewrite) - opa.rs: plumb credential_signing through OPA data - policy lib: add credential_signing to serde and proto conversion Tested end-to-end: Claude Code → OpenShell sandbox → Bedrock (us-east-2) with proxy-side SigV4 re-signing. Sandbox never sees real AWS credentials. TODO: replace hand-rolled SigV4 with aws-sigv4 crate for correct service name resolution across all AWS services. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
Replace the hand-rolled HMAC key derivation, canonical request formatting, and ISO 8601 date math with the official aws-sigv4 crate. This gives us a tested, correct SigV4 implementation while keeping the same proxy-side re-signing flow. - Add aws-sigv4, aws-credential-types, aws-smithy-runtime-api deps - Remove hmac dependency (no longer used directly) - Rewrite apply_sigv4_to_request to delegate to aws_sigv4::http_request::sign() - Delete AwsCredentials struct, sign_request, format_iso8601, and internal crypto helpers (~90 lines of hand-rolled code) - Keep normalize_aws_signing_name table (no SDK alternative exists) - Keep strip_aws_headers (raw byte manipulation, no SDK equivalent) - Update l7/rest.rs call site to pass credentials as &str directly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
…guessing Move the AWS signing service name into the policy document instead of inferring it from the hostname. The policy author specifies the correct signing service name (e.g. "bedrock") directly, eliminating the fragile normalize_aws_signing_name mapping table. Policy YAML example: credential_signing: sigv4 signing_service: bedrock - Proto: add signing_service field (19) to NetworkEndpoint - Policy lib: add signing_service to serde struct and proto conversions - L7/OPA/relay: plumb signing_service through to RelayRequestOptions - rest.rs: use signing_service from policy; error if empty with SigV4 - sigv4.rs: replace extract_aws_region_and_service with extract_aws_region, delete normalize_aws_signing_name entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
The aws-sigv4 SDK signs every header passed to SignableRequest. When the proxy or transport modifies headers like Connection or Accept-Encoding between signing and delivery, the signature breaks (403 InvalidSignature from AWS). Fix by only including host, content-type, and content-length in the signed headers — matching the working hand-rolled implementation. Also enable PayloadChecksumKind::XAmzSha256 so the x-amz-content-sha256 header is included in the signature, which AWS Bedrock requires. Tested end-to-end: Claude Code → OpenShell sandbox → Bedrock succeeds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
Add session_token parameter to apply_sigv4_to_request so SigV4 signing works with temporary STS credentials (AccessKeyId + SecretAccessKey + SessionToken). The aws-sigv4 SDK handles injecting the x-amz-security-token header automatically when a session token is present. The call site resolves AWS_SESSION_TOKEN from the SecretResolver alongside the access/secret keys. The token is optional — static IAM keys continue to work without it. Prepares for issue NVIDIA#1576 (aws_sts_assume_role refresh strategy) which will provide short-lived STS credentials via gateway-side rotation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
Add a standalone integration test that exercises the full proxy-side
re-signing flow: take a raw HTTP request with fake AWS auth headers,
strip them, re-sign with real credentials via apply_sigv4_to_request,
send over TLS to Bedrock, and verify a 200 response.
Marked #[ignore] — requires real AWS credentials:
AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx cargo test \
-p openshell-sandbox --test sigv4_signing -- --ignored --nocapture
Also makes the sigv4 module pub for integration test access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
|
All contributors have signed the DCO ✍️ ✅ |
Author
|
I have read the DCO document and I hereby sign the DCO. |
- Reorder imports per rustfmt - Use map_or instead of if-let/else for request line parsing - Use ToString::to_string instead of redundant closure - Use usize::try_from instead of truncating as-cast - Remove redundant as_deref on Option<&str> - Backtick-wrap identifiers in doc comments - Reformat long assert! lines Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add proxy-side AWS SigV4 credential signing for CONNECT tunnel requests. When
credential_signing: sigv4is 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 officialaws-sigv4crate, 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)
credential_signing(field 18) andsigning_service(field 19) toNetworkEndpointNetworkEndpointDefserde struct and proto conversionssigv4.rsmodule: SigV4 signing viaaws-sigv4crate, AWS header stripping, region extraction from hostnameCredentialSigningenum andsigning_servicefield, parse from OPA policy datal7/rest.rs): hook SigV4 intorelay_http_request_with_options_guarded— strip AWS headers before fail-closed placeholder scan, buffer body, re-sign after credential rewritecredential_signingandsigning_servicethrough OPA data andRelayRequestOptionsAWS_SESSION_TOKENfor STS temporary credentialsaws-sigv4,aws-credential-types,aws-smithy-runtime-api; removehmac#[ignore]'d, requires AWS credentials)host,content-type, andcontent-lengthare included in the signature. Signing all headers causes403 InvalidSignatureExceptionbecause the proxy modifies headers likeConnectionandAccept-Encodingbetween signing and delivery.signing_servicefield: AWS signing service names don't always match hostname prefixes (e.g.bedrock-runtime→bedrock). Theaws-sigv4crate 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.PayloadChecksumKind::XAmzSha256is enabled to includex-amz-content-sha256, which Bedrock requires.credential_signingandsigning_serviceround-trip through proto → OPA data → L7 configcargo test -p openshell-sandbox --test sigv4_signing -- --ignored)mise run pre-commitpasses