diff --git a/source/extensions/common/aws/signer_impl.cc b/source/extensions/common/aws/signer_impl.cc index 157ad46aa4b3b..86730647966bf 100644 --- a/source/extensions/common/aws/signer_impl.cc +++ b/source/extensions/common/aws/signer_impl.cc @@ -24,8 +24,7 @@ void SignerImpl::sign(Http::RequestMessage& message, bool sign_body) { } void SignerImpl::sign(Http::RequestHeaderMap& headers) { - // S3 payloads require special treatment. - if (service_name_ == "s3") { + if (require_content_hash_) { headers.setReference(SignatureHeaders::get().ContentSha256, SignatureConstants::get().UnsignedPayload); sign(headers, SignatureConstants::get().UnsignedPayload); diff --git a/source/extensions/common/aws/signer_impl.h b/source/extensions/common/aws/signer_impl.h index f925b6046b9dc..78908874e042a 100644 --- a/source/extensions/common/aws/signer_impl.h +++ b/source/extensions/common/aws/signer_impl.h @@ -47,8 +47,19 @@ class SignerImpl : public Signer, public Logger::Loggable { public: SignerImpl(absl::string_view service_name, absl::string_view region, const CredentialsProviderSharedPtr& credentials_provider, TimeSource& time_source) - : service_name_(service_name), region_(region), credentials_provider_(credentials_provider), - time_source_(time_source), long_date_formatter_(SignatureConstants::get().LongDateFormat), + : service_name_(service_name), region_(region), + + // S3, Glacier, ES payloads require special treatment. + // S3: + // https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html. + // ES: + // https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-request-signing.html. + // Glacier: + // https://docs.aws.amazon.com/amazonglacier/latest/dev/amazon-glacier-signing-requests.html. + require_content_hash_{service_name_ == "s3" || service_name_ == "glacier" || + service_name_ == "es"}, + credentials_provider_(credentials_provider), time_source_(time_source), + long_date_formatter_(SignatureConstants::get().LongDateFormat), short_date_formatter_(SignatureConstants::get().ShortDateFormat) {} void sign(Http::RequestMessage& message, bool sign_body = false) override; @@ -74,6 +85,8 @@ class SignerImpl : public Signer, public Logger::Loggable { const std::string service_name_; const std::string region_; + + const bool require_content_hash_; CredentialsProviderSharedPtr credentials_provider_; TimeSource& time_source_; DateFormatter long_date_formatter_; diff --git a/test/extensions/common/aws/signer_impl_test.cc b/test/extensions/common/aws/signer_impl_test.cc index 2bae6a72b25e5..857399749fb1d 100644 --- a/test/extensions/common/aws/signer_impl_test.cc +++ b/test/extensions/common/aws/signer_impl_test.cc @@ -41,6 +41,27 @@ class SignerImplTest : public testing::Test { message_->body() = std::make_unique(body); } + void expectSignHeaders(absl::string_view service_name, absl::string_view signature, + absl::string_view payload) { + auto* credentials_provider = new NiceMock(); + EXPECT_CALL(*credentials_provider, getCredentials()).WillOnce(Return(credentials_)); + Http::TestRequestHeaderMapImpl headers{}; + headers.setMethod("GET"); + headers.setPath("/"); + headers.addCopy(Http::LowerCaseString("host"), "www.example.com"); + + SignerImpl signer(service_name, "region", CredentialsProviderSharedPtr{credentials_provider}, + time_system_); + signer.sign(headers); + + EXPECT_EQ(fmt::format("AWS4-HMAC-SHA256 Credential=akid/20180102/region/{}/aws4_request, " + "SignedHeaders=host;x-amz-content-sha256;x-amz-date, " + "Signature={}", + service_name, signature), + headers.get(Http::CustomHeaders::get().Authorization)->value().getStringView()); + EXPECT_EQ(payload, headers.get(SignatureHeaders::get().ContentSha256)->value().getStringView()); + } + NiceMock* credentials_provider_; Event::SimulatedTimeSystem time_system_; Http::RequestMessagePtr message_; @@ -169,46 +190,16 @@ TEST_F(SignerImplTest, SignHostHeader) { message_->headers().get(Http::CustomHeaders::get().Authorization)->value().getStringView()); } -// Verify signing headers for S3 -TEST_F(SignerImplTest, SignHeadersS3) { - auto* credentials_provider = new NiceMock(); - EXPECT_CALL(*credentials_provider, getCredentials()).WillOnce(Return(credentials_)); - Http::TestRequestHeaderMapImpl headers{}; - headers.setMethod("GET"); - headers.setPath("/"); - headers.addCopy(Http::LowerCaseString("host"), "www.example.com"); - - SignerImpl signer("s3", "region", CredentialsProviderSharedPtr{credentials_provider}, - time_system_); - signer.sign(headers); - - EXPECT_EQ("AWS4-HMAC-SHA256 Credential=akid/20180102/region/s3/aws4_request, " - "SignedHeaders=host;x-amz-content-sha256;x-amz-date, " - "Signature=d97cae067345792b78d2bad746f25c729b9eb4701127e13a7c80398f8216a167", - headers.get(Http::CustomHeaders::get().Authorization)->value().getStringView()); - EXPECT_EQ(SignatureConstants::get().UnsignedPayload, - headers.get(SignatureHeaders::get().ContentSha256)->value().getStringView()); -} - -// Verify signing headers for non S3 -TEST_F(SignerImplTest, SignHeadersNonS3) { - auto* credentials_provider = new NiceMock(); - EXPECT_CALL(*credentials_provider, getCredentials()).WillOnce(Return(credentials_)); - Http::TestRequestHeaderMapImpl headers{}; - headers.setMethod("GET"); - headers.setPath("/"); - headers.addCopy(Http::LowerCaseString("host"), "www.example.com"); - - SignerImpl signer("service", "region", CredentialsProviderSharedPtr{credentials_provider}, - time_system_); - signer.sign(headers); - - EXPECT_EQ("AWS4-HMAC-SHA256 Credential=akid/20180102/region/service/aws4_request, " - "SignedHeaders=host;x-amz-content-sha256;x-amz-date, " - "Signature=d9fd9be575a254c924d843964b063d770181d938ae818f5b603ef0575a5ce2cd", - headers.get(Http::CustomHeaders::get().Authorization)->value().getStringView()); - EXPECT_EQ(SignatureConstants::get().HashedEmptyString, - headers.get(SignatureHeaders::get().ContentSha256)->value().getStringView()); +// Verify signing headers for services. +TEST_F(SignerImplTest, SignHeadersByService) { + expectSignHeaders("s3", "d97cae067345792b78d2bad746f25c729b9eb4701127e13a7c80398f8216a167", + SignatureConstants::get().UnsignedPayload); + expectSignHeaders("service", "d9fd9be575a254c924d843964b063d770181d938ae818f5b603ef0575a5ce2cd", + SignatureConstants::get().HashedEmptyString); + expectSignHeaders("es", "0fd9c974bb2ad16c8d8a314dca4f6db151d32cbd04748d9c018afee2a685a02e", + SignatureConstants::get().UnsignedPayload); + expectSignHeaders("glacier", "8d1f241d77c64cda57b042cd312180f16e98dbd7a96e5545681430f8dbde45a0", + SignatureConstants::get().UnsignedPayload); } } // namespace