feat(providers): AWS STS AssumeRole refresh strategy and aws-s3 profile#1782
Conversation
|
This pull request has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. |
|
This is still waiting on #1638 to go in first. |
1b1cbd3 to
cb10f83
Compare
|
/ok to test cb10f83 |
pimlock
left a comment
There was a problem hiding this comment.
Hi @russellb, I did a first pass, but I run out of time to get through this fully. I needed to check this out locally and run this to understand how it worked (wasn't as familiar with the cred refresh).
I added a few comments.
The biggest item I had was around how the implicit extra env vars that are managed by the refresh, but not defined as such in the provider profile.
I did investigation around this and options. I would need to spend more time figuring out what makes sense, but I will need to come back to it on Thursday. In the meantime, I can share raw notes from agent, but I haven't checked these very deeply yet: https://gist.github.com/pimlock/6a99b3d9c4e19c6efc3b0d2da59b6b63
|
@pimlock thank you very much for the review! I know it can be very time-consuming. I will dig into the comments tomorrow. |
|
/ok to test cbe9b43 |
|
@russellb Thanks for addressing the feedback! I'm running a bit behind, so I'll do a final review tomorrow and follow up. |
No problem! |
cbe9b43 to
2c6dfb8
Compare
|
/ok to test 2c6dfb8 |
|
Label |
PR Review StatusValidation: This PR is project-valid because it implements the linked provider-v2 feature request #1576 and completes the AWS credential path enabled by merged PR #1638. Head SHA: Thanks @pimlock — I checked the runtime-credential resolution, duplicated mintability logic, declarative multi-output mapping, and collision-reservation concerns from your July 7 review. Thanks @russellb — the July 8 update does address those original concerns with the shared mintability check and pinned
Please add regression coverage for the endpoint override rejection, partial source credentials, profile/strategy/canonical-key validation, v2 disable-after-config rotation, and additional-output expiry cleanup. No local tests or builds were run as part of this code-only review. Docs: The feature is otherwise covered in existing Fern-managed pages and needs no navigation change, but the docs gate fails until the secret-handling example is corrected. CI: Next state: |
|
Addressed each finding in its own commit on top of 2c6dfb8. Summary below; commit SHAs referenced per item.
Fixed in e7a978c. The endpoint override is now a
Fixed in 916e9d2. Added
Fixed in 29970bb. The mint path now treats the pair as all-or-nothing and errors on a partial pair instead of falling through to the ambient identity; configure rejects the partial pair early with the same message.
Fixed in a726dfc. The gate now runs in
Fixed in 6799dcc.
Fixed in e8495fd. The off-AWS example now uses
Regression tests land with each corresponding fix rather than as a separate commit: endpoint-override rejection and partial-source-credential rejection (configure + mint), profile/strategy/canonical-key validation (no-profile and non-canonical-key cases), v2 disable-after-config rotation, and additional-output expiry cleanup (with an independently-changed expiry preserved). Verification: One note for the CI gate: the |
Re-check After Author UpdateThanks @russellb. I reviewed current head What I checked: the full current diff, the six fix commits, the STS configure/mint/delete paths, regression coverage, middle-label wildcard consumers, Fern docs, and the current CI state. The endpoint-override, canonical profile binding, partial source-credential, providers-v2 gate, and process-argument secret findings are resolved. Multi-key expiry cleanup is present, but its preservation guarantee is not concurrency-safe. Disposition: partially resolved; author changes are still required. Remaining items:
No local tests or builds were run as part of this code-only review. Docs: the Fern-managed pages are updated and need no navigation change; the remaining example corrections are listed above. CI: Next state: |
|
Addressed the three remaining items from the re-check, each in its own commit on top of e8495fd.
Fixed in 557221f. The equality check and removal now live in a pure helper (
Fixed in 623512e.
Fixed in 6c58d2a. The boto3 walkthrough now creates a Verification: |
Re-check After Author UpdateThanks @russellb. I reviewed current head What I checked: the full current diff, the focused commits since Disposition: partially resolved; one blocking correctness/security issue remains. Remaining items:
No local tests, builds, clippy, pre-commit, or E2E were run as part of this code-only review. Docs: the user-facing behavior is covered by existing Fern pages under CI: Next state: |
|
Addressed both concurrency items from the latest re-check (head 623512e), each in its own commit.
Fixed in f462775. The generation acts as the store-backed check you described (a delete bumps the row out of existence, so the version-matched write fails closed). I did not introduce a separate lease/tombstone object — the existing per-row Deterministic regression test Residual window I want to be transparent about: a delete that lands between the post-mint existence re-check and the provider credential write can still leave the freshly minted credential values in the provider. That is consistent with delete semantics —
Fixed in bf68e0b. Scope note: this guard serializes within a gateway process, matching the existing concurrency model for sandbox-affecting mutations. It is not cross-replica; a store-backed reservation would be needed for multi-writer HA, which would be a broader change than this profile-scoped PR. Verification: |
Re-check After Author UpdateThanks @russellb. I reviewed current head What I checked: the full current diff, the two commits since Disposition: partially resolved; author changes are still required. Remaining items:
No local tests, builds, clippy, pre-commit, or E2E were run as part of this code-only review. Docs: the user-facing behavior is covered in existing Fern pages; no CI: DCO is green. Required Branch Checks, Helm Lint, and E2E are waiting for the copy-PR mirror on this head. Next state: |
PR Review StatusValidation: This PR is project-valid because it implements the linked provider-v2 request #1576 and completes the AWS credential path enabled by merged PR #1638. Head SHA: Thanks @russellb. I checked the three commits since the prior
Non-blocking follow-ups:
No local tests, builds, clippy, pre-commit, or E2E were run as part of this code-only review. Docs: the direct UX is covered in existing Fern pages and needs no navigation change, subject to the reference correction above. CI: Next state: |
|
Addressed the two warnings and the first blocking item (head b0365a1). On the second blocking item I'm pushing back — reasoning below.
Fixed in a5a89f0. The rotation now claims the generation before touching the provider: it performs the version-matched refresh-state write ( On literal single-operation atomicity across the two objects: the store exposes only per-object CAS — there is no multi-object transaction API — so a single atomic datastore op spanning the refresh row and the provider row isn't available. The claim-then-write ordering is the optimistic-concurrency equivalent: generation ownership is the gate, and the loser mutates nothing. (I also switched the persist to consume a typed conditional-write result rather than matching error strings — see the CWE-693 item below.) Deterministic tests:
You're right that the gateway is active-active (I checked:
The guard added in this PR is still a strict improvement — it closes the same-process race (the common case, including single-replica rollouts). I'd propose the cross-replica reservation be tracked as its own follow-up rather than gating this feature. Happy to file that issue / open an RFC if you agree with the scoping.
Fixed in b0365a1. The explicit source-credential path now accepts an optional secret
Fixed in 31c339e. Verification: |
|
/ok to test b0365a1 |
|
Hi @russellb, thanks for addressing all the comments. I think this is ready For the context, the I agree on postponing this item:
IMO this is now ready, if you could please resolve conflicts, I will approve and we can get this merged. |
Add gateway-managed AWS STS credential refresh (provider-v2, NVIDIA#1576). The gateway calls sts:AssumeRole and writes three short-lived credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) to the provider record; the proxy re-signs requests with SigV4. Adds the aws and aws-s3 provider profiles and a declarative multi-output refresh model (additional_outputs) so one AssumeRole co-mints all three credentials. Hardening (from review): - Reject caller-supplied STS endpoint overrides; the endpoint is always region-resolved in production, test-only otherwise (CWE-918). - Validate a single canonical STS profile binding at configure (matching strategy, canonical AWS_ACCESS_KEY_ID primary, both declared outputs); remove the mint-path fallback to hardcoded sibling keys. - Require aws_access_key_id/aws_secret_access_key as a complete pair, with optional aws_session_token for temporary source credentials. - Enforce the providers_v2 gate on every mint (worker and manual rotate), not just at configure time. - Clear refresh-owned expiries for the primary and every pinned additional output inside the provider CAS closure, preserving independent changes. - Gate the provider credential write on refresh-generation ownership so a rotation cannot resurrect a deleted refresh or write stale-generation credentials (CWE-362). - Serialize refresh configuration against sandbox mutations to reserve credential keys atomically within a gateway process. - Return a typed Conflict for absent-row conditional writes instead of a backend-dependent error string. - Mirror runtime whole-middle-label host wildcard semantics in the prover. Signed-off-by: Russell Bryant <rbryant@redhat.com>
b0365a1 to
e3ad817
Compare
|
Conflicts are resolved, but I'm running through the full example scenario to make sure it's good, as the resolved conflicts were not trivial edits. |
Testing complete. Still functional. |
|
/ok to test e3ad817 |
|
@pimlock do you have more visibility into what failed in CI? or the ability to retrigger it if it's an environment thing? |
Summary
Add gateway-owned AWS STS AssumeRole as a credential refresh strategy, ship
awsandaws-s3provider profiles, and extend policy validation to support single-label*wildcards in middle DNS labels.Combined with the SigV4 proxy re-signing from #1638 (now merged), sandboxes can access S3 and other AWS services without ever seeing real credentials — the gateway mints short-lived STS credentials and the proxy re-signs requests on the fly.
Related Issue
Refs #1576
Changes
STS credential refresh (
openshell-server)AwsStsAssumeRolevariant inProviderCredentialRefreshStrategy(proto fieldAWS_STS_ASSUME_ROLE = 6).provider_refresh.rsimplements the refresh loop: callssts:AssumeRoleusing the gateway's ambient AWS credentials (or explicit long-lived keys from refresh material), stores the resultingAccessKeyId,SecretAccessKey, andSessionTokenon the provider, and schedules re-rotation before expiry.ConfigureProviderRefreshandRotateProviderCredentialRPCs gated behind theproviders_v2_enabledsetting.Provider profiles (
openshell-providers)AWS provider profiles follow the same pattern as the existing
aws-bedrockprofile: a generic base plus service-specific variants.providers/aws.yaml— base AWS profile with STS refresh material (role_arn, session_name, external_id, aws_region, optional long-lived keys) but no endpoints or binaries allowlist. Intended for AWS services that don't yet have a dedicated profile — the user attaches their own policy to supply endpoints. Same rolegoogle-cloudplays relative togoogle-vertex-ai.providers/aws-s3.yaml— S3-specific profile that adds pre-configured endpoints covering regional (*.s3.*.amazonaws.com), global (*.s3.amazonaws.comwithsigning_region: us-east-1), and dualstack variants. All endpoints usecredential_signing: sigv4andsigning_service: s3. Includes a binaries allowlist for Python, curl, and the AWS CLI.Policy: single-label
*wildcard in middle DNS labels (openshell-policy)validate_host_wildcardnow permits*as a complete middle label (e.g.*.s3.*.amazonaws.com) in addition to leading-label and intra-label positions. Matches exactly one DNS label via Rego'sglob.matchwith.separator, consistent with TLS wildcard semantics.host_matches_wildcard_middlerule andendpoint_config_for_middle_wildcardin Rego.**in any position and partial wildcards in middle labels.TUI (
openshell-tui)AwsStsAssumeRolematch arm to the refresh strategy label in the provider settings view.Documentation
docs/sandboxes/manage-providers.mdx: STS refresh setup instructionsexamples/aws-s3-sts.md: end-to-end manual test guide (create IAM role, configure provider, test S3 PUT/GET/LIST from sandbox)Testing
cargo test -p openshell-providers— 65 passed (STS serde roundtrip, aws/aws-s3 profile parsing, endpoint validation)cargo test -p openshell-policy— 91 passed (wildcard DNS validation, middle-label wildcards, credential_signing validation)cargo test -p openshell-server— all passed (STS configure v2 gate, success path, credential key collision)cargo test -p openshell-supervisor-network— all passed (wildcard host matching, OPA endpoint config)cargo test -p openshell-tui— 24 passedChecklist