chef / chef-server

Chef Infra Server is a hub for configuration data; storing cookbooks, node policies and metadata of managed nodes.
https://www.chef.io/chef/
Apache License 2.0
289 stars 210 forks source link

Evaluate erlcloud Erlang library for AWS S3 v4 request/url signing #1911

Closed lbakerchef closed 3 years ago

lbakerchef commented 4 years ago

See issue: https://github.com/chef/chef-server/issues/1783

Evaluation of erlcloud: https://github.com/erlcloud/erlcloud

As part of the upcoming AWS sigv4 update work to Chef Server, we should evaluate the suitability of Erlang libraries in order to lower costs of development and future maintenance. Evaluation criteria should include, but not necessarily be limited to: AWS v4 signing support, URL presigning support (if needed - see below), license compatibility, support for STS assumerole and instance profiles, ability to mock/modify time of the request, support for Erlang 22, availability of documentation, whether the library is actively maintained, ease of use, and performance and resource usage.

Question: Do we need presigning of URLs? It appears that we do. Here is a function in chef_s3.erl that suggests presigning is necessary:

%% @doc returns the S3 credentials for generating a presigned url
%% to send back to the requestor. This url will be used by the
%% requestor to  contact bookshelf or S3 directly, and as such,
%% the URL needs to be publicly accessible.
get_external_config(VHostUrl) ->
    aws_config(s3_external_url(VHostUrl)).

Answer: After conferring with Mark Anderson, the definitive answer is YES, we do need this capability, and it is a deal-killer if we cannot get it somehow. For one thing, the chef client API expects and uses a presigned URL. Bookshelf also apparently uses presigned URLs (possibly in interaction with the chef client). One option is to check any forks of erlcloud to find out if this capability has been added. A second option could be to work with erlcloud (motobob or kkuzmin) to get a PR merged (possibly #586) which addresses the issue. Yet another option could be to implement the sigv2/sigv4 paths in erchef, and leave bookshelf alone (but how does that get us away from sigv2?). See more about presigning below.

License: https://github.com/erlcloud/erlcloud/blob/master/COPYRIGHT Excerpt from legal dept: "The license is compliant with Chef licenses, but the terms of the grant require that the text of the file stay with the library. If you can do that, feel free to use."

STS assumerole and instance profiles: Supported.

Erlang versions: 21 is officially supported. Appears to compile and run under 22 in limited testing (see below). Further investigation is required.

Documentation: Little.

AWS v4 signing: Appears to do v4 signing transparently "under the hood" without need for overt construction of signing keys and other documents by the developer. sigv2/sigv4 usage was identified using the following methodology (see "Output of erlcloud Erlang session" below): 1) Enable S3 Server Access Logging on a bucket. Server Access Logging was enabled on bucket . 2) Choose an access log to examine. LOG-2020-02-06-21-19-31-A6698A7E9DDAE4ED was chosen for examination. 3) Look for the SignatureVersion element in the server access log, as per https://aws.amazon.com/blogs/aws/amazon-s3-update-sigv2-deprecation-period-extended-modified/ and https://docs.aws.amazon.com/AmazonS3/latest/dev/LogFormat.html . Forensic analysis concluded that SignatureVersion element SigV4 was identified in the access log. SignatureVersion element SigV2 was not identified in the access log. 4) Identify bucket region, as per https://aws.amazon.com/blogs/aws/amazon-s3-update-sigv2-deprecation-period-extended-modified/ . The aforementioned document indicates that the us-east-2 region makes exclusive use of SigV4 buckets. The bucket used for testing was confirmed to be located in the us-east-2 region.

URL presigning: No.

mini_s3 does sigv2 presigning, also referred to as "query string request" for enabling direct 3rd-party access to to s3 data without proxying the request (the other way to do this is via http request with the signature embedded in the headers). erlcloud apparently does not do this, although there are some issues and PRs which have been filed. Some options are: use another library, write our own sigv4 presigning, don't do sigv4 presigning and proxy requests instead, or continue to use sigv2 presigning, possibly as a stop-gap measure or delaying tactic until erlcloud adds the feature.

Issues and PRs regarding URL presigning: erlcloud/erlcloud#562 erlcloud/erlcloud#586 erlcloud/erlcloud#560 erlcloud/erlcloud#342

Mock/modify time of the request: Possibly through: https://docs.aws.amazon.com/general/latest/gr/sigv4-date-handling.html

(pre?)Signed request verification: Per Mark Anderson, "We need the capability to take a (pre?)signed request and verify that it is good, i.e. answer the question 'is it appropriately signed?' This capability is needed by bookshelf which currently does this with v2 requests." Possibly see erlcloud's make_get_url.

If I understand this correctly, we will need to somehow (re?)construct a presigned url to compare with the request url coming in. In theory, we'd just take our own presigned url construction function, and jam in arguments for method, bucketname, key, lifetime, headers, and config (access key and secret access key in particular). In practice, I'm not sure if we would have all of the necessary inputs to accomplish this. Further investigation is required.

Output of erlcloud Erlang session:

$ cd erlcloud
$ okta_aws --all
Fetching credentials for: chef-engineering
Assuming AWS role Okta_AdministratorAccess...
Temporary credentials stored in profile chef-engineering
Credentials expire in 12 hours
$ cat ~/.aws/credentials
[chef-engineering]
aws_access_key_id = censored
aws_secret_access_key = censored
aws_session_token = censored
$ ./rebar3 shell
===> Verifying dependencies...
===> Compiling erlcloud
Erlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:0] [hipe] [dtrace]

Eshell V10.6  (abort with ^G)
1> application:ensure_all_started(erlcloud).
{ok,[xmerl,jsx,eini,base16,lhttpc,erlcloud]}.
2> application:set_env(erlcloud, aws_access_key_id, "censored").
ok
3> application:set_env(erlcloud, aws_secret_access_key, "censored").
ok
4> application:set_env(erlcloud, aws_security_token, "censored").
ok
5> application:set_env(erlcloud, aws_region, "us-east-1").
6> io:fwrite("~p~n", [erlcloud_s3:put_object("my-bucket", "blah", "this is a test")]).
[{version_id,"null"},
 {"server","AmazonS3"},
 {"content-length","0"},
 {"etag","\"54b0c58c7ce9f2a8b551351102ee0938\""},
 {"date","Thu, 06 Feb 2020 17:03:44 GMT"},
 {"x-amz-request-id","censored"},
 {"x-amz-id-2", "censored"}]
ok
7> io:fwrite("~p~n", [erlcloud_s3:get_object("my-bucket", "blah")]).
[{last_modified,"Thu, 06 Feb 2020 17:03:44 GMT"},
 {etag,"\"54b0c58c7ce9f2a8b551351102ee0938\""},
 {content_length,"14"},
 {content_type,[]},
 {content_encoding,undefined},
 {delete_marker,false},
 {version_id,"null"},
 {tag_count,"null"},
 {content,<<"this is a test">>}]
ok
8> io:fwrite("~p~n", [erlcloud_s3:list_objects("my-bucket")]).
[{name,"my-bucket"},
 {prefix,[]},
 {marker,[]},
 {next_marker,[]},
 {delimiter,[]},
 {max_keys,1000},
 {is_truncated,false},
 {common_prefixes,[]},
 {contents,[[{key,"LOG-2020-02-06-21-19-31-A6698A7E9DDAE4ED"},
             {last_modified,{{2020,2,6},{21,19,32}}},
             {etag,"\"c994c50d1f3f5bb15060029ee253d0fe\""},
             {size,621},
             {storage_class,"STANDARD"},
             {owner,[{id,"3272ee65a908a7677109fedda345db8d9554ba26398b2ca10581de88777e2b61"},
                     {display_name,"s3-log-service"}]}],
 <SNIP>
ok
9> io:fwrite("~p~n", [erlcloud_s3:get_object("my-bucket", "LOG-2020-02-06-21-19-31-A6698A7E9DDAE4ED")]).
[{last_modified,"Thu, 06 Feb 2020 21:19:32 GMT"},
 {etag,"\"c994c50d1f3f5bb15060029ee253d0fe\""},
 {content_length,"621"},
 {content_type,"text/plain"},
 {content_encoding,undefined},
 {delete_marker,false},
 {version_id,"null"},
 {tag_count,"null"},
 {content,<<"9f7e6edbfa57bfdacd815809cf294dca9b4d83fd36f214b360783ad344250fd7 my-bucket [06/Feb/2020:20:45:55 +0000] 68.46.227.248 arn:aws:sts::112758395563:assumed-role/Okta_AdministratorAccess/censored 306C943EAA1D0402 REST.GET.LIFECYCLE - \"GET /my-bucket?lifecycle= HTTP/1.1\" 404 - 291 - 26 25 \"-\" \"S3Console/0.4, aws-internal/3 aws-sdk-java/1.11.666 Linux/4.9.184-0.1.ac.235.83.329.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.232-b09 java/1.8.0_232 vendor/Oracle_Corporation\" - c3ljou/kxFz9oLvwTpRxYhpxm2qIdV1cvg9MEcGS0eQelFmsyrF1rBYzxuP1RheEKNUY/EIA7bI= SigV4 ECDHE-RSA-AES128-SHA AuthHeader s3.amazonaws.com TLSv1.2\n">>}]
ok

Study: Integration with Chef Server:

A study was conducted to integrate erlcloud with the oc_erchef component of Chef Server. The basic approach selected was to turn off bookshelf, and replace the innards of mini_s3 with calls to erlcloud functions. This 'mini_s3 as an erlcloud wrapper' approach was selected for suitability to 'proof of concept' purposes and expedience, as all routing to mini_s3 could easily be captured and rerouted to ercloud (note that any final approach may differ).

current_mini_s3_erlcloud_conversion

The erlcloud dependency was wired-in via edits to rebar.config files, and a branch was tested via the buildkite verify and omnibus-adhoc pipelines. Failures were observed in bookshelf before disabling it. This was expected, as bookshelf will need to be patched for sigv4. Additionally, oc_erchef was observed attempting to connect to S3 but failing due to credentials issues. The credentials issue was tackled in a separate study outlined further down. A sample credentials failure is recorded below:

Failures:

  1) Cookbook Artifacts API endpoint API v0 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
     Failure/Error: raise "bad response code #{response.code} in response: #{response}"

     RuntimeError:
       bad response code 403 in response: <?xml version="1.0" encoding="UTF-8"?>
       <Error><Code>InvalidAccessKeyId</Code><Message>The AWS Access Key Id you provided does not exist in our records.</Message><AWSAccessKeyId>ASIARUQHMSKVZYHVTSBS</AWSAccessKeyId><RequestId>A5DA56400F3647D4</RequestId><HostId>WFMdktKad9a3raDJPcBYOQAJU14hTUnwPb79ZuiKjtvwcFVgKs6r+0TFHlrtSLEIkrYe4Z+MVGg=</HostId></Error>
     Shared Example Group: "deletes cookbook artifacts" called from ./spec/api/cookbook_artifacts/delete_spec.rb:196
     # ./lib/pedant/rspec/common.rb:429:in `ensure_2xx'
     # ./lib/pedant/rspec/cookbook_util.rb:57:in `upload_to_sandbox'
     # ./lib/pedant/rspec/cookbook_util.rb:73:in `block in upload_files_to_sandbox'
     # ./lib/pedant/rspec/cookbook_util.rb:73:in `each'
     # ./lib/pedant/rspec/cookbook_util.rb:73:in `upload_files_to_sandbox'
     # ./lib/pedant/rspec/cookbook_util.rb:275:in `make_cookbook_artifact_with_recipes'
     # ./spec/api/cookbook_artifacts/delete_spec.rb:109:in `block (6 levels) in <top (required)>'

Impedance Mismatches

Issues were encountered during the study. One example, the aws config record used by the mini_s3 and erlcloud libraries differs in name, numbers, and types of elements. Furthermore, erlcloud expects the s3 url of its aws_config record to be broken into s3_host and s3_port elements or fields, while mini_s3 places both the host and port data into a single url packed into the s3_host field. This particular issue was worked around by deploying a hack, but will need to be revisited in any future erlcloud work. Further anomalies and inconsistencies between the libraries are listed below, as well as additional concerns:

Study: Credentials Problems and Resolution

There are several ways of getting aws credentials into erlcloud. The most obvious and expedient way was to generate credentials using okta_aws and insert those credentials into environment variables to be picked up by erlcloud. However, this method also generates a token in addition to an access key and a secret access key, and was found to be incompatible with the 'access key' method of providing credentials to erlcloud which uses longer-lived (non-temporary) credentials and does not use a token:

$ okta_aws --all
Fetching credentials for: chef-engineering
Assuming AWS role Okta_AdministratorAccess...
Temporary credentials stored in profile chef-engineering
Credentials expire in 12 hours
$ cat .aws/credentials
[chef-engineering]
aws_access_key_id = censored
aws_secret_access_key = censored
aws_session_token = censored
$ vim .bash_profile
$ source .bash_profile
$ env | grep AWS
AWS_DEPT=Eng
AWS_SSH_KEY_ID=censored
AWS_PROFILE=chef-engineering
AWS_DEFAULT_REGION=us-east-1
AWS_DEFAULT_PROFILE=censored
AWS_SECRET_ACCESS_KEY=censored
AWS_ACCESS_KEY_ID=censored
AWS_CONTACT=censored
$ ./rebar3 shell
===> Verifying dependencies...
===> Compiling mini_s3
Erlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]

Eshell V10.6  (abort with ^G)
1> application:ensure_all_started(erlcloud).
{ok,[xmerl,jsx,eini,base16,lhttpc,erlcloud]}
2>
2> erlcloud_s3:list_buckets().
** exception error: {aws_error,{http_error,403,"Forbidden",
                                           <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>InvalidAccessKeyId</Code><Message>The AW"...>>}}
     in function  erlcloud_s3:s3_request/8 (/Users/censored/mini_s3/_build/default/lib/erlcloud/src/erlcloud_s3.erl, line 1814)
     in call from erlcloud_s3:s3_xml_request/8 (/Users/censored/mini_s3/_build/default/lib/erlcloud/src/erlcloud_s3.erl, line 1798)
     in call from erlcloud_s3:list_buckets/1 (/Users/censored/mini_s3/_build/default/lib/erlcloud/src/erlcloud_s3.erl, line 429)

After further research, a different way of generating credentials was discovered which involved creating a new IAMS user from within the Amazons AWS console and giving it the correct permissions. This resulted in an an access key and secret access key and no token. Placing those values into the environment and firing up an Erlang shell yielded the following:

$ # these can also be placed in /etc/environment on the dev vm
$ export AWS_ACCESS_KEY_ID=censored
$ export AWS_SECRET_ACCESS_KEY=censored
$ ./rebar3 shell
===> Verifying dependencies...
===> Compiling mini_s3
Erlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]

Eshell V10.6  (abort with ^G)
1> application:ensure_all_started(erlcloud).
{ok,[xmerl,jsx,eini,base16,lhttpc,erlcloud]}
2> erlcloud_s3:list_buckets().
[{owner,[{id,"9f7e6edbfa57bfdacd815809cf294dca9b4d83fd36f214b360783ad344250fd7"},
         {display_name,"awsops+engineering"}]},
 {buckets,[[{name,"a2-bucket"},
            {creation_date,{{2018,10,18},{19,16,9}}}],
           [{name,"a2-bucket-priv"},
            {creation_date,{{2019,2,6},{0,31,21}}}],
           [{name,"apop-bucket"},
<SNIP>

Study: sigv4 Presigned URLs

An evaluation was conducted of a PR https://github.com/erlcloud/erlcloud/pull/586 which added sigv4 presigned URL capability to erlcloud. Presigned URLs were generated "by hand" in an Erlang shell for an HTTP PUT and a GET against the same pre-existing S3 bucket located in an exclusively sigv4 region (us-east-2). After obtaining the presigned URLs, curl was used to execute the PUT and GET:

$ ./rebar3 shell
===> Verifying dependencies...
===> Compiling mini_s3
Erlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]

Eshell V10.6  (abort with ^G)
1> Config = mini_s3:new("censored", "censored", "s3.us-east-2.amazonaws.com").
{aws_config,"autoscaling.amazonaws.com","ec2.amazonaws.com",
            "iam.amazonaws.com","sts.amazonaws.com","https://",
            "s3.us-east-2.amazonaws.com",443,false,2,vhost,false,
            "sdb.amazonaws.com","elasticloadbalancing.amazonaws.com",
            "rds.us-east-1.amazonaws.com",
            "email.us-east-1.amazonaws.com","queue.amazonaws.com",
            undefined,undefined,"https://",
            "elasticmapreduce.us-east-1.amazonaws.com",undefined,
            "https://","sns.amazonaws.com",undefined,
            "mechanicalturk.amazonaws.com","monitoring.amazonaws.com",
            undefined,undefined,...}
2> erlcloud_s3:make_presigned_v4_url(604800, "my-bucket", put, "yadda/test", [], Config).
"https://my-bucket.s3.us-east-2.amazonaws.com:443/yadda/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200311%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200311T185304Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=479a18348697ff9b952c0032096f04037070b285aba239ce0664918070c8d9d4"
3> erlcloud_s3:make_presigned_v4_url(604800, "my-bucket", get, "yadda/test", [], Config).
"https://my-bucket.s3.us-east-2.amazonaws.com:443/yadda/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200311%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200311T172703Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=947ed4bcb3e14fa0554f30b8562cf091037a9f60011bbae85c081d441f979a53"
4> halt().
$ curl -v -X PUT -d "hi=ho&lets=go" "https://my-bucket.s3.us-east-2.amazonaws.com:443/yadda/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200311%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200311T185304Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=479a18348697ff9b952c0032096f04037070b285aba239ce0664918070c8d9d4"
*   Trying 52.219.100.242:443...
* TCP_NODELAY set
* Connected to my-bucket.s3.us-east-2.amazonaws.com (52.219.100.242) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /opt/chef-workstation/embedded/ssl/certs/cacert.pem
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
<SNIP>
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=Washington; L=Seattle; O=Amazon.com, Inc.; CN=*.s3.us-east-2.amazonaws.com
*  start date: Jan 31 00:00:00 2020 GMT
*  expire date: May 27 12:00:00 2021 GMT
*  subjectAltName: host "my-bucket.s3.us-east-2.amazonaws.com" matched cert's "*.s3.us-east-2.amazonaws.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert Baltimore CA-2 G2
*  SSL certificate verify ok.
> PUT /yadda/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200311%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200311T185304Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=479a18348697ff9b952c0032096f04037070b285aba239ce0664918070c8d9d4 HTTP/1.1
> Host: my-bucket.s3.us-east-2.amazonaws.com
> User-Agent: curl/7.65.1
> Accept: */*
> Content-Length: 13
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 13 out of 13 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< x-amz-id-2: yag4bj3J+VVZlnC/8bnap1Nglim/FWUReF7s+HVpLBomIdRg/tKS83h9PkCG+ZYbEXuzPVcoZMU=
< x-amz-request-id: E460ECE6493FF00C
< Date: Wed, 11 Mar 2020 19:40:22 GMT
< ETag: "2f6b98158df6006980a38c6d1f465fb9"
< Content-Length: 0
< Server: AmazonS3
<
* Connection #0 to host my-bucket.s3.us-east-2.amazonaws.com left intact
$  curl -v "https://my-bucket.s3.us-east-2.amazonaws.com:443/yadda/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200311%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200311T172703Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=947ed4bcb3e14fa0554f30b8562cf091037a9f60011bbae85c081d441f979a53"
*   Trying 52.219.80.32:443...
* TCP_NODELAY set
* Connected to my-bucket.s3.us-east-2.amazonaws.com (52.219.80.32) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /opt/chef-workstation/embedded/ssl/certs/cacert.pem
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
<SNIP>
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=Washington; L=Seattle; O=Amazon.com, Inc.; CN=*.s3.us-east-2.amazonaws.com
*  start date: Jan 31 00:00:00 2020 GMT
*  expire date: May 27 12:00:00 2021 GMT
*  subjectAltName: host "my-bucket.s3.us-east-2.amazonaws.com" matched cert's "*.s3.us-east-2.amazonaws.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert Baltimore CA-2 G2
*  SSL certificate verify ok.
> GET /yadda/test?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200311%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200311T172703Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=947ed4bcb3e14fa0554f30b8562cf091037a9f60011bbae85c081d441f979a53 HTTP/1.1
> Host: my-bucket.s3.us-east-2.amazonaws.com
> User-Agent: curl/7.65.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< x-amz-id-2: 6DbuEMt7u8/DKeC6ef4P5DYtHAI2dGZtU5aBeUSqJEjCNcDFqH7bqS7a50DCQfp1/QWr9v02BuY=
< x-amz-request-id: 97BA76511C378507
< Date: Wed, 11 Mar 2020 19:43:03 GMT
< Last-Modified: Wed, 11 Mar 2020 19:40:22 GMT
< ETag: "2f6b98158df6006980a38c6d1f465fb9"
< Accept-Ranges: bytes
< Content-Type: application/x-www-form-urlencoded
< Content-Length: 13
< Server: AmazonS3
<
* Connection #0 to host my-bucket.s3.us-east-2.amazonaws.com left intact
hi=ho&lets=go

Study: Presigned URLs with Headers

Testing of chef-server with the newly-added sigv4 presigned URL capability revealed the need for the ability to incorporate HTTP headers when presigned URLs are being generated. Neither erlcloud nor the presigned URL PR https://github.com/erlcloud/erlcloud/pull/586 possessed this capability, therefore a proof-of-concept had to be coded up in-house. Afterwards, presigned URLs with two arbitrary headers incorporated were generated "by hand" in an Erlang shell for an HTTP PUT against a pre-existing S3 bucket located in an exclusively sigv4 region (us-east-2). After obtaining the presigned URLs, curl was used to execute the PUT. The PUT was successful:

$ ./rebar3 shell
===> Verifying dependencies...
===> Compiling mini_s3
Erlang/OTP 22 [erts-10.6] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]

Eshell V10.6  (abort with ^G)
1> Config = mini_s3:new("censored", "censored", "s3.us-east-2.amazonaws.com").
{aws_config,"autoscaling.amazonaws.com","ec2.amazonaws.com",
            "iam.amazonaws.com","sts.amazonaws.com","https://",
            "s3.us-east-2.amazonaws.com",443,false,2,vhost,false,
            "sdb.amazonaws.com","elasticloadbalancing.amazonaws.com",
            "rds.us-east-1.amazonaws.com",
            "email.us-east-1.amazonaws.com","queue.amazonaws.com",
            undefined,undefined,"https://",
            "elasticmapreduce.us-east-1.amazonaws.com",undefined,
            "https://","sns.amazonaws.com",undefined,
            "mechanicalturk.amazonaws.com","monitoring.amazonaws.com",
            undefined,undefined,...}
2> erlcloud_s3:make_presigned_v4_url(604800, "my-bucket", put, "yadda/abc", [], [{"abc","123"}, {"xyz","456"}], Config).
"https://my-bucket.s3.us-east-2.amazonaws.com/yadda/abc?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200320%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200320T221918Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=abc%3Bhost%3Bxyz&X-Amz-Signature=e579e7849d5e2e02c55da88c3269e3b50974d435c6ada3e0ad247e413017028c"
3> halt().
$ curl -H "abc: 123" -H "xyz: 456" -X PUT -d "hey=ho" "https://my-bucket.s3.us-east-2.amazonaws.com/yadda/abc?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200320%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200320T221918Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=abc%3Bhost%3Bxyz&X-Amz-Signature=e579e7849d5e2e02c55da88c3269e3b50974d435c6ada3e0ad247e413017028c"
$

Study: Content-MD5 Header

Problems were encountered during generation of the Content-MD5 header (S3 would not accept it), and the correct way of generating the header was undocumented. Alas, discovery requires experimentation:

$ echo -n "hey=ho" |  openssl dgst -md5 -binary | openssl enc -base64
raL9JL16a5alGkpeDCpYOA==
$ curl -v -H "Content-MD5: raL9JL16a5alGkpeDCpYOA==" -X PUT -d "hey=ho" "https://my-bucket.s3.us-east-2.amazonaws.com/yadda/abc?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200321%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200321T025344Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=content-md5%3Bhost&X-Amz-Signature=26c42653ebdaa53b5bf2fc7c10bacdebda83de2f18bbc12170bf831ab94bc19d"
*   Trying 52.219.100.152:443...
* TCP_NODELAY set
* Connected to my-bucket.s3.us-east-2.amazonaws.com (52.219.100.152) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /opt/chef-workstation/embedded/ssl/certs/cacert.pem
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=Washington; L=Seattle; O=Amazon.com, Inc.; CN=*.s3.us-east-2.amazonaws.com
*  start date: Jan 31 00:00:00 2020 GMT
*  expire date: May 27 12:00:00 2021 GMT
*  subjectAltName: host "my-bucket.s3.us-east-2.amazonaws.com" matched cert's "*.s3.us-east-2.amazonaws.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert Baltimore CA-2 G2
*  SSL certificate verify ok.
> PUT /yadda/abc?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200321%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200321T025344Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=content-md5%3Bhost&X-Amz-Signature=26c42653ebdaa53b5bf2fc7c10bacdebda83de2f18bbc12170bf831ab94bc19d HTTP/1.1
> Host: my-bucket.s3.us-east-2.amazonaws.com
> User-Agent: curl/7.65.1
> Accept: */*
> Content-MD5: raL9JL16a5alGkpeDCpYOA==
> Content-Length: 6
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 6 out of 6 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< x-amz-id-2: CqUCHXAvq9VzmYK7VKhSfZ5YP00Vl8o9vLg+laSpF3yeHIJj/oZqbuBRl4jA7ZnvReo7bsfIm5w=
< x-amz-request-id: 9C80F9A12CEF8170
< Date: Sat, 21 Mar 2020 02:55:14 GMT
< ETag: "ada2fd24bd7a6b96a51a4a5e0c2a5838"
< Content-Length: 0
< Server: AmazonS3
<
* Connection #0 to host my-bucket.s3.us-east-2.amazonaws.com left intact

Host Header Problem

During pedant tests (specifically --focus-knife) several errors were received from S3 of the form: "SignatureDoesNotMatch - The request signature we calculated does not match the signature you provided. Check your key and signing method." The code was extensively instrumented on both the client and server sides. Forensic analysis of the diagnostic output revealed that knife cookbook upload was sometimes including a host header when hitting the presigned URL endpoint that was different than the host header which was used during URL presigning. Specifically, it was adding including a port number on the end of the host string on the host header (e.g. "Host: whatever.com:port"). The reason this is occurring is currently unknown.

In a nutshell, the presigned URL never appears to be created with a host header that includes a port number (at least with the observed failures); knife sometimes hits the presigned URL endpoint with a host header that includes a port number. I have also tried it the 'other way' i.e. forced the creation of presigned URLs with host headers that have port numbers, but then we get pedant test failures for the instances when a chef client hits the presigned URLs and doesn't include a port number on the host header.

Several solutions to the problem were proposed and rejected because any fix must be backwards-compatible with the current version of hosted chef; however, to illustrate the problem, a build was created exhibiting the default behavior of presigned URLs being constructed with host headers without port numbers, and a single line of ruby code was patched into line 148 of /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-15.8.23/lib/chef/http.rb on a running dev VM. This line of code simply removes any colon and port number from the end of the host header string, and thus appears to remove the error. Other files which were examined include upload_spec.rb, delete_spec.rb, cookbook_uploader.rb, cookbook_util.rb, knife_util.rb, sandbox.rb and basic_client.rb.

A similar study was conducted which forced the creation of presigned URLs with port numbers in the host headers. A baseline run of chef-server-ctl test --all resulted in 138 pedant test failures. Then an attempt was made to patch http.rb as above, except adding a port number to the host header instead of removing. The patch had no effect, suggesting that a different client was being used by pedant for these failures. After some investigation a patch was made to the authenticated_request method of /opt/opscode/embedded/service/oc-chef-pedant/lib/pedant/request.rb which succeeded in bring the number of pedant test errors down from 138 to 16. It was noted that with the 16 remaining errors, at least in some cases the port is still not being added to the host header, suggesting that another file might need to be patched.

This issue has yet to be resolved, and more investigation is required to fully understand the issue and resolution.

Excerpt from chef-server-ctl test --focus-knife: (presigned URLs created without port on host header)

Failures:

  1) knife cookbook upload as an admin should succeed
     Failure/Error: should have_outcome :status => 0, :stderr => /Uploaded 1 cookbook/

       Executed command should have matched the outcome spec {:status=>0, :stderr=>/Uploaded 1 cookbook/}, but it didn't!

        Failed Command: knife cookbook upload joy_of_cooking -c /tmp/oc-chef-pedant/dot-chef-20200610-4447-16vq9l3/knife.rb

        Command Setting: {:cwd=>"/opt/opscode/embedded/service/oc-chef-pedant/fixtures/test_repository", :user=>nil, :group=>nil, :umask=>nil, :timeout=>600, :valid_exit_codes=>[0], :environment=>{"BUNDLE_GEMFILE"=>nil, "BUNDLE_BIN_PATH"=>nil, "GEM_PATH"=>nil, "GEM_HOME"=>nil, "RUBYOPT"=>nil}}

        Exit Status: 100

        Standard Output:

        Standard Error:

       Uploading joy_of_cooking [0.0.1]
       ERROR: Failed to upload /tmp/tmp_working_dir_path20200610-4463-1kn4nnz/joy_of_cooking/recipes/default.rb (200bc1c0f816f3702c0046f6b4964280) to https://s3.us-east-2.amazonaws.com:443/lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-200bc1c0f816f3702c0046f6b4964280?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200610T232147Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=0424bf1e8606bc3c26b60f99ff765cab17123b8df39c3f5cf640465334fc7cc9 : 403 "Forbidden"
              <?xml version="1.0" encoding="UTF-8"?>
              <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
              20200610T232147Z
              20200610/us-east-2/s3/aws4_request
              714d400438bcc9d97816431027b89a369469e4738d4d45161fb4b048c4c84364</StringToSign><SignatureProvided>0424bf1e8606bc3c26b60f99ff765cab17123b8df39c3f5cf640465334fc7cc9</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 37 31 34 64 34 30 30 34 33 38 62 63 63 39 64 39 37 38 31 36 34 33 31 30 32 37 62 38 39 61 33 36 39 34 36 39 65 34 37 33 38 64 34 64 34 35 31 36 31 66 62 34 62 30 34 38 63 34 63 38 34 33 36 34</StringToSignBytes><CanonicalRequest>PUT
              /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-200bc1c0f816f3702c0046f6b4964280
              X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232147Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
              content-md5:IAvBwPgW83AsAEb2tJZCgA==
              content-type:application/x-binary
              host:s3.us-east-2.amazonaws.com:443

              content-md5;content-type;host
              UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 32 30 30 62 63 31 63 30 66 38 31 36 66 33 37 30 32 63 30 30 34 36 66 36 62 34 39 36 34 32 38 30 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 49 41 76 42 77 50 67 57 38 33 41 73 41 45 62 32 74 4a 5a 43 67 41 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>F008185C8BE93775</RequestId><HostId>V4AtKFCJzCTXEB0Tx1QoMhM0PZboFC5vvwlm5NAzH1NfiN9l7V8jT4ftov6VEBwJxakJzO87Gb0=</HostId></Error>
       #<Thread:0x0000000001ef4768@/opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:49 run> terminated with exception (report_on_exception is true):
       /opt/opscode/embedded/lib/ruby/2.6.0/net/http/response.rb:122:in `error!': 403 "Forbidden" (Net::HTTPServerException)
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:152:in `request'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:123:in `put'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/cookbook_uploader.rb:129:in `block in uploader_function_for'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:52:in `block (3 levels) in process'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `loop'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `block (2 levels) in process'
       ERROR: Failed to upload /tmp/tmp_working_dir_path20200610-4463-1kn4nnz/joy_of_cooking/metadata.json (2982fc9d3cde771bebf9bc9e242ee78c) to https://s3.us-east-2.amazonaws.com:443/lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-2982fc9d3cde771bebf9bc9e242ee78c?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200610T232147Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=4eaca1d28b4d16f16200d3f92c0d45339f1cf9aad46737fbe668b050611061c3 : 403 "Forbidden"
              <?xml version="1.0" encoding="UTF-8"?>
              <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
              20200610T232147Z
              20200610/us-east-2/s3/aws4_request
              30971dc8918b2fa953247d3c7b2e29ca294cce2fba658f570d6cbfd530998f69</StringToSign><SignatureProvided>4eaca1d28b4d16f16200d3f92c0d45339f1cf9aad46737fbe668b050611061c3</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 33 30 39 37 31 64 63 38 39 31 38 62 32 66 61 39 35 33 32 34 37 64 33 63 37 62 32 65 32 39 63 61 32 39 34 63 63 65 32 66 62 61 36 35 38 66 35 37 30 64 36 63 62 66 64 35 33 30 39 39 38 66 36 39</StringToSignBytes><CanonicalRequest>PUT
              /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-2982fc9d3cde771bebf9bc9e242ee78c
              X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232147Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
              content-md5:KYL8nTzedxvr+byeJC7njA==
              content-type:application/x-binary
              host:s3.us-east-2.amazonaws.com:443

              content-md5;content-type;host
              UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 32 39 38 32 66 63 39 64 33 63 64 65 37 37 31 62 65 62 66 39 62 63 39 65 32 34 32 65 65 37 38 63 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 4b 59 4c 38 6e 54 7a 65 64 78 76 72 2b 62 79 65 4a 43 37 6e 6a 41 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>5414BE78C94F152F</RequestId><HostId>Gr4O3S/rgrznnAeJBd1BfkxbeTA5IZT8KWtuf/XJ54roZ8yFAmYWbsLUvbwZllLWojJP7XHOIcQ=</HostId></Error>
       #<Thread:0x0000000001ef44e8@/opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:49 run> terminated with exception (report_on_exception is true):
       /opt/opscode/embedded/lib/ruby/2.6.0/net/http/response.rb:122:in `error!': 403 "Forbidden" (Net::HTTPServerException)
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:152:in `request'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:123:in `put'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/cookbook_uploader.rb:129:in `block in uploader_function_for'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:52:in `block (3 levels) in process'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `loop'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `block (2 levels) in process'
       ERROR: Failed to upload /tmp/tmp_working_dir_path20200610-4463-1kn4nnz/joy_of_cooking/metadata.rb (f2ee2a0acc249dc40112aaa7a4c6df6c) to https://s3.us-east-2.amazonaws.com:443/lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-f2ee2a0acc249dc40112aaa7a4c6df6c?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200610T232147Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=7f05309a29a068a114357f459cdb9dffa45da22215c49a86de8679afa913297b : 403 "Forbidden"
              <?xml version="1.0" encoding="UTF-8"?>
              <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
              20200610T232147Z
              20200610/us-east-2/s3/aws4_request
              397cdfb6f71417bc8e762a659ecb97c9def3a8bce2bc66e802dd47cec34f6771</StringToSign><SignatureProvided>7f05309a29a068a114357f459cdb9dffa45da22215c49a86de8679afa913297b</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 33 39 37 63 64 66 62 36 66 37 31 34 31 37 62 63 38 65 37 36 32 61 36 35 39 65 63 62 39 37 63 39 64 65 66 33 61 38 62 63 65 32 62 63 36 36 65 38 30 32 64 64 34 37 63 65 63 33 34 66 36 37 37 31</StringToSignBytes><CanonicalRequest>PUT
              /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-f2ee2a0acc249dc40112aaa7a4c6df6c
              X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232147Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
              content-md5:8u4qCswkncQBEqqnpMbfbA==
              content-type:application/x-binary
              host:s3.us-east-2.amazonaws.com:443

              content-md5;content-type;host
              UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 66 32 65 65 32 61 30 61 63 63 32 34 39 64 63 34 30 31 31 32 61 61 61 37 61 34 63 36 64 66 36 63 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 38 75 34 71 43 73 77 6b 6e 63 51 42 45 71 71 6e 70 4d 62 66 62 41 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>783E11EDCA22255F</RequestId><HostId>kaChcD4rbxbP2A4RCO7w9TzsV052QaJq4mDMBDuWCAgr5ptsTuXD+pvMn3LXumjRx8KV+YUkXUU=</HostId></Error>
       #<Thread:0x0000000001ef48a8@/opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:49 run> terminated with exception (report_on_exception is true):
       /opt/opscode/embedded/lib/ruby/2.6.0/net/http/response.rb:122:in `error!': 403 "Forbidden" (Net::HTTPServerException)
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:152:in `request'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:123:in `put'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/cookbook_uploader.rb:129:in `block in uploader_function_for'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:52:in `block (3 levels) in process'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `loop'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `block (2 levels) in process'
       ERROR: You authenticated successfully to https://api.chef-server.dev/organizations/pedant_testorg_api_4447/ as pedant_admin_user_api_4447 but you are not authorized for this action.
       Response:  <?xml version="1.0" encoding="UTF-8"?>
                 <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
                 20200610T232147Z
                 20200610/us-east-2/s3/aws4_request
                 397cdfb6f71417bc8e762a659ecb97c9def3a8bce2bc66e802dd47cec34f6771</StringToSign><SignatureProvided>7f05309a29a068a114357f459cdb9dffa45da22215c49a86de8679afa913297b</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 33 39 37 63 64 66 62 36 66 37 31 34 31 37 62 63 38 65 37 36 32 61 36 35 39 65 63 62 39 37 63 39 64 65 66 33 61 38 62 63 65 32 62 63 36 36 65 38 30 32 64 64 34 37 63 65 63 33 34 66 36 37 37 31</StringToSignBytes><CanonicalRequest>PUT
                 /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-f2ee2a0acc249dc40112aaa7a4c6df6c
                 X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232147Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
                 content-md5:8u4qCswkncQBEqqnpMbfbA==
                 content-type:application/x-binary
                 host:s3.us-east-2.amazonaws.com:443

                 content-md5;content-type;host
                 UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 66 32 65 65 32 61 30 61 63 63 32 34 39 64 63 34 30 31 31 32 61 61 61 37 61 34 63 36 64 66 36 63 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 37 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 38 75 34 71 43 73 77 6b 6e 63 51 42 45 71 71 6e 70 4d 62 66 62 41 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>783E11EDCA22255F</RequestId><HostId>kaChcD4rbxbP2A4RCO7w9TzsV052QaJq4mDMBDuWCAgr5ptsTuXD+pvMn3LXumjRx8KV+YUkXUU=</HostId></Error>
     # ./spec/api/knife/cookbook/upload_spec.rb:34:in `block (5 levels) in <top (required)>'

  2) knife cookbook upload as a normal user should succeed
     Failure/Error: should have_outcome :status => 0, :stderr => /Uploaded 1 cookbook/

       Executed command should have matched the outcome spec {:status=>0, :stderr=>/Uploaded 1 cookbook/}, but it didn't!

        Failed Command: knife cookbook upload joy_of_cooking -c /tmp/oc-chef-pedant/dot-chef-20200610-4447-88cyj5/knife.rb

        Command Setting: {:cwd=>"/opt/opscode/embedded/service/oc-chef-pedant/fixtures/test_repository", :user=>nil, :group=>nil, :umask=>nil, :timeout=>600, :valid_exit_codes=>[0], :environment=>{"BUNDLE_GEMFILE"=>nil, "BUNDLE_BIN_PATH"=>nil, "GEM_PATH"=>nil, "GEM_HOME"=>nil, "RUBYOPT"=>nil}}

        Exit Status: 100

        Standard Output:

        Standard Error:

       Uploading joy_of_cooking [0.0.1]
       ERROR: Failed to upload /tmp/tmp_working_dir_path20200610-4481-1f7s61j/joy_of_cooking/recipes/default.rb (200bc1c0f816f3702c0046f6b4964280) to https://s3.us-east-2.amazonaws.com:443/lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-200bc1c0f816f3702c0046f6b4964280?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200610T232149Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=3b4b6ac2523b99ad6149ffbec776b6635455efc829689e9189faa46e910bf32e : 403 "Forbidden"
              <?xml version="1.0" encoding="UTF-8"?>
              <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
              20200610T232149Z
              20200610/us-east-2/s3/aws4_request
              321869347d6757d4ffe7f116a97cb459637467c3a10b3b8a3fc8781b1904be6d</StringToSign><SignatureProvided>3b4b6ac2523b99ad6149ffbec776b6635455efc829689e9189faa46e910bf32e</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 33 32 31 38 36 39 33 34 37 64 36 37 35 37 64 34 66 66 65 37 66 31 31 36 61 39 37 63 62 34 35 39 36 33 37 34 36 37 63 33 61 31 30 62 33 62 38 61 33 66 63 38 37 38 31 62 31 39 30 34 62 65 36 64</StringToSignBytes><CanonicalRequest>PUT
              /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-200bc1c0f816f3702c0046f6b4964280
              X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232149Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
              content-md5:IAvBwPgW83AsAEb2tJZCgA==
              content-type:application/x-binary
              host:s3.us-east-2.amazonaws.com:443

              content-md5;content-type;host
              UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 32 30 30 62 63 31 63 30 66 38 31 36 66 33 37 30 32 63 30 30 34 36 66 36 62 34 39 36 34 32 38 30 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 49 41 76 42 77 50 67 57 38 33 41 73 41 45 62 32 74 4a 5a 43 67 41 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>259BD15924D57CB1</RequestId><HostId>tevLtSadAqE8rDe2U88C0vkGfd6Lnozcmf1BZs7uto9/UoRaj0kK7avjVxlfxf/X7sNGYdm8MQ4=</HostId></Error>
       #<Thread:0x000000000296a710@/opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:49 run> terminated with exception (report_on_exception is true):
       /opt/opscode/embedded/lib/ruby/2.6.0/net/http/response.rb:122:in `error!': 403 "Forbidden" (Net::HTTPServerException)
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:152:in `request'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:123:in `put'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/cookbook_uploader.rb:129:in `block in uploader_function_for'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:52:in `block (3 levels) in process'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `loop'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `block (2 levels) in process'
       ERROR: Failed to upload /tmp/tmp_working_dir_path20200610-4481-1f7s61j/joy_of_cooking/metadata.rb (f2ee2a0acc249dc40112aaa7a4c6df6c) to https://s3.us-east-2.amazonaws.com:443/lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-f2ee2a0acc249dc40112aaa7a4c6df6c?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200610T232149Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=d535e6337725325dafc04867f20f8c29a068ba64c21067eba55cf5ff8f65ad57 : 403 "Forbidden"
              <?xml version="1.0" encoding="UTF-8"?>
              <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
              20200610T232149Z
              20200610/us-east-2/s3/aws4_request
              4b0d49c058f9fdfa001937b4b63e28fa8adec9a2485487d3afbd223823027f68</StringToSign><SignatureProvided>d535e6337725325dafc04867f20f8c29a068ba64c21067eba55cf5ff8f65ad57</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 34 62 30 64 34 39 63 30 35 38 66 39 66 64 66 61 30 30 31 39 33 37 62 34 62 36 33 65 32 38 66 61 38 61 64 65 63 39 61 32 34 38 35 34 38 37 64 33 61 66 62 64 32 32 33 38 32 33 30 32 37 66 36 38</StringToSignBytes><CanonicalRequest>PUT
              /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-f2ee2a0acc249dc40112aaa7a4c6df6c
              X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232149Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
              content-md5:8u4qCswkncQBEqqnpMbfbA==
              content-type:application/x-binary
              host:s3.us-east-2.amazonaws.com:443

              content-md5;content-type;host
              UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 66 32 65 65 32 61 30 61 63 63 32 34 39 64 63 34 30 31 31 32 61 61 61 37 61 34 63 36 64 66 36 63 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 38 75 34 71 43 73 77 6b 6e 63 51 42 45 71 71 6e 70 4d 62 66 62 41 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>BDB5EEA10B78A36F</RequestId><HostId>3oAc0sZi1Sq2Ai/W31D9nMM6/TNdEOZvb9JAmTMXakNJQ2HXi2khG/MEYmhh3eP6+ltoR75i4qI=</HostId></Error>
       #<Thread:0x000000000296a5f8@/opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:49 run> terminated with exception (report_on_exception is true):
       /opt/opscode/embedded/lib/ruby/2.6.0/net/http/response.rb:122:in `error!': 403 "Forbidden" (Net::HTTPServerException)
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:152:in `request'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:123:in `put'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/cookbook_uploader.rb:129:in `block in uploader_function_for'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:52:in `block (3 levels) in process'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `loop'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `block (2 levels) in process'
       ERROR: Failed to upload /tmp/tmp_working_dir_path20200610-4481-1f7s61j/joy_of_cooking/README.md (6e21094b7a920e374e7261f50e9c4eef) to https://s3.us-east-2.amazonaws.com:443/lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-6e21094b7a920e374e7261f50e9c4eef?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200610T232149Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=ab54566862c57f542a0cf788c4709c48d493bf0c3567b6a3435e06f0a26cb910 : 403 "Forbidden"
              <?xml version="1.0" encoding="UTF-8"?>
              <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
              20200610T232149Z
              20200610/us-east-2/s3/aws4_request
              68abf9123436b850f84620ba735dfa8b39ca5c553db876b65ebdcd566fa51ba5</StringToSign><SignatureProvided>ab54566862c57f542a0cf788c4709c48d493bf0c3567b6a3435e06f0a26cb910</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 36 38 61 62 66 39 31 32 33 34 33 36 62 38 35 30 66 38 34 36 32 30 62 61 37 33 35 64 66 61 38 62 33 39 63 61 35 63 35 35 33 64 62 38 37 36 62 36 35 65 62 64 63 64 35 36 36 66 61 35 31 62 61 35</StringToSignBytes><CanonicalRequest>PUT
       ERROR: Failed to upload /tmp/tmp_working_dir_path20200610-4481-1f7s61j/joy_of_cooking/metadata.json (2982fc9d3cde771bebf9bc9e242ee78c) to https://s3.us-east-2.amazonaws.com:443/lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-2982fc9d3cde771bebf9bc9e242ee78c?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200610T232149Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=a9f1315549d71807de203b20d64c29247d7cd09ba0e87aa9f2427b4c9571f8f2 : 403 "Forbidden"
              <?xml version="1.0" encoding="UTF-8"?>
              <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
              20200610T232149Z
              20200610/us-east-2/s3/aws4_request
              d97c9cb1a99b8afb4fd82d7f454dbc5f458b321cb978e4dfb467c412f0d2dca0</StringToSign><SignatureProvided>a9f1315549d71807de203b20d64c29247d7cd09ba0e87aa9f2427b4c9571f8f2</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 64 39 37 63 39 63 62 31 61 39 39 62 38 61 66 62 34 66 64 38 32 64 37 66 34 35 34 64 62 63 35 66 34 35 38 62 33 32 31 63 62 39 37 38 65 34 64 66 62 34 36 37 63 34 31 32 66 30 64 32 64 63 61 30</StringToSignBytes><CanonicalRequest>PUT
              /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-2982fc9d3cde771bebf9bc9e242ee78c
              X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232149Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
              content-md5:KYL8nTzedxvr+byeJC7njA==
              content-type:application/x-binary
              host:s3.us-east-2.amazonaws.com:443

              content-md5;content-type;host
              UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 32 39 38 32 66 63 39 64 33 63 64 65 37 37 31 62 65 62 66 39 62 63 39 65 32 34 32 65 65 37 38 63 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 4b 59 4c 38 6e 54 7a 65 64 78 76 72 2b 62 79 65 4a 43 37 6e 6a 41 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>3494F1A62111E993</RequestId><HostId>2W3OWNs/6nmAWzwnbf433cqEBycif0+Ew+0LNL7Os2Ut+HmCP4nIaKEGjIlGgOg/guDRqB40TGM=</HostId></Error>
       #<Thread:0x000000000296a4b8@/opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:49 run> terminated with exception (report_on_exception is true):
       /opt/opscode/embedded/lib/ruby/2.6.0/net/http/response.rb:122:in `error!': 403 "Forbidden" (Net::HTTPServerException)
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:152:in `request'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:123:in `put'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/cookbook_uploader.rb:129:in `block in uploader_function_for'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:52:in `block (3 levels) in process'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `loop'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `block (2 levels) in process'
              /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-6e21094b7a920e374e7261f50e9c4eef
              X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232149Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
              content-md5:biEJS3qSDjdOcmH1DpxO7w==
              content-type:application/x-binary
              host:s3.us-east-2.amazonaws.com:443

              content-md5;content-type;host
              UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 36 65 32 31 30 39 34 62 37 61 39 32 30 65 33 37 34 65 37 32 36 31 66 35 30 65 39 63 34 65 65 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 62 69 45 4a 53 33 71 53 44 6a 64 4f 63 6d 48 31 44 70 78 4f 37 77 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>1EA07E387F95F95A</RequestId><HostId>qiB4FXeEvURjb+0TcdGpHKrlFTj7TVzKjrkqXf513vCfELa9qeS/RkEMEtuYbMBt5OFpTZ6AGhI=</HostId></Error>
       #<Thread:0x000000000296a850@/opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:49 run> terminated with exception (report_on_exception is true):
       /opt/opscode/embedded/lib/ruby/2.6.0/net/http/response.rb:122:in `error!': 403 "Forbidden" (Net::HTTPServerException)
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:152:in `request'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/http.rb:123:in `put'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/cookbook_uploader.rb:129:in `block in uploader_function_for'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:52:in `block (3 levels) in process'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `loop'
        from /opt/opscode/embedded/lib/ruby/gems/2.6.0/gems/chef-16.1.16/lib/chef/util/threaded_job_queue.rb:50:in `block (2 levels) in process'
       ERROR: You authenticated successfully to https://api.chef-server.dev/organizations/pedant_testorg_api_4447/ as pedant_user_api_4447 but you are not authorized for this action.
       Response:  <?xml version="1.0" encoding="UTF-8"?>
                 <Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
                 20200610T232149Z
                 20200610/us-east-2/s3/aws4_request
                 68abf9123436b850f84620ba735dfa8b39ca5c553db876b65ebdcd566fa51ba5</StringToSign><SignatureProvided>ab54566862c57f542a0cf788c4709c48d493bf0c3567b6a3435e06f0a26cb910</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 0a 32 30 32 30 30 36 31 30 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 36 38 61 62 66 39 31 32 33 34 33 36 62 38 35 30 66 38 34 36 32 30 62 61 37 33 35 64 66 61 38 62 33 39 63 61 35 63 35 35 33 64 62 38 37 36 62 36 35 65 62 64 63 64 35 36 36 66 61 35 31 62 61 35</StringToSignBytes><CanonicalRequest>PUT
                 /lbaker-east-ohio/organization-9ec903035d3bc6e9993e9ad9b0aea9fc/checksum-6e21094b7a920e374e7261f50e9c4eef
                 X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200610%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200610T232149Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost
                 content-md5:biEJS3qSDjdOcmH1DpxO7w==
                 content-type:application/x-binary
                 host:s3.us-east-2.amazonaws.com:443

                 content-md5;content-type;host
                 UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 39 65 63 39 30 33 30 33 35 64 33 62 63 36 65 39 39 39 33 65 39 61 64 39 62 30 61 65 61 39 66 63 2f 63 68 65 63 6b 73 75 6d 2d 36 65 32 31 30 39 34 62 37 61 39 32 30 65 33 37 34 65 37 32 36 31 66 35 30 65 39 63 34 65 65 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 30 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 30 54 32 33 32 31 34 39 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 39 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 6d 64 35 25 33 42 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3a 62 69 45 4a 53 33 71 53 44 6a 64 4f 63 6d 48 31 44 70 78 4f 37 77 3d 3d 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 62 69 6e 61 72 79 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 3a 34 34 33 0a 0a 63 6f 6e 74 65 6e 74 2d 6d 64 35 3b 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>1EA07E387F95F95A</RequestId><HostId>qiB4FXeEvURjb+0TcdGpHKrlFTj7TVzKjrkqXf513vCfELa9qeS/RkEMEtuYbMBt5OFpTZ6AGhI=</HostId></Error>
     # ./spec/api/knife/cookbook/upload_spec.rb:41:in `block (5 levels) in <top (required)>'

Finished in 1 minute 43.24 seconds (files took 2.95 seconds to load)
38 examples, 2 failures, 2 pending

Failed examples:

rspec ./spec/api/knife/cookbook/upload_spec.rb:33 # knife cookbook upload as an admin should succeed
rspec ./spec/api/knife/cookbook/upload_spec.rb:40 # knife cookbook upload as a normal user should succeed

Excerpt from patched http.rb illustrating pseudo 'correction' of the issue - see line labeled 'PATCH':

def request(method, path, headers = {}, data = false)
  http_attempts ||= 0
  url = create_url(path)
  processed_method, url, processed_headers, processed_data = apply_request_middleware(method, url, headers, data)
  # PATCH line #148
  processed_headers['HOST'][-4..-1] == ':443' ? processed_headers['HOST'][-4..-1] = '' : :noop
  response, rest_request, return_value = send_http_request(processed_method, url, processed_headers, processed_data)
  response, rest_request, return_value = apply_response_middleware(response, rest_request, return_value)

  response.error! unless success_response?(response)
  return_value

Excerpt from chef-server-ctl test --focus-knife after applying pseudo 'correction': (presigned URLs created without port on host header)

Finished in 1 minute 44.15 seconds (files took 3.11 seconds to load)
38 examples, 0 failures, 2 pending

Excerpt from chef-server-ctl test --focus-knife: (presigned URLs created with port on host header)

Finished in 16 minutes 57 seconds (files took 3.37 seconds to load)
5204 examples, 138 failures, 129 pending

Failed examples:

rspec ./spec/api/cookbook_artifacts/delete_spec.rb[1:1:1:1:2:2:1] # Cookbook Artifacts API endpoint API v0 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
rspec ./spec/api/cookbook_artifacts/delete_spec.rb[1:2:1:1:2:2:1] # Cookbook Artifacts API endpoint API v2 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:1:1] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns a 200 response
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:1:2] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns valid file URLs
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:2:1] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns a 200 response
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:2:2] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns valid file URLs
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:3:1] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an user outside of the organization should respond with 403 Forbidden
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:4:1] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> with invalid user should respond with 401 Unauthorized
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:1:1] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns a 200 response
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:1:2] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns valid file URLs
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:2:1] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns a 200 response
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:2:2] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns valid file URLs
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:3:1] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an user outside of the organization should respond with 403 Forbidden
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:4:1] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> with invalid user should respond with 401 Unauthorized
<SNIP>

Excerpt from patched /opt/opscode/embedded/service/oc-chef-pedant/lib/pedant/request.rb illustrating pseudo 'correction' of the issue - see line labeled 'PATCH':

def authenticated_request(method, url, requestor, opts={}, &validator)
  headers, payload = construct_request(method, url, requestor, opts)
  # PATCH
  if headers['Host'] == 's3.us-east-2.amazonaws.com'
    headers['Host'] += ':443'
  end
  do_request(method, url, headers, payload, &validator)
end

Excerpt from chef-server-ctl test --focus-knife after applying pseudo 'correction': (presigned URLs created with port on host header)

Failures:

  1) Cookbook Artifacts API endpoint API v0 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
     Failure/Error: response.code.should eq expected_reponse_code.to_s

       expected: "404"
            got: "403"

       (compared using ==)
     Shared Example Group: "deletes cookbook artifacts" called from ./spec/api/cookbook_artifacts/delete_spec.rb:196
     # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
     # ./spec/api/cookbook_artifacts/delete_spec.rb:134:in `block (6 levels) in <top (required)>'

  2) Cookbook Artifacts API endpoint API v2 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
     Failure/Error: response.code.should eq expected_reponse_code.to_s

       expected: "404"
            got: "403"

       (compared using ==)
     Shared Example Group: "deletes cookbook artifacts" called from ./spec/api/cookbook_artifacts/delete_spec.rb:200
     # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
     # ./spec/api/cookbook_artifacts/delete_spec.rb:134:in `block (6 levels) in <top (required)>'

  3) Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns valid file URLs
     Failure/Error: response.body.should == recipe_content

       expected: "hello-1592121423-492390787-23232"
            got: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SignatureDoesNotMatch</Code><Message>The re...ostId>JdWY1pvD5xmAKi4RRNIbmgIQYIfVfYUBnyUHY/zWX15gQXLTxu5tme1msNCcogtaWYyUIPirrdI=</HostId></Error>" (using ==)
       Diff:
       @@ -1,2 +1,12 @@
       -hello-1592121423-492390787-23232
       +<?xml version="1.0" encoding="UTF-8"?>
       +<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
       +20200614T075708Z
       +20200614/us-east-2/s3/aws4_request
       +c6c64a900d0ad218f8e0139b4bf25103fdaca94cf2f7cd7bd4efea4f85556bd1</StringToSign><SignatureProvided>8fd291d8e3f6557c7d0f7b9b5c4cfaa725a4fd2bc6dc75615be92cf084407511</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 34 54 30 37 35 37 30 38 5a 0a 32 30 32 30 30 36 31 34 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 63 36 63 36 34 61 39 30 30 64 30 61 64 32 31 38 66 38 65 30 31 33 39 62 34 62 66 32 35 31 30 33 66 64 61 63 61 39 34 63 66 32 66 37 63 64 37 62 64 34 65 66 65 61 34 66 38 35 35 35 36 62 64 31</StringToSignBytes><CanonicalRequest>GET
       +/lbaker-east-ohio/organization-0529aa2acf46010f707f56e88f20c191/checksum-c485dc9aa909e8a8cd20e9e74e72c51f
       +X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200614%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200614T075708Z&amp;X-Amz-Expires=28800&amp;X-Amz-SignedHeaders=host
       +host:s3.us-east-2.amazonaws.com
       +
       +host
       +UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>47 45 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 30 35 32 39 61 61 32 61 63 66 34 36 30 31 30 66 37 30 37 66 35 36 65 38 38 66 32 30 63 31 39 31 2f 63 68 65 63 6b 73 75 6d 2d 63 34 38 35 64 63 39 61 61 39 30 39 65 38 61 38 63 64 32 30 65 39 65 37 34 65 37 32 63 35 31 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 34 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 34 54 30 37 35 37 30 38 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 32 38 38 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>3F2EEE54E04FC7D9</RequestId><HostId>JdWY1pvD5xmAKi4RRNIbmgIQYIfVfYUBnyUHY/zWX15gQXLTxu5tme1msNCcogtaWYyUIPirrdI=</HostId></Error>

     Shared Example Group: "successful_cookbook_fetch" called from ./spec/api/cookbook_artifacts/read_spec.rb:229
     Shared Example Group: "reads cookbook artifacts" called from ./spec/api/cookbook_artifacts/read_spec.rb:257
     # ./spec/api/cookbook_artifacts/read_spec.rb:221:in `block (5 levels) in <top (required)>'

  4) Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns valid file URLs
     Failure/Error: response.body.should == recipe_content

       expected: "hello-1592121423-492390787-23232"
            got: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SignatureDoesNotMatch</Code><Message>The re...ostId>B/lDuUAGVhCsXZUOgrSVXTLC+u9kX5ca0mnqXRZrP0C1IfnTKu5jj0kaRO3DKJQjyGiRc9P1hRU=</HostId></Error>" (using ==)
       Diff:
       @@ -1,2 +1,12 @@
       -hello-1592121423-492390787-23232
       +<?xml version="1.0" encoding="UTF-8"?>
       +<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
       +20200614T075710Z
       +20200614/us-east-2/s3/aws4_request
       +0051c551c074cbc9488e8c60dae328a93cc6a1cf852c64ac2e8241969205ba5c</StringToSign><SignatureProvided>b84a8a820dc110c0843ee89633b0b060da66be378c693f6abf15d8b6dc5e49b7</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 34 54 30 37 35 37 31 30 5a 0a 32 30 32 30 30 36 31 34 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 30 30 35 31 63 35 35 31 63 30 37 34 63 62 63 39 34 38 38 65 38 63 36 30 64 61 65 33 32 38 61 39 33 63 63 36 61 31 63 66 38 35 32 63 36 34 61 63 32 65 38 32 34 31 39 36 39 32 30 35 62 61 35 63</StringToSignBytes><CanonicalRequest>GET
       +/lbaker-east-ohio/organization-0529aa2acf46010f707f56e88f20c191/checksum-c485dc9aa909e8a8cd20e9e74e72c51f
       +X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200614%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200614T075710Z&amp;X-Amz-Expires=28800&amp;X-Amz-SignedHeaders=host
       +host:s3.us-east-2.amazonaws.com
       +
       +host
       +UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>47 45 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 30 35 32 39 61 61 32 61 63 66 34 36 30 31 30 66 37 30 37 66 35 36 65 38 38 66 32 30 63 31 39 31 2f 63 68 65 63 6b 73 75 6d 2d 63 34 38 35 64 63 39 61 61 39 30 39 65 38 61 38 63 64 32 30 65 39 65 37 34 65 37 32 63 35 31 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 34 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 34 54 30 37 35 37 31 30 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 32 38 38 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>9F61812C421E8286</RequestId><HostId>B/lDuUAGVhCsXZUOgrSVXTLC+u9kX5ca0mnqXRZrP0C1IfnTKu5jj0kaRO3DKJQjyGiRc9P1hRU=</HostId></Error>

     Shared Example Group: "successful_cookbook_fetch" called from ./spec/api/cookbook_artifacts/read_spec.rb:235
     Shared Example Group: "reads cookbook artifacts" called from ./spec/api/cookbook_artifacts/read_spec.rb:257
     # ./spec/api/cookbook_artifacts/read_spec.rb:221:in `block (5 levels) in <top (required)>'

  5) Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns valid file URLs
     Failure/Error: response.body.should == recipe_content

       expected: "hello-1592121423-492390787-23232"
            got: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SignatureDoesNotMatch</Code><Message>The re...ostId>+mTc1RaYVGIKC8J1kuMjHKwxIq1Kd+HDgZ+pSQwLA6xLjY0jnU2a13sHlhbHNTe+81EfZjKTleU=</HostId></Error>" (using ==)
       Diff:
       @@ -1,2 +1,12 @@
       -hello-1592121423-492390787-23232
       +<?xml version="1.0" encoding="UTF-8"?>
       +<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
       +20200614T075713Z
       +20200614/us-east-2/s3/aws4_request
       +fe1feb97dfef3232fd2fa0e9f40dc308e6bb4e75ab40088d4e2a86045f371272</StringToSign><SignatureProvided>223d777e77712c6fe4da69956011e66eb50adcbb15eeba2b7a3b82ce3cefb14c</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 34 54 30 37 35 37 31 33 5a 0a 32 30 32 30 30 36 31 34 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 66 65 31 66 65 62 39 37 64 66 65 66 33 32 33 32 66 64 32 66 61 30 65 39 66 34 30 64 63 33 30 38 65 36 62 62 34 65 37 35 61 62 34 30 30 38 38 64 34 65 32 61 38 36 30 34 35 66 33 37 31 32 37 32</StringToSignBytes><CanonicalRequest>GET
       +/lbaker-east-ohio/organization-0529aa2acf46010f707f56e88f20c191/checksum-c485dc9aa909e8a8cd20e9e74e72c51f
       +X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200614%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200614T075713Z&amp;X-Amz-Expires=28800&amp;X-Amz-SignedHeaders=host
       +host:s3.us-east-2.amazonaws.com
       +
       +host
       +UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>47 45 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 30 35 32 39 61 61 32 61 63 66 34 36 30 31 30 66 37 30 37 66 35 36 65 38 38 66 32 30 63 31 39 31 2f 63 68 65 63 6b 73 75 6d 2d 63 34 38 35 64 63 39 61 61 39 30 39 65 38 61 38 63 64 32 30 65 39 65 37 34 65 37 32 63 35 31 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 34 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 34 54 30 37 35 37 31 33 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 32 38 38 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>FD84AB88668CF5C3</RequestId><HostId>+mTc1RaYVGIKC8J1kuMjHKwxIq1Kd+HDgZ+pSQwLA6xLjY0jnU2a13sHlhbHNTe+81EfZjKTleU=</HostId></Error>

     Shared Example Group: "successful_cookbook_fetch" called from ./spec/api/cookbook_artifacts/read_spec.rb:229
     Shared Example Group: "reads cookbook artifacts" called from ./spec/api/cookbook_artifacts/read_spec.rb:261
     # ./spec/api/cookbook_artifacts/read_spec.rb:221:in `block (5 levels) in <top (required)>'

  6) Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns valid file URLs
     Failure/Error: response.body.should == recipe_content

       expected: "hello-1592121423-492390787-23232"
            got: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SignatureDoesNotMatch</Code><Message>The re...ostId>xeio5VqsBS/BGeY6IFFznmX+eDt1VpQ/qj1B5nHKDyPd3wHQIgicFfOMxEifwRei8pm/fFDz6mc=</HostId></Error>" (using ==)
       Diff:
       @@ -1,2 +1,12 @@
       -hello-1592121423-492390787-23232
       +<?xml version="1.0" encoding="UTF-8"?>
       +<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
       +20200614T075715Z
       +20200614/us-east-2/s3/aws4_request
       +6d87679e6e2c503f08a603b23ca13e0bac52580222536cf1520d3dd1e72d4504</StringToSign><SignatureProvided>78c66bc0602eb1e6de7e27720b431917308e672bf08aa5960644664f59ef9882</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 34 54 30 37 35 37 31 35 5a 0a 32 30 32 30 30 36 31 34 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 36 64 38 37 36 37 39 65 36 65 32 63 35 30 33 66 30 38 61 36 30 33 62 32 33 63 61 31 33 65 30 62 61 63 35 32 35 38 30 32 32 32 35 33 36 63 66 31 35 32 30 64 33 64 64 31 65 37 32 64 34 35 30 34</StringToSignBytes><CanonicalRequest>GET
       +/lbaker-east-ohio/organization-0529aa2acf46010f707f56e88f20c191/checksum-c485dc9aa909e8a8cd20e9e74e72c51f
       +X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200614%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200614T075715Z&amp;X-Amz-Expires=28800&amp;X-Amz-SignedHeaders=host
       +host:s3.us-east-2.amazonaws.com
       +
       +host
       +UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>47 45 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 30 35 32 39 61 61 32 61 63 66 34 36 30 31 30 66 37 30 37 66 35 36 65 38 38 66 32 30 63 31 39 31 2f 63 68 65 63 6b 73 75 6d 2d 63 34 38 35 64 63 39 61 61 39 30 39 65 38 61 38 63 64 32 30 65 39 65 37 34 65 37 32 63 35 31 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 34 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 34 54 30 37 35 37 31 35 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 32 38 38 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>1FB684C525339D0B</RequestId><HostId>xeio5VqsBS/BGeY6IFFznmX+eDt1VpQ/qj1B5nHKDyPd3wHQIgicFfOMxEifwRei8pm/fFDz6mc=</HostId></Error>

     Shared Example Group: "successful_cookbook_fetch" called from ./spec/api/cookbook_artifacts/read_spec.rb:235
     Shared Example Group: "reads cookbook artifacts" called from ./spec/api/cookbook_artifacts/read_spec.rb:261
     # ./spec/api/cookbook_artifacts/read_spec.rb:221:in `block (5 levels) in <top (required)>'

  7) Cookbooks API endpoint API v0 behaves like deletes cookbooks DELETE /cookbooks/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
     Failure/Error: response.code.should eq expected_reponse_code.to_s

       expected: "404"
            got: "403"

       (compared using ==)
     Shared Example Group: "deletes cookbooks" called from ./spec/api/cookbooks/delete_spec.rb:173
     # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
     # ./lib/pedant/rspec/cookbook_util.rb:317:in `block in verify_checksum_cleanup'
     # ./lib/pedant/rspec/cookbook_util.rb:316:in `each'
     # ./lib/pedant/rspec/cookbook_util.rb:316:in `verify_checksum_cleanup'
     # ./spec/api/cookbooks/delete_spec.rb:113:in `block (6 levels) in <top (required)>'

  8) Cookbooks API endpoint API v2 behaves like deletes cookbooks DELETE /cookbooks/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
     Failure/Error: response.code.should eq expected_reponse_code.to_s

       expected: "404"
            got: "403"

       (compared using ==)
     Shared Example Group: "deletes cookbooks" called from ./spec/api/cookbooks/delete_spec.rb:177
     # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
     # ./lib/pedant/rspec/cookbook_util.rb:317:in `block in verify_checksum_cleanup'
     # ./lib/pedant/rspec/cookbook_util.rb:316:in `each'
     # ./lib/pedant/rspec/cookbook_util.rb:316:in `verify_checksum_cleanup'
     # ./spec/api/cookbooks/delete_spec.rb:113:in `block (6 levels) in <top (required)>'

  9) Cookbooks API endpoint API v0 behaves like reads cookbooks GET /cookbooks/<name>/<version> as a normal user allows access to cookbook recipe files via net/http
     Failure/Error: response.body.should == recipe_content

       expected: "hello-1592121423-492390787-23232"
            got: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SignatureDoesNotMatch</Code><Message>The re...ostId>nwpfEH8yztgIB7lvDWQvzGp0Wbz/HGKIFlYNaIGcscuAcGXg3/r6AOmCaMkw0t2FQ7Atglji+RE=</HostId></Error>" (using ==)
       Diff:
       @@ -1,2 +1,12 @@
       -hello-1592121423-492390787-23232
       +<?xml version="1.0" encoding="UTF-8"?>
       +<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
       +20200614T075800Z
       +20200614/us-east-2/s3/aws4_request
       +d9c0ac36c6b871b458a81cb63e3d948487e479e95368585d285628284c79ce29</StringToSign><SignatureProvided>03a4dfedf01ec6ca899bf96a00bc5aca14fba2af15560c0d270d538b226d830b</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 34 54 30 37 35 38 30 30 5a 0a 32 30 32 30 30 36 31 34 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 64 39 63 30 61 63 33 36 63 36 62 38 37 31 62 34 35 38 61 38 31 63 62 36 33 65 33 64 39 34 38 34 38 37 65 34 37 39 65 39 35 33 36 38 35 38 35 64 32 38 35 36 32 38 32 38 34 63 37 39 63 65 32 39</StringToSignBytes><CanonicalRequest>GET
       +/lbaker-east-ohio/organization-0529aa2acf46010f707f56e88f20c191/checksum-c485dc9aa909e8a8cd20e9e74e72c51f
       +X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200614%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200614T075800Z&amp;X-Amz-Expires=28800&amp;X-Amz-SignedHeaders=host
       +host:s3.us-east-2.amazonaws.com
       +
       +host
       +UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>47 45 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 30 35 32 39 61 61 32 61 63 66 34 36 30 31 30 66 37 30 37 66 35 36 65 38 38 66 32 30 63 31 39 31 2f 63 68 65 63 6b 73 75 6d 2d 63 34 38 35 64 63 39 61 61 39 30 39 65 38 61 38 63 64 32 30 65 39 65 37 34 65 37 32 63 35 31 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 34 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 34 54 30 37 35 38 30 30 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 32 38 38 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>2A49AA415A8BCA59</RequestId><HostId>nwpfEH8yztgIB7lvDWQvzGp0Wbz/HGKIFlYNaIGcscuAcGXg3/r6AOmCaMkw0t2FQ7Atglji+RE=</HostId></Error>

     Shared Example Group: "reads cookbooks" called from ./spec/api/cookbooks/read_spec.rb:360
     # ./spec/api/cookbooks/read_spec.rb:329:in `block (6 levels) in <top (required)>'

  10) Cookbooks API endpoint API v2 behaves like reads cookbooks GET /cookbooks/<name>/<version> as a normal user allows access to cookbook recipe files via net/http
      Failure/Error: response.body.should == recipe_content

        expected: "hello-1592121423-492390787-23232"
             got: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SignatureDoesNotMatch</Code><Message>The re...ostId>9nsZ9jV6zwq1A3O70jEMhuBHavLQaKMtz4pCpsrQkVEq9bzX1RQkK/VmGa81yOB81lstZrRC9PM=</HostId></Error>" (using ==)
        Diff:
        @@ -1,2 +1,12 @@
        -hello-1592121423-492390787-23232
        +<?xml version="1.0" encoding="UTF-8"?>
        +<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>AKIARUQHMSKVZVNSIY5B</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256
        +20200614T075805Z
        +20200614/us-east-2/s3/aws4_request
        +f32db71671d626ce8ca686d85792d816b9c597a3906dd67ea3bd42dbbce1e456</StringToSign><SignatureProvided>2fc97a7af5b255933cc68913b8c6f1a650dab1978f3d24287447cc3f1b38175a</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 30 30 36 31 34 54 30 37 35 38 30 35 5a 0a 32 30 32 30 30 36 31 34 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 66 33 32 64 62 37 31 36 37 31 64 36 32 36 63 65 38 63 61 36 38 36 64 38 35 37 39 32 64 38 31 36 62 39 63 35 39 37 61 33 39 30 36 64 64 36 37 65 61 33 62 64 34 32 64 62 62 63 65 31 65 34 35 36</StringToSignBytes><CanonicalRequest>GET
        +/lbaker-east-ohio/organization-0529aa2acf46010f707f56e88f20c191/checksum-c485dc9aa909e8a8cd20e9e74e72c51f
        +X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIARUQHMSKVZVNSIY5B%2F20200614%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20200614T075805Z&amp;X-Amz-Expires=28800&amp;X-Amz-SignedHeaders=host
        +host:s3.us-east-2.amazonaws.com
        +
        +host
        +UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>47 45 54 0a 2f 6c 62 61 6b 65 72 2d 65 61 73 74 2d 6f 68 69 6f 2f 6f 72 67 61 6e 69 7a 61 74 69 6f 6e 2d 30 35 32 39 61 61 32 61 63 66 34 36 30 31 30 66 37 30 37 66 35 36 65 38 38 66 32 30 63 31 39 31 2f 63 68 65 63 6b 73 75 6d 2d 63 34 38 35 64 63 39 61 61 39 30 39 65 38 61 38 63 64 32 30 65 39 65 37 34 65 37 32 63 35 31 66 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 52 55 51 48 4d 53 4b 56 5a 56 4e 53 49 59 35 42 25 32 46 32 30 32 30 30 36 31 34 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 30 30 36 31 34 54 30 37 35 38 30 35 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 32 38 38 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 73 33 2e 75 73 2d 65 61 73 74 2d 32 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>5FA556128D89795C</RequestId><HostId>9nsZ9jV6zwq1A3O70jEMhuBHavLQaKMtz4pCpsrQkVEq9bzX1RQkK/VmGa81yOB81lstZrRC9PM=</HostId></Error>

      Shared Example Group: "reads cookbooks" called from ./spec/api/cookbooks/read_spec.rb:364
      # ./spec/api/cookbooks/read_spec.rb:329:in `block (6 levels) in <top (required)>'

  11) Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums deleting all checksums should succeed
      Failure/Error: response.code.should eq expected_reponse_code.to_s

        expected: "404"
             got: "403"

        (compared using ==)
      Shared Example Group: "updates cookbooks" called from ./spec/api/cookbooks/update_spec.rb:921
      # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
      # ./lib/pedant/rspec/cookbook_util.rb:317:in `block in verify_checksum_cleanup'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `each'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `verify_checksum_cleanup'
      # ./spec/api/cookbooks/update_spec.rb:289:in `block (5 levels) in <top (required)>'

  12) Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums deleting some checksums should succeed
      Failure/Error: response.code.should eq expected_reponse_code.to_s

        expected: "404"
             got: "403"

        (compared using ==)
      Shared Example Group: "updates cookbooks" called from ./spec/api/cookbooks/update_spec.rb:921
      # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
      # ./lib/pedant/rspec/cookbook_util.rb:317:in `block in verify_checksum_cleanup'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `each'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `verify_checksum_cleanup'
      # ./spec/api/cookbooks/update_spec.rb:344:in `block (5 levels) in <top (required)>'

  13) Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums changing all different checksums should succeed
      Failure/Error: response.code.should eq expected_reponse_code.to_s

        expected: "404"
             got: "403"

        (compared using ==)
      Shared Example Group: "updates cookbooks" called from ./spec/api/cookbooks/update_spec.rb:921
      # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
      # ./lib/pedant/rspec/cookbook_util.rb:317:in `block in verify_checksum_cleanup'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `each'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `verify_checksum_cleanup'
      # ./spec/api/cookbooks/update_spec.rb:396:in `block (5 levels) in <top (required)>'

  14) Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums changing some different checksums should succeed
      Failure/Error: response.code.should eq expected_reponse_code.to_s

        expected: "404"
             got: "403"

        (compared using ==)
      Shared Example Group: "updates cookbooks" called from ./spec/api/cookbooks/update_spec.rb:921
      # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
      # ./lib/pedant/rspec/cookbook_util.rb:317:in `block in verify_checksum_cleanup'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `each'
      # ./lib/pedant/rspec/cookbook_util.rb:316:in `verify_checksum_cleanup'
      # ./spec/api/cookbooks/update_spec.rb:451:in `block (5 levels) in <top (required)>'

  15) Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums CHEF-3716 coverage it does not delete checksums in use by another version
      Failure/Error: response.code.should eq expected_reponse_code.to_s

        expected: "404"
             got: "403"

        (compared using ==)
      Shared Example Group: "updates cookbooks" called from ./spec/api/cookbooks/update_spec.rb:921
      # ./lib/pedant/rspec/cookbook_util.rb:336:in `verify_checksum_url'
      # ./spec/api/cookbooks/update_spec.rb:616:in `block (7 levels) in <top (required)>'
      # ./spec/api/cookbooks/update_spec.rb:615:in `each'
      # ./spec/api/cookbooks/update_spec.rb:615:in `block (6 levels) in <top (required)>'

  16) Sandboxes API Endpoint Sandboxes Endpoint, PUT when committing an incomplete sandbox should respond with 503 Service Unavailable
      Failure/Error: should look_like expected_response

        Response should have HTTP status code 503 ('Service Unavailable'), but it was actually 500 ('Internal Server Error')
          Reponse Body: {"error":["internal service error"]}
      # ./lib/pedant/rspec/common.rb:78:in `block in should_respond_with'

Finished in 19 minutes 24 seconds (files took 3.26 seconds to load)
5204 examples, 16 failures, 129 pending

Failed examples:

rspec ./spec/api/cookbook_artifacts/delete_spec.rb[1:1:1:1:2:2:1] # Cookbook Artifacts API endpoint API v0 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
rspec ./spec/api/cookbook_artifacts/delete_spec.rb[1:2:1:1:2:2:1] # Cookbook Artifacts API endpoint API v2 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:1:2] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns valid file URLs
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:1:1:2:2:2] # Cookbook Artifacts API endpoint API v0 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns valid file URLs
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:1:2] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as a normal user returns valid file URLs
rspec ./spec/api/cookbook_artifacts/read_spec.rb[1:2:1:2:2:2] # Cookbook Artifacts API endpoint API v2 behaves like reads cookbook artifacts GET /cookbook_artifacts/<name>/<version> as an admin user returns valid file URLs
rspec ./spec/api/cookbooks/delete_spec.rb[1:1:1:1:2:2:1] # Cookbooks API endpoint API v0 behaves like deletes cookbooks DELETE /cookbooks/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
rspec ./spec/api/cookbooks/delete_spec.rb[1:2:1:1:2:2:1] # Cookbooks API endpoint API v2 behaves like deletes cookbooks DELETE /cookbooks/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
rspec ./spec/api/cookbooks/read_spec.rb[1:1:1:2:1:2:1] # Cookbooks API endpoint API v0 behaves like reads cookbooks GET /cookbooks/<name>/<version> as a normal user allows access to cookbook recipe files via net/http
rspec ./spec/api/cookbooks/read_spec.rb[1:2:1:2:1:2:1] # Cookbooks API endpoint API v2 behaves like reads cookbooks GET /cookbooks/<name>/<version> as a normal user allows access to cookbook recipe files via net/http
rspec ./spec/api/cookbooks/update_spec.rb[1:1:1:1:3:4] # Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums deleting all checksums should succeed
rspec ./spec/api/cookbooks/update_spec.rb[1:1:1:1:3:5] # Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums deleting some checksums should succeed
rspec ./spec/api/cookbooks/update_spec.rb[1:1:1:1:3:6] # Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums changing all different checksums should succeed
rspec ./spec/api/cookbooks/update_spec.rb[1:1:1:1:3:7] # Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums changing some different checksums should succeed
rspec ./spec/api/cookbooks/update_spec.rb[1:1:1:1:3:9:1] # Cookbooks API endpoint API v0 behaves like updates cookbooks PUT /cookbooks/<name>/<version> [update] for checksums CHEF-3716 coverage it does not delete checksums in use by another version
rspec ./spec/api/sandboxes/complete_endpoint_spec.rb:196 # Sandboxes API Endpoint Sandboxes Endpoint, PUT when committing an incomplete sandbox should respond with 503 Service Unavailable

Bookshelf API URL Endpoint Problem

After enabling bookshelf for testing, a number of errors were received of the following form:

Failures:

  1) Cookbook Artifacts API endpoint API v0 behaves like deletes cookbook artifacts DELETE /cookbook_artifacts/<name>/<version> for existing cookbooks when deleting existent version of an existing cookbook should cleanup unused checksum data in s3/bookshelf
     Failure/Error:
       raise e, "Failed to open TCP connection to " +
         "#{conn_address}:#{conn_port} (#{e.message})"

     SocketError:
       Failed to open TCP connection to bookshelf.api.chef-server.dev:443 (getaddrinfo: Name or service not known)
     Shared Example Group: "deletes cookbook artifacts" called from ./spec/api/cookbook_artifacts/delete_spec.rb:196
     # ./lib/pedant/core_ext/net_http.rb:36:in `rescue in block in connect'
     # ./lib/pedant/core_ext/net_http.rb:33:in `block in connect'
     # ./lib/pedant/core_ext/net_http.rb:32:in `connect'
     # /opt/opscode/embedded/service/gem/ruby/2.6.0/gems/rest-client-2.1.0/lib/restclient/request.rb:727:in `transmit'
     # /opt/opscode/embedded/service/gem/ruby/2.6.0/gems/rest-client-2.1.0/lib/restclient/request.rb:163:in `execute'
     # /opt/opscode/embedded/service/gem/ruby/2.6.0/gems/rest-client-2.1.0/lib/restclient/request.rb:63:in `execute'
     # ./lib/pedant/request.rb:169:in `do_request_with_retry'
     # ./lib/pedant/request.rb:161:in `do_request'
     # ./lib/pedant/request.rb:110:in `authenticated_request'
     # ./lib/pedant/request.rb:217:in `put'
     # ./lib/pedant/rspec/cookbook_util.rb:57:in `upload_to_sandbox'
     # ./lib/pedant/rspec/cookbook_util.rb:73:in `block in upload_files_to_sandbox'
     # ./lib/pedant/rspec/cookbook_util.rb:73:in `each'
     # ./lib/pedant/rspec/cookbook_util.rb:73:in `upload_files_to_sandbox'
     # ./lib/pedant/rspec/cookbook_util.rb:275:in `make_cookbook_artifact_with_recipes'
     # ./spec/api/cookbook_artifacts/delete_spec.rb:109:in `block (6 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # SocketError:
     #   getaddrinfo: Name or service not known
     #   ./lib/pedant/core_ext/net_http.rb:34:in `initialize'

The pedant tests were trying to connect to bookshelf.api.chef-server.dev:443 but investigation using various tools (ping, curl, etc) revealed that there was no such active endpoint. It was hypothesized that the actual endpoint which chef servers were using was api.chef-server.dev:443, but changes introduced by erlcloud and mini_s3 added the bucketname (bookshelf) to the front of presigned urls, which is correct for s3 but (apparently) not for bookshelf. It was confirmed that api.chef-server.dev:443 was operational.

The first strategy to attempt to solve the problem was to change (through configuration) the bookshelf api endpoint from api.chef-server.dev:443 to bookshelf.api.chef-server.dev:443. Pursuant to this, various permutations of the following config settings were tried in chef-server.rb but they were unsuccessful:

api_fqdn
bookshelf['vip']
bookshelf['external_url']
bookshelf['listen']
opscode_erchef['s3_bucket']

Eventually, an erlcloud configuration was discovered which would change the presigned url style from 'bucketname before host' (vhost e.g. protocol://bucketname.host:port) to 'bucketname after host' (path e.g. protocol://host/bucketname:port), and a temporary hack was patched into mini_s3's new/3 function which would crudely detect whether bookshelf or s3 was being used, and configure erlcloud for the correct url style accordingly. This hack worked.

In summary, it has been confirmed that s3 accepts both 'bucketname before host' (vhost) and 'bucketname after host' (path) style urls, but bookshelf (apparently) doesn't and accepts only 'bucketname after host' (path) style urls. The production fix will be to change mini_s3's new function to return configs for only 'bucketname after host' style urls.

ADDENDUM: Amazon states "Buckets created after September 30, 2020, will support only virtual hosted-style requests. Path-style requests will continue to be supported for buckets created on or before this date." The production fix will thus be to change bookshelf to accept bucketname before host (vhost) style urls, and listen on bookshelf.api.chef-server.dev vs. api.chef-server.dev. The previously-suggested production fix was easier, and known. This newly-advised fix will be more difficult, and will require further investigation on how to achieve it.

erlcloud configs:

s3_bucket_access_method=vhost::vhost|path|auto,
s3_bucket_after_host=false::boolean(),

mini_s3 functions requiring patching: new/3 new/4

bookshelf and server-side code requiring patching: unknown

See: https://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/ https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html http://www.wryway.com/blog/aws-s3-url-styles/ https://acloud.guru/forums/aws-certified-solutions-architect-associate/discussion/-KMozONkdwMHmGJbKfc0/url-of-an-s3-bucket-quiz-question

Preliminary Assessment

erlcloud will not be a super quick/easy 'drop-in' replacement for mini_s3, but neither would any other library, most likely. Further investigation is required.

Conclusion

Forthcoming.

PRs: https://github.com/chef/chef-server/pull/1993

Branches: - chef-server lbaker/aws-sigv4 lbaker/aws-sigv4-debug lbaker/aws-sigv4-praj lbaker/aws-sigv4-scratch lbaker/aws-sigv4-scratch-rebased-erlang22 lbaker/presigned lbaker/presigned-headers - mini_s3 lbaker/erlcloud lbaker/erlcloud-env lbaker/ex-aws lbaker/presigned lbaker/presigned-headers-host lbaker/presigned-headers - lbakerchef/erlcloud lbaker/aws-sigv4 lbaker/presigned

stevendanna commented 4 years ago

These are some of my notes from a brief investigation of the "Host header problem" section of this issue. I've obviously looked into this much less than you have, so take this with a grain of salt.

There are 3 places where we need to consider what Host header is being used:

  1. Host header being used when generating presigned URLs
  2. Host header being sent by the client when using the presigned-URL
  3. Host header being used when validating presigned URLs

In my reading of the code, it is possible we have some inconsistencies in all 3 steps, however, since you have observed some oddness on the client-side, lets start there:

Forensic analysis of the diagnostic output revealed that when hitting a presigned URL endpoint, knife cookbook upload was (sometimes?) including a host header that was different than the host header which was used during presigned URL creation (sigv2 apparently did not use a host header to generate presigned URLs; sigv4 does). Specifically, it was (sometimes?) adding a port number to the end of the host string on the host header (e.g. "Host: whatever.com:port"). The reason this is occurring is currently unknown.

which implies that the client is adding the port unexpectedly. There are a few questions we may be concerned with here:

a. What does chef-client currently do? b. What did chef-client do at any point in the past? c. What we can expect from 3rd party clients.

Let's start with (a)

Based on my reading of the code, chef-client will currently add the port to the Host header along the code paths we care about. The header gets added here:

https://github.com/chef/chef/blob/master/lib/chef/http/authenticator.rb#L110

The authenticator middleware is used in the Chef::ServerAPI http client:

https://github.com/chef/chef/blob/master/lib/chef/server_api.rb#L47

The ServerAPI client is used in both the cookbook upload and cookbook download code:

https://github.com/chef/chef/blob/master/lib/chef/cookbook_uploader.rb#L43 https://github.com/chef/chef/blob/master/lib/chef/cookbook/synchronizer.rb#L327

This likely explains why you are seeing knife/chef-client add the port the header.

I haven't yet gone through the version history to see how consistent that behavior is yet to answer (b).

A further wrinkle here is what happens when we think about the behavior of other HTTP clients (including other HTTP client objects you can instantiate inside the chef code base).

HTTP/1.1 requires a Host header (note: HTTP/1.0 does not, so this new signing method might break HTTP/1.0 clients or non-conformant 1.1 clients).

However, the client is not required to always include the port in the header. Specifically, when the port matches the common port of the schema in use.

RFC 2616 says:

   Host = "Host" ":" host [ ":" port ] ; Section 3.2.2

A "host" without any trailing port information implies the default port for the service requested (e.g., "80" for an HTTP URL).

Thus, eliding the port is fine for port 80 when using HTTP and port 443 when using HTTPs. Further, based on https://url.spec.whatwg.org/#url-parsing many URL parsers, also elide the port when it matches the default, even when it is explicitly included in the URL.

As a result, many HTTP clients only append the port to the host header when a non-standard port is used. For example:

I think this puts us in a rather tough position. Namely,

You noted that:

UPDATE: I have tried it the 'other way' i.e. forced the creation of presigned URLs with host headers that have port numbers, but then we get pedant test failures for the instances when a chef client hits a presigned URLs and doesn't include a port number on the host header

I think it would be good to look carefully at those pedant failures and confirm (1) we definitely are always pre-signing with the port and (2) where the failures are happening inside chef/pedant.

Given my current analysis, I don't think we can avoid breaking at least some API clients here, so we'll have to think carefully about which breakage has the smallest scope.

I also looked into the server-side of this equation but haven't included those notes here since I wasn't convinced I was looking at the right branches.

lbakerchef commented 4 years ago

Specifically, it was (sometimes?) adding a port number to the end of the host string on the host header (e.g. "Host: whatever.com:port").

which implies that the client is adding the port unexpectedly.

Thanks. I changed 'adding a port number' to 'including a port number' for clarification purposes. I don't want to imply that I know the client is taking a www.host.com and appending a port to make it www.host.com:port although that could be the case.

lbakerchef commented 4 years ago

You noted that:

UPDATE: I have tried it the 'other way' i.e. forced the creation of presigned URLs with host headers that have port numbers, but then we get pedant test failures for the instances when a chef client hits a presigned URLs and doesn't include a port number on the host header

I think it would be good to look carefully at those pedant failures and confirm (1) we definitely are always pre-signing with the port and (2) where the failures are happening inside chef/pedant.

Pursuant to your comment here, I updated the Host Header Problem section with an additional study on this, and associated data and output.

PrajaktaPurohit commented 3 years ago

Closing this since we have evaluated and are currently using the Erlcloud library.