Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metadata with trailing whitespace causes SignatureDoesNotMatch with signature_version s3 #2409

Closed
tiandrey opened this issue Jun 10, 2021 · 11 comments
Assignees
Labels
bug This issue is a confirmed bug. closing-soon p2 This is a standard priority issue s3

Comments

@tiandrey
Copy link

Describe the bug
I've run into this bug when our local pypicloud (that uses botocore and boto3) instance failed to upload specific package into local ceph storage - the error was botocore.exceptions.ClientError: An error occurred (SignatureDoesNotMatch) when calling the PutObject operation: Unknown. I've investigated this problem and found out that it was caused by trailing whitespace in one of metadata fields: if I remove trailing whitespace, upload works fine. Also changing signature_version from s3 to s3v4 solves the problem, but sometimes s3v4 signature is not supported and we have to use s3 signature.

Steps to reproduce
The following minimalistic snippet of code reproduces the bug.

import boto3
from botocore.config import Config

settings = {
    'endpoint_url': 'http://my-little-ceph',
    'aws_access_key_id': 'access_key',
    'aws_secret_access_key': 'secret_key',
}

config_hash = {
    's3': {
        'addressing_style': 'path',
    },
    'signature_version': 's3',
}
config = Config(**config_hash)
s3conn = boto3.resource('s3', config=config, **settings)
bucket = s3conn.Bucket('test-bucket')
data = b'test data'
key = bucket.Object('test_upload')

metadata = {
    'example': 'metadata with trailing space '
}

key.put(Metadata=metadata, Body=data)

Expected behavior
Upload completes without error.

Suggested solution
It looks like the problem lies in canonical_custom_headers method of HmacV1Auth class defined in botocore/auth.py:

            if headers[key] is not None:
                if lk.startswith('x-amz-'):
                    custom_headers[lk] = ','.join(v.strip() for v in
                                                  headers.get_all(key))

You strip leading and trailing whitespace from headers that are used to create signature, but leave original header values as is, and this leads to failure in signature checking. You should either remove .strip() from this method (I've checked, it works fine), or change values of original headers too.

Debug logs

2021-06-10 14:21:48,905 - botocore.hooks - DEBUG - Changing event name from creating-client-class.iot-data to creating-client-class.iot-data-plane
2021-06-10 14:21:48,906 - botocore.hooks - DEBUG - Changing event name from before-call.apigateway to before-call.api-gateway
2021-06-10 14:21:48,907 - botocore.hooks - DEBUG - Changing event name from request-created.machinelearning.Predict to request-created.machine-learning.Predict
2021-06-10 14:21:48,909 - botocore.hooks - DEBUG - Changing event name from before-parameter-build.autoscaling.CreateLaunchConfiguration to before-parameter-build.auto-scaling.CreateLaunchConfiguration
2021-06-10 14:21:48,909 - botocore.hooks - DEBUG - Changing event name from before-parameter-build.route53 to before-parameter-build.route-53
2021-06-10 14:21:48,909 - botocore.hooks - DEBUG - Changing event name from request-created.cloudsearchdomain.Search to request-created.cloudsearch-domain.Search
2021-06-10 14:21:48,910 - botocore.hooks - DEBUG - Changing event name from docs.*.autoscaling.CreateLaunchConfiguration.complete-section to docs.*.auto-scaling.CreateLaunchConfiguration.complete-section
2021-06-10 14:21:48,913 - botocore.hooks - DEBUG - Changing event name from before-parameter-build.logs.CreateExportTask to before-parameter-build.cloudwatch-logs.CreateExportTask
2021-06-10 14:21:48,913 - botocore.hooks - DEBUG - Changing event name from docs.*.logs.CreateExportTask.complete-section to docs.*.cloudwatch-logs.CreateExportTask.complete-section
2021-06-10 14:21:48,914 - botocore.hooks - DEBUG - Changing event name from before-parameter-build.cloudsearchdomain.Search to before-parameter-build.cloudsearch-domain.Search
2021-06-10 14:21:48,914 - botocore.hooks - DEBUG - Changing event name from docs.*.cloudsearchdomain.Search.complete-section to docs.*.cloudsearch-domain.Search.complete-section
/usr/local/lib/python3.5/dist-packages/boto3/compat.py:94: PythonDeprecationWarning: Boto3 will no longer support Python 3.5 starting February 1, 2021. To continue receiving service updates, bug fixes, and security updates please upgrade to Python 3.6 or later. More information can be found here: https://aws.amazon.com/blogs/developer/announcing-the-end-of-support-for-python-3-4-and-3-5-in-the-aws-sdk-for-python-and-aws-cli-v1/
  warnings.warn(warning, PythonDeprecationWarning)
2021-06-10 14:21:48,922 - botocore.loaders - DEBUG - Loading JSON file: /usr/local/lib/python3.5/dist-packages/boto3/data/s3/2006-03-01/resources-1.json
2021-06-10 14:21:48,924 - botocore.loaders - DEBUG - Loading JSON file: /usr/local/lib/python3.5/dist-packages/botocore/data/endpoints.json
2021-06-10 14:21:48,936 - botocore.hooks - DEBUG - Event choose-service-name: calling handler <function handle_service_name_alias at 0x7f6322060488>
2021-06-10 14:21:48,944 - botocore.loaders - DEBUG - Loading JSON file: /usr/local/lib/python3.5/dist-packages/botocore/data/s3/2006-03-01/service-2.json
2021-06-10 14:21:48,955 - botocore.hooks - DEBUG - Event creating-client-class.s3: calling handler <function add_generate_presigned_post at 0x7f632209dd90>
2021-06-10 14:21:48,955 - botocore.hooks - DEBUG - Event creating-client-class.s3: calling handler <function lazy_call.<locals>._handler at 0x7f6321be2268>
2021-06-10 14:21:48,970 - botocore.hooks - DEBUG - Event creating-client-class.s3: calling handler <function add_generate_presigned_url at 0x7f632209db70>
2021-06-10 14:21:48,972 - botocore.endpoint - DEBUG - Setting s3 timeout as (60, 60)
2021-06-10 14:21:48,973 - botocore.loaders - DEBUG - Loading JSON file: /usr/local/lib/python3.5/dist-packages/botocore/data/_retry.json
2021-06-10 14:21:48,974 - botocore.client - DEBUG - Registering retry handlers for service: s3
2021-06-10 14:21:48,974 - boto3.resources.factory - DEBUG - Loading s3:s3
2021-06-10 14:21:48,975 - boto3.resources.factory - DEBUG - Loading s3:Bucket
2021-06-10 14:21:48,976 - boto3.resources.model - DEBUG - Renaming Bucket attribute name
2021-06-10 14:21:48,977 - botocore.hooks - DEBUG - Event creating-resource-class.s3.Bucket: calling handler <function lazy_call.<locals>._handler at 0x7f6321be2378>
2021-06-10 14:21:48,977 - boto3.resources.factory - DEBUG - Loading s3:Object
2021-06-10 14:21:48,978 - botocore.hooks - DEBUG - Event creating-resource-class.s3.Object: calling handler <function lazy_call.<locals>._handler at 0x7f6321be2400>
2021-06-10 14:21:48,978 - boto3.resources.action - DEBUG - Calling s3:put_object with {'Metadata': {'example': 'metadata with trailing space '}, 'Body': b'test data', 'Key': 'test_upload/test_upload', 'Bucket': 'pypicloud-dev'}
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-parameter-build.s3.PutObject: calling handler <function validate_ascii_metadata at 0x7f6321e08730>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-parameter-build.s3.PutObject: calling handler <function sse_md5 at 0x7f6321e02b70>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-parameter-build.s3.PutObject: calling handler <function convert_body_to_file_like_object at 0x7f6321e09048>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-parameter-build.s3.PutObject: calling handler <function validate_bucket_name at 0x7f6321e02ae8>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-parameter-build.s3.PutObject: calling handler <bound method S3RegionRedirector.redirect_from_cache of <botocore.utils.S3RegionRedirector object at 0x7f632160b940>>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-parameter-build.s3.PutObject: calling handler <bound method S3ArnParamHandler.handle_arn of <botocore.utils.S3ArnParamHandler object at 0x7f63215ae048>>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-parameter-build.s3.PutObject: calling handler <function generate_idempotent_uuid at 0x7f6321e02950>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-call.s3.PutObject: calling handler <function conditionally_calculate_md5 at 0x7f63221989d8>
2021-06-10 14:21:48,979 - botocore.hooks - DEBUG - Event before-call.s3.PutObject: calling handler <function add_expect_header at 0x7f6321e02e18>
2021-06-10 14:21:48,980 - botocore.handlers - DEBUG - Adding expect 100 continue header to request.
2021-06-10 14:21:48,980 - botocore.hooks - DEBUG - Event before-call.s3.PutObject: calling handler <bound method S3RegionRedirector.set_request_url of <botocore.utils.S3RegionRedirector object at 0x7f632160b940>>
2021-06-10 14:21:48,980 - botocore.hooks - DEBUG - Event before-call.s3.PutObject: calling handler <function inject_api_version_header_if_needed at 0x7f6321e09158>
2021-06-10 14:21:48,980 - botocore.endpoint - DEBUG - Making request for OperationModel(name=PutObject) with params: {'context': {'auth_type': None, 'has_streaming_input': True, 'client_config': <botocore.config.Config object at 0x7f632190d048>, 'signing': {'bucket': 'pypicloud-dev'}, 'client_region': 'us-east-1'}, 'headers': {'Content-MD5': '63M6AMDJ0zbmVpGjerVCkw==', 'Expect': '100-continue', 'x-amz-meta-example': 'metadata with trailing space ', 'User-Agent': 'Boto3/1.16.63 Python/3.5.3 Linux/4.19.0-9-amd64 Botocore/1.19.63 Resource'}, 'query_string': {}, 'method': 'PUT', 'url_path': '/pypicloud-dev/test_upload/test_upload', 'body': <_io.BytesIO object at 0x7f6321621b48>, 'url': 'http://127.0.0.1:22900/pypicloud-dev/test_upload/test_upload'}
2021-06-10 14:21:48,980 - botocore.hooks - DEBUG - Event request-created.s3.PutObject: calling handler <bound method RequestSigner.handler of <botocore.signers.RequestSigner object at 0x7f6321903ba8>>
2021-06-10 14:21:48,980 - botocore.hooks - DEBUG - Event choose-signer.s3.PutObject: calling handler <function set_operation_specific_signer at 0x7f6321e02840>
2021-06-10 14:21:48,980 - botocore.hooks - DEBUG - Event before-sign.s3.PutObject: calling handler <bound method S3EndpointSetter.set_endpoint of <botocore.utils.S3EndpointSetter object at 0x7f63215ae0b8>>
2021-06-10 14:21:48,980 - botocore.utils - DEBUG - Using S3 path style addressing.
2021-06-10 14:21:48,980 - botocore.auth - DEBUG - Calculating signature using hmacv1 auth.
2021-06-10 14:21:48,980 - botocore.auth - DEBUG - HTTP request method: PUT
2021-06-10 14:21:48,981 - botocore.auth - DEBUG - StringToSign:
PUT
63M6AMDJ0zbmVpGjerVCkw==

Thu, 10 Jun 2021 11:21:48 GMT
x-amz-meta-example:metadata with trailing space
/pypicloud-dev/test_upload/test_upload
2021-06-10 14:21:48,981 - botocore.endpoint - DEBUG - Sending http request: <AWSPreparedRequest stream_output=False, method=PUT, url=http://127.0.0.1:22900/pypicloud-dev/test_upload/test_upload, headers={'Expect': b'100-continue', 'User-Agent': b'Boto3/1.16.63 Python/3.5.3 Linux/4.19.0-9-amd64 Botocore/1.19.63 Resource', 'Date': b'Thu, 10 Jun 2021 11:21:48 GMT', 'Authorization': b'AWS OXKY1EDONZ6M68K13HI7:OT0Lavc3rqXn8OnhrFSUn1gD2wM=', 'Content-MD5': b'63M6AMDJ0zbmVpGjerVCkw==', 'Content-Length': '9', 'x-amz-meta-example': b'metadata with trailing space '}>
2021-06-10 14:21:48,982 - urllib3.connectionpool - DEBUG - Starting new HTTP connection (1): 127.0.0.1:22900
2021-06-10 14:21:48,983 - botocore.awsrequest - DEBUG - Waiting for 100 Continue response.
2021-06-10 14:21:48,994 - botocore.awsrequest - DEBUG - Received a non 100 Continue response from the server, NOT sending request body.
2021-06-10 14:21:48,994 - urllib3.connectionpool - DEBUG - http://127.0.0.1:22900 "PUT /pypicloud-dev/test_upload/test_upload HTTP/1.1" 403 196
2021-06-10 14:21:48,994 - botocore.parsers - DEBUG - Response headers: {'Content-Type': 'application/xml', 'Content-Length': '196', 'Accept-Ranges': 'bytes', 'Date': 'Thu, 10 Jun 2021 11:21:48 GMT', 'x-amz-request-id': 'tx00000000000003c2fe5d1-0060c1f5cc-bd207-default'}
2021-06-10 14:21:48,995 - botocore.parsers - DEBUG - Response body:
b'<?xml version="1.0" encoding="UTF-8"?><Error><Code>SignatureDoesNotMatch</Code><RequestId>tx00000000000003c2fe5d1-0060c1f5cc-bd207-default</RequestId><HostId>bd207-default-default</HostId></Error>'
2021-06-10 14:21:48,996 - botocore.hooks - DEBUG - Event needs-retry.s3.PutObject: calling handler <botocore.retryhandler.RetryHandler object at 0x7f632160bfd0>
2021-06-10 14:21:48,997 - botocore.retryhandler - DEBUG - No retry needed.
2021-06-10 14:21:48,997 - botocore.hooks - DEBUG - Event needs-retry.s3.PutObject: calling handler <bound method S3RegionRedirector.redirect_from_error of <botocore.utils.S3RegionRedirector object at 0x7f632160b940>>
Traceback (most recent call last):
  File "./test-upload-stripped.py", line 31, in <module>
    key.put(Metadata=metadata, Body=data)
  File "/usr/local/lib/python3.5/dist-packages/boto3/resources/factory.py", line 520, in do_action
    response = action(self, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/boto3/resources/action.py", line 83, in __call__
    response = getattr(parent.meta.client, operation_name)(*args, **params)
  File "/usr/local/lib/python3.5/dist-packages/botocore/client.py", line 357, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/local/lib/python3.5/dist-packages/botocore/client.py", line 676, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (SignatureDoesNotMatch) when calling the PutObject operation: Unknown
@tiandrey tiandrey added the needs-triage This issue or PR still needs to be triaged. label Jun 10, 2021
@kdaily kdaily self-assigned this Jun 14, 2021
@kdaily kdaily added investigating This issue is being investigated and/or work is in progress to resolve the issue. and removed needs-triage This issue or PR still needs to be triaged. labels Jun 14, 2021
@kdaily
Copy link
Member

kdaily commented Jun 14, 2021

Hi @tiandrey,

Does this occur for you when using AWS S3 resources and not Ceph? I tried this using a standard S3 endpoint, and I did not receive this error:

2021-06-14 13:16:53,381 botocore.hooks [DEBUG] Changing event name from creating-client-class.iot-data to creating-client-class.iot-data-plane
2021-06-14 13:16:53,383 botocore.hooks [DEBUG] Changing event name from before-call.apigateway to before-call.api-gateway
2021-06-14 13:16:53,384 botocore.hooks [DEBUG] Changing event name from request-created.machinelearning.Predict to request-created.machine-learning.Predict
2021-06-14 13:16:53,385 botocore.hooks [DEBUG] Changing event name from before-parameter-build.autoscaling.CreateLaunchConfiguration to before-parameter-build.auto-scaling.CreateLaunchConfiguration
2021-06-14 13:16:53,385 botocore.hooks [DEBUG] Changing event name from before-parameter-build.route53 to before-parameter-build.route-53
2021-06-14 13:16:53,386 botocore.hooks [DEBUG] Changing event name from request-created.cloudsearchdomain.Search to request-created.cloudsearch-domain.Search
2021-06-14 13:16:53,386 botocore.hooks [DEBUG] Changing event name from docs.*.autoscaling.CreateLaunchConfiguration.complete-section to docs.*.auto-scaling.CreateLaunchConfiguration.complete-section
2021-06-14 13:16:53,389 botocore.hooks [DEBUG] Changing event name from before-parameter-build.logs.CreateExportTask to before-parameter-build.cloudwatch-logs.CreateExportTask
2021-06-14 13:16:53,389 botocore.hooks [DEBUG] Changing event name from docs.*.logs.CreateExportTask.complete-section to docs.*.cloudwatch-logs.CreateExportTask.complete-section
2021-06-14 13:16:53,389 botocore.hooks [DEBUG] Changing event name from before-parameter-build.cloudsearchdomain.Search to before-parameter-build.cloudsearch-domain.Search
2021-06-14 13:16:53,389 botocore.hooks [DEBUG] Changing event name from docs.*.cloudsearchdomain.Search.complete-section to docs.*.cloudsearch-domain.Search.complete-section
2021-06-14 13:16:53,409 botocore.loaders [DEBUG] Loading JSON file: /Users/kdaily/.pyenv/versions/3.8.5/envs/test-prefix/lib/python3.8/site-packages/boto3/data/s3/2006-03-01/resources-1.json
2021-06-14 13:16:53,412 botocore.utils [DEBUG] IMDS ENDPOINT: http://169.254.169.254/
2021-06-14 13:16:53,418 botocore.credentials [DEBUG] Looking for credentials via: env
2021-06-14 13:16:53,418 botocore.credentials [INFO] Found credentials in environment variables.
2021-06-14 13:16:53,419 botocore.loaders [DEBUG] Loading JSON file: /Users/kdaily/.pyenv/versions/3.8.5/envs/test-prefix/lib/python3.8/site-packages/botocore/data/endpoints.json
2021-06-14 13:16:53,425 botocore.hooks [DEBUG] Event choose-service-name: calling handler <function handle_service_name_alias at 0x10b2731f0>
2021-06-14 13:16:53,440 botocore.loaders [DEBUG] Loading JSON file: /Users/kdaily/.pyenv/versions/3.8.5/envs/test-prefix/lib/python3.8/site-packages/botocore/data/s3/2006-03-01/service-2.json
2021-06-14 13:16:53,447 botocore.hooks [DEBUG] Event creating-client-class.s3: calling handler <function add_generate_presigned_post at 0x10b21a040>
2021-06-14 13:16:53,447 botocore.hooks [DEBUG] Event creating-client-class.s3: calling handler <function lazy_call.<locals>._handler at 0x10b3540d0>
2021-06-14 13:16:53,468 botocore.hooks [DEBUG] Event creating-client-class.s3: calling handler <function add_generate_presigned_url at 0x10b219dc0>
2021-06-14 13:16:53,470 botocore.endpoint [DEBUG] Setting s3 timeout as (60, 60)
2021-06-14 13:16:53,471 botocore.loaders [DEBUG] Loading JSON file: /Users/kdaily/.pyenv/versions/3.8.5/envs/test-prefix/lib/python3.8/site-packages/botocore/data/_retry.json
2021-06-14 13:16:53,471 botocore.client [DEBUG] Registering retry handlers for service: s3
2021-06-14 13:16:53,472 boto3.resources.factory [DEBUG] Loading s3:s3
2021-06-14 13:16:53,473 boto3.resources.factory [DEBUG] Loading s3:Bucket
2021-06-14 13:16:53,473 boto3.resources.model [DEBUG] Renaming Bucket attribute name
2021-06-14 13:16:53,474 botocore.hooks [DEBUG] Event creating-resource-class.s3.Bucket: calling handler <function lazy_call.<locals>._handler at 0x10b314670>
2021-06-14 13:16:53,474 boto3.resources.factory [DEBUG] Loading s3:Object
2021-06-14 13:16:53,475 botocore.hooks [DEBUG] Event creating-resource-class.s3.Object: calling handler <function lazy_call.<locals>._handler at 0x10b360430>
2021-06-14 13:16:53,475 boto3.resources.action [DEBUG] Calling s3:put_object with {'Bucket': 'test-bucket', 'Key': 'test-prefix/test_upload', 'Metadata': {'example': 'metadata with trailing space '}, 'Body': b'test data'}
2021-06-14 13:16:53,475 botocore.hooks [DEBUG] Event before-parameter-build.s3.PutObject: calling handler <function validate_ascii_metadata at 0x10b2a03a0>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-parameter-build.s3.PutObject: calling handler <function sse_md5 at 0x10b298790>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-parameter-build.s3.PutObject: calling handler <function convert_body_to_file_like_object at 0x10b2a0ca0>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-parameter-build.s3.PutObject: calling handler <function validate_bucket_name at 0x10b298700>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-parameter-build.s3.PutObject: calling handler <bound method S3RegionRedirector.redirect_from_cache of <botocore.utils.S3RegionRedirector object at 0x10c9f8dc0>>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-parameter-build.s3.PutObject: calling handler <bound method S3ArnParamHandler.handle_arn of <botocore.utils.S3ArnParamHandler object at 0x10c9f8e80>>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-parameter-build.s3.PutObject: calling handler <function generate_idempotent_uuid at 0x10b298550>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-call.s3.PutObject: calling handler <function conditionally_calculate_md5 at 0x10b0f79d0>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-call.s3.PutObject: calling handler <function add_expect_header at 0x10b298a60>
2021-06-14 13:16:53,476 botocore.handlers [DEBUG] Adding expect 100 continue header to request.
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-call.s3.PutObject: calling handler <bound method S3RegionRedirector.set_request_url of <botocore.utils.S3RegionRedirector object at 0x10c9f8dc0>>
2021-06-14 13:16:53,476 botocore.hooks [DEBUG] Event before-call.s3.PutObject: calling handler <function inject_api_version_header_if_needed at 0x10b2a0dc0>
2021-06-14 13:16:53,476 botocore.endpoint [DEBUG] Making request for OperationModel(name=PutObject) with params: {'url_path': '/test-bucket/test-prefix/test_upload', 'query_string': {}, 'method': 'PUT', 'headers': {'x-amz-meta-example': 'metadata with trailing space ', 'User-Agent': 'Boto3/1.17.94 Python/3.8.5 Darwin/19.6.0 Botocore/1.20.94 Resource', 'Content-MD5': '63M6AMDJ0zbmVpGjerVCkw==', 'Expect': '100-continue'}, 'body': <_io.BytesIO object at 0x10ca55770>, 'url': 'https://s3.us-west-2.amazonaws.com/test-bucket/test-prefix/test_upload', 'context': {'client_region': 'us-west-2', 'client_config': <botocore.config.Config object at 0x10c876f40>, 'has_streaming_input': True, 'auth_type': None, 'signing': {'bucket': 'test-bucket'}}}
2021-06-14 13:16:53,477 botocore.hooks [DEBUG] Event request-created.s3.PutObject: calling handler <bound method RequestSigner.handler of <botocore.signers.RequestSigner object at 0x10c876bb0>>
2021-06-14 13:16:53,477 botocore.hooks [DEBUG] Event choose-signer.s3.PutObject: calling handler <function set_operation_specific_signer at 0x10b298430>
2021-06-14 13:16:53,477 botocore.hooks [DEBUG] Event before-sign.s3.PutObject: calling handler <bound method S3EndpointSetter.set_endpoint of <botocore.utils.S3EndpointSetter object at 0x10c9f8f10>>
2021-06-14 13:16:53,477 botocore.utils [DEBUG] Using S3 path style addressing.
2021-06-14 13:16:53,477 botocore.auth [DEBUG] Calculating signature using hmacv1 auth.
2021-06-14 13:16:53,477 botocore.auth [DEBUG] HTTP request method: PUT
2021-06-14 13:16:53,477 botocore.auth [DEBUG] StringToSign:
PUT
63M6AMDJ0zbmVpGjerVCkw==

Mon, 14 Jun 2021 20:16:53 GMT
x-amz-meta-example:metadata with trailing space
x-amz-security-token:REDACTED
/test-bucket/test-prefix/test_upload
2021-06-14 13:16:53,477 botocore.endpoint [DEBUG] Sending http request: <AWSPreparedRequest stream_output=False, method=PUT, url=https://s3.us-west-2.amazonaws.com/test-bucket/test-prefix/test_upload, headers={'x-amz-meta-example': b'metadata with trailing space ', 'User-Agent': b'Boto3/1.17.94 Python/3.8.5 Darwin/19.6.0 Botocore/1.20.94 Resource', 'Content-MD5': b'63M6AMDJ0zbmVpGjerVCkw==', 'Expect': b'100-continue', 'x-amz-security-token': b'REDACTED', 'Date': b'Mon, 14 Jun 2021 20:16:53 GMT', 'Authorization': b'AWS ASIA6DHCFT5H3ZHRPS2K:DNMSUFQdgvsoCff5kuFaHn2jZ64=', 'Content-Length': '9'}>
2021-06-14 13:16:53,478 botocore.httpsession [DEBUG] Certificate path: /Users/kdaily/.pyenv/versions/3.8.5/envs/test-prefix/lib/python3.8/site-packages/botocore/cacert.pem
2021-06-14 13:16:53,478 urllib3.connectionpool [DEBUG] Starting new HTTPS connection (1): s3.us-west-2.amazonaws.com:443
2021-06-14 13:16:54,093 botocore.awsrequest [DEBUG] Waiting for 100 Continue response.
2021-06-14 13:16:54,272 botocore.awsrequest [DEBUG] 100 Continue response seen, now sending request body.
2021-06-14 13:16:54,448 urllib3.connectionpool [DEBUG] https://s3.us-west-2.amazonaws.com:443 "PUT /test-bucket/test-prefix/test_upload HTTP/1.1" 200 0
2021-06-14 13:16:54,449 botocore.parsers [DEBUG] Response headers: {'x-amz-id-2': 'zOeJt/i3UuKg45vfv1znV1ftlsRIFKl/P4pL7RjxRZzJ7y8HaCeuNhATXWEj4YJNWt7nvBW9B9E=', 'x-amz-request-id': 'ZYSA2Z3K9E1JPQKH', 'Date': 'Mon, 14 Jun 2021 20:16:55 GMT', 'ETag': '"eb733a00c0c9d336e65691a37ab54293"', 'Content-Length': '0', 'Server': 'AmazonS3'}
2021-06-14 13:16:54,449 botocore.parsers [DEBUG] Response body:
b''
2021-06-14 13:16:54,450 botocore.hooks [DEBUG] Event needs-retry.s3.PutObject: calling handler <botocore.retryhandler.RetryHandler object at 0x10c9f8d60>
2021-06-14 13:16:54,450 botocore.retryhandler [DEBUG] No retry needed.
2021-06-14 13:16:54,450 botocore.hooks [DEBUG] Event needs-retry.s3.PutObject: calling handler <bound method S3RegionRedirector.redirect_from_error of <botocore.utils.S3RegionRedirector object at 0x10c9f8dc0>>
2021-06-14 13:16:54,450 boto3.resources.action [DEBUG] Response: {'ResponseMetadata': {'RequestId': 'ZYSA2Z3K9E1JPQKH', 'HostId': 'zOeJt/i3UuKg45vfv1znV1ftlsRIFKl/P4pL7RjxRZzJ7y8HaCeuNhATXWEj4YJNWt7nvBW9B9E=', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amz-id-2': 'zOeJt/i3UuKg45vfv1znV1ftlsRIFKl/P4pL7RjxRZzJ7y8HaCeuNhATXWEj4YJNWt7nvBW9B9E=', 'x-amz-request-id': 'ZYSA2Z3K9E1JPQKH', 'date': 'Mon, 14 Jun 2021 20:16:55 GMT', 'etag': '"eb733a00c0c9d336e65691a37ab54293"', 'content-length': '0', 'server': 'AmazonS3'}, 'RetryAttempts': 0}, 'ETag': '"eb733a00c0c9d336e65691a37ab54293"'}

@kdaily kdaily added bug This issue is a confirmed bug. response-requested Waiting on additional info and feedback. and removed investigating This issue is being investigated and/or work is in progress to resolve the issue. labels Jun 14, 2021
@tiandrey
Copy link
Author

I haven't tried Amazon S3 (in fact, I don't even have an account there), but I also tried local MinIO instance - and it works fine with strip() (as it is now) and doesn't work when I remove strip().
My assumption is that different S3-compatible servers process headers with trailing whitespace differently. In fact, HTTP/1.1 (RFC 2616) states "Such leading or trailing LWS (linear whitespace) MAY be removed without changing the semantics of the field value" (https://datatracker.ietf.org/doc/html/rfc2616#page-32). I think if you try to get metadata from Amazon S3, it won't have trailing space in it (I'll check Ceph and MinIO later).
So may be the best way to be compatible with all S3 implementations is to strip trailing (and leading) whitespace in headers prior to signing and sending it (e.g. not only use stripped headers for signing - send headers stripped too)?

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. label Jun 14, 2021
@RyanFitzSimmonsAK RyanFitzSimmonsAK added the p2 This is a standard priority issue label Nov 4, 2022
@tim-finnigan
Copy link
Contributor

Is this still an issue? There hasn't been any activity here in a while. There are quite a few things that could cause the SignatureDoesNotMatch error, and there are some other issues both here and in the boto3 repo that may be helpful to review.

@tim-finnigan tim-finnigan assigned tim-finnigan and unassigned kdaily Jul 8, 2024
@tim-finnigan tim-finnigan added the response-requested Waiting on additional info and feedback. label Jul 8, 2024
@tiandrey
Copy link
Author

If you didn't change specified (and related) pieces of code then it would still be an issue of course.
Sure, this (leading/trailing whitespace in metadata) must be a rare case, but still I think that your code is (or was, unfortunately I don't have time to re-check now) wrong to use modified header values (stripped of leading/tailing whitespace) for calculating signature while you send this signature alongside unmodified headers (with leading/trailing whitespace); and correct way to handle that case would be to use canonicalized (i.e. with leading/trailing whitespace removed) headers everywhere - both in signature calculation and sending to upstream.

@tim-finnigan
Copy link
Contributor

tim-finnigan commented Jul 10, 2024

I just tested this and it uploaded the file for me (on version 1.34.128):

import boto3
from botocore.config import Config

config_hash = {
    's3': {
        'addressing_style': 'path',
    },
    'signature_version': 'v4',
}

config = Config(**config_hash)
s3conn = boto3.resource('s3', config=config)
bucket = s3conn.Bucket('test-bucket')
data = b'test data'
key = bucket.Object('test_upload')

metadata = {
    'example': 'metadata with trailing space '
}

key.put(Metadata=metadata, Body=data)

Here is documentation on configuration values used: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html. As noted there, using s3 for signature_version is deprecated (Signature Version 2).

If you'd like to us to investigate this further please provide an updated snippet for reproducing the issue.

@tim-finnigan tim-finnigan added closing-soon and removed response-requested Waiting on additional info and feedback. labels Jul 10, 2024
@tiandrey
Copy link
Author

Yet still there are some S3-compatible servers that work better with 's3' signature.

@tiandrey
Copy link
Author

Snippet from the OP still gives the same error as before with s3 signature.

botocore.exceptions.ClientError: An error occurred (SignatureDoesNotMatch) when calling the PutObject operation: Unknown
---
boto3           1.34.142
botocore        1.34.142

@tiandrey
Copy link
Author

Update - I've fixed the problem in our installation exactly by switching to s3v4 signature in pypicloud 3 years ago, but reported that bug for those who cannot use newer signature version for various reasons.
Seeing as there are no more users suffering from the same issue (at least there are no more comments nor reactions for 3 years), I assume it's safe to close this issue with comment "we don't want to waste time fixing legacy code".
Bad engineering though :-(

@tiandrey tiandrey closed this as not planned Won't fix, can't repro, duplicate, stale Jul 10, 2024
Copy link

This issue is now closed. Comments on closed issues are hard for our team to see.
If you need more assistance, please open a new issue that references this one.

@nateprewitt
Copy link
Contributor

Hi @tiandrey, to be clear, for both SigV4 and SigV2 (the s3 option), both parties are required to strip white space and lower-case all headers during signing. It's generally frowned upon to modify user's input before sending it across the wire which we do not intend to change. The normalization for signing ensures we avoid any issues around header normalization from HTTP clients. From the sound of it, this is a bug in your S3-like service where it has an incorrect implementation of SigV2. You may consider raising an issue there.

@tiandrey
Copy link
Author

Hi @nateprewitt, thanks for the info, official documentation is not really clear about trimming trailing whitespace, but referenced RFC states that leading and trailing LWS may be removed without changing the semantics of field value, so it can be assumed that leading and trailing LWS is not a part of field value, so this issue is really caused by our S3 server software (namely ceph) not stripping headers before calculating checksum. I'll check if this is fixed in ceph upstream and open issue there if it's unfixed yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a confirmed bug. closing-soon p2 This is a standard priority issue s3
Projects
None yet
Development

No branches or pull requests

5 participants