fix(aiohttp): Preserve SigV4-signed propagation headers - #7427
Conversation
f2c8ac5 to
5ca7b40
Compare
|
Could a maintainer take a look when there is time? Flagging it because this is a regression in 2.69.0 rather than a feature request, and the failure mode is quiet. S3 calls through aiobotocore fail only while a transaction is active, so object storage stops working while the rest of the service looks healthy and keeps serving. We lost about an hour of writes before tracing it back to the SDK, having first gone down the path of suspecting our own credentials. The change is small and mirrors the guard Happy to rework it if you would rather solve it in the Boto3 integration instead, or to drop the second test if it reads as redundant. |
ea6f871 to
8279bdd
Compare
pabloDeputter
left a comment
There was a problem hiding this comment.
Looks good; there is some behavior that doesn't yet match stdlib. See my comments for more info.
| ) in sentry_sdk.get_current_scope().iter_trace_propagation_headers( | ||
| span=span | ||
| ): | ||
| if key.lower() in signed_headers: |
There was a problem hiding this comment.
This only skips headers listed in SignedHeaders, so an existing unsigned sentry-trace is still overwritten. Could we match the behavior in stdlib.py and leave any existing sentry-trace untouched.
There was a problem hiding this comment.
adding a test for this would be nice as well; we could adapt test_aws_http_connection_appends_baggage_but_preserves_sentry_trace from to verify that sentry-trace remains unchanged while unsigned baggage is appended.
| # one, and the service rejects the request with | ||
| # `SignatureDoesNotMatch`. `stdlib` skips signed headers for the | ||
| # same reason; do the same on this path. | ||
| signed_headers: "Set[str]" = set() |
There was a problem hiding this comment.
Would be nice to use the same headers_to_skip approach as in the synchronous implementation? It would include an existing sentry-trace + signed propagation headers from the authorization header or uRL.
8279bdd to
aa7050d
Compare
|
Good catch on the All three points addressed: Switched to the Added
aiohttp suite is 147 passing. The 3 failures in Re-verified end to end against a live R2 bucket with all default integrations enabled: LIST and PUT both succeed inside an active transaction. |
|
Also, separately from the technical bits: thank you for the fast turnaround on this. You reviewed a drive-by PR from someone you have never seen before, within the hour, and caught a case I had already convinced myself was finished. That Appreciate the time, and sorry for the extra round trip. |
- collect headers that must not be modified in `headers_to_skip`. - preserve existing `sentry-trace` headers to avoid replacing the current trace context. - leave signed `sentry-trace` and `baggage` headers untouched; missing propagation headers are added. - tests added for signed, unsigned and presigned requests. Resolves getsentry#7426
aa7050d to
70af55c
Compare
|
|
||
|
|
||
| TRANSACTION_STYLE_VALUES = ("handler_name", "method_and_path_pattern") | ||
| _SENTRY_HEADER_NAMES = frozenset((BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME)) |
There was a problem hiding this comment.
You can share the set that already exists in stdlib.py by putting this in consts.py, for example.
|
Thank you @pabloDeputter and @alexander-alderman-webb |
|
The It failed on That parameter set feeds 16 random bytes as the cache key and expects an empty description: ("get", [uuid.uuid4().bytes], {}, ""),
Measured on this branch, 200k draws: Same shape as the CI failure. It is rare per test, but it runs on every job in the matrix, so it surfaces occasionally on unrelated PRs. Worth fixing separately if you agree: a fixed byte string that can never decode, for example |
Fixes #7426.
Cause
aiobotocore sends botocore's already-signed requests through aiohttp.
Since #7050 the Boto3 integration adds
sentry-traceandbaggageatbefore-sign, so both end up named inSignedHeaders. The aiohttp client integration then rewrites them on the way out. Forbaggageit appends:The value that gets sent no longer matches the value that was signed, so the service rejects the request with
SignatureDoesNotMatch.stdlibalready skips propagation headers named inSignedHeaders, also added in #7050. That is why synchronous boto3 is unaffected and only the aiobotocore path breaks. This applies the same rule on the aiohttp side, reusing the existing_get_aws_sigv4_signed_headers_from_authorization_headerhelper. Headers that are not named inSignedHeadersare still added, so propagation is unchanged.How I isolated it
Against a real Cloudflare R2 bucket on 2.69.0, holding credentials, botocore 1.43.75 and aiobotocore 3.9.1 constant:
Worth flagging for triage: R2 is not being strict about what may be signed. With no SDK in the picture, botocore signing
baggage,sentry-trace,x-koodle-testorx-amz-meta-*is accepted every time. The only problem is the header changing after it was signed.Why it is easy to miss
Calls only fail while a transaction is active. Work outside one keeps succeeding, so a background index warm at startup completed normally and every request after it failed. That made it look intermittent and credential-shaped rather than like a dependency change. In our case a rebuild moved 2.63.0 to 2.69.0 with no code change and quietly took down all object storage reads and writes.
Tests
test_outgoing_trace_headers_leave_sigv4_signed_headers_aloneasserts a signedsentry-traceandbaggagepass through untouched. It fails without the fix, with the appendedbaggageshown above.test_outgoing_trace_headers_added_when_not_signedcovers the other direction: an unsignedbaggageis still added alongside a signedsentry-trace, so the guard cannot silently drop propagation.Full
tests/integrations/aiohttp/passes (145).tests/integrations/boto3/has 3 failures that are already present on master without this change.Update
Also covered the presigned URL case.
stdlibreads signed header names from the Authorization header and, when there is none, fromX-Amz-SignedHeadersin the query string. The first version of this patch only handled the former, so a presigned URL fetched through aiohttp would still have had its signedbaggagerewritten. There is now a test for it, which fails without the fix in the same way.Suite is 146 passing.