Require explicit AWS credentials in the S3 sink - #2964
Conversation
The S3 sink built its boto3 client with whatever credentials boto3 could resolve when the workflow omitted aws_access_key_id/aws_secret_access_key. Since /workflows/run accepts caller-supplied specifications, any workflow author could then write chosen content to a chosen bucket and key as the inference server's AWS identity. create_s3_client now raises before reaching boto3.client() unless both credentials are present, so the ambient chain (env vars, ~/.aws/credentials, instance or task role) is never consulted. aws_region stays optional. This is a breaking change for deployments that relied on an instance role for this block; those workflows must now pass credentials explicitly, ideally via an Environment Secrets Store block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QJc8mQDyvkz97MArPTd15t
The existing tests cover create_s3_client directly, which proves the guard exists but not that the server's identity is unreachable through the block. This test puts AWS credentials in the environment, the way an EC2 or ECS deployment holds them, then calls S3SinkBlockV1.run() with the credential fields omitted. It asserts the run fails and that botocore issues no API call at all. Reverting v1.py to the pre-fix version fails this test, so it discriminates on the behavior it claims to cover. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QJc8mQDyvkz97MArPTd15t
|
👋 Thanks for the pull request! Here is how automated Claude review works here, so you spend credits (and reviewer time) wisely. 🚦 This PR is marked Ready for review, so automated Claude review will run — and every pass spends real credits. Warning 💸 The Claude reviewer bills in credits, not vibesAutomated review spins up a real agent that reads real code and spends real credits on every pass. It is glad to help — but it is not a rubber duck, a linter you poke in a loop, or a substitute for reading the contributing guide. Treat it like an expensive senior reviewer whose time you booked, and show up prepared. Draft when unsure, Ready when you mean it:
However you get there, arrive prepared:
Reviews are not free. A draft costs nothing to review; a Ready PR is a promise that it is worth reviewing.
|
|
🤖 Claude review started at commit New commits are not auto-reviewed. Add the |
|
⏳ This PR is on hold pending your answers — the review will not advance to sign-off until the IMPORTANT question below is answered. The code change itself is correct and complete: IMPORTANT questionThe fix is a blanket removal of ambient-credential support for every deployment, but the threat model you describe is specific to the multi-tenant hosted Was an env-gated opt-in considered instead of a hard break — e.g. an Why this changes the outcome: if maintainers intend to preserve the IAM-role pattern for trusted self-hosted deployments, this PR should ship the env-gated opt-in rather than the blanket break; if the decision is deliberately fail-closed everywhere (accepting the self-hosted regression as documented in the PR body), it can proceed as-is. Either answer is fine — but it needs to be an explicit, confirmed product decision, not an implicit side effect. Unanswered, this may keep the PR out of a release. Re-review is not automatic: new commits are not auto-reviewed — add the Reviewed at HEAD: 6fe0ffd |
|
Review summary Skills: review-workflows-blocks, review-topic-backward-compat-and-versioning, review-topic-auth-and-tenant-security, review-topic-test-hygiene No blocking code-level findings. The credential-isolation fix is implemented correctly, the only caller ( This is not a merge-ready sign-off: one IMPORTANT product/security question is open (see the action-item comment above) about whether the blanket removal of ambient credentials — which regresses the AWS-recommended IAM instance-role pattern on trusted single-tenant self-hosted servers — is deliberate versus an env-gated opt-in. The review will not advance to sign-off until that is answered and confirmed. No changelog/version companion is required: the change is confined to a single Workflow block and touches neither Minor doubts (non-blocking): a missing-credentials Reviewed at HEAD: 6fe0ffd |
What does this PR do?
The S3 sink built its boto3 client with whatever credentials boto3 could resolve when the workflow omitted aws_access_key_id/aws_secret_access_key. Since /workflows/run accepts caller-supplied specifications, any workflow author could then write chosen content to a chosen bucket and key as the inference server's AWS identity.
create_s3_client now raises before reaching boto3.client() unless both credentials are present, so the ambient chain (env vars, ~/.aws/credentials, instance or task role) is never consulted. aws_region stays optional.
Related Issue(s):
Type of Change
This is a breaking change for deployments that rely on an instance role for this block. Those workflows must now pass credentials explicitly, ideally via an Environment Secrets Store block.
Testing
Test details:
test_create_s3_client_rejects_missing_credentials (test_s3_sink.py:265)
Passes both credentials as None and expects a ValueError. It also patches boto3.client and asserts it was never called, which is the difference between failing after boto3 resolved an identity and failing before it looked.
test_create_s3_client_passes_explicit_credentials (test_s3_sink.py:277)
Sends key, secret, and region through and asserts boto3.client got exactly those kwargs. Without it, the first test would pass against a function that raises on everything.
test_run_does_not_fall_back_to_server_aws_credentials (test_s3_sink.py:716)
Puts AWS credentials in the environment so the process looks like a server with an instance role, then calls the real S3SinkBlockV1.run() with a caller-chosen bucket and no workflow credentials. It asserts the run raises and that botocore.client.BaseClient._make_api_call was never reached, so no AWS request left the process. This is the test that covers the vulnerability itself; the other two only cover the helper.
Checklist