diff --git a/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD b/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD index ee92fb652582e..693f0b92ff34d 100644 --- a/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD +++ b/api/envoy/extensions/filters/http/aws_request_signing/v3/BUILD @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], + deps = [ + "//envoy/type/matcher/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto b/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto index ae46400130d52..dd439e97d6d08 100644 --- a/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto +++ b/api/envoy/extensions/filters/http/aws_request_signing/v3/aws_request_signing.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.aws_request_signing.v3; +import "envoy/type/matcher/v3/string.proto"; + import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -16,6 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#extension: envoy.filters.http.aws_request_signing] // Top level configuration for the AWS request signing filter. +// [#next-free-field: 6] message AwsRequestSigning { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.aws_request_signing.v2alpha.AwsRequestSigning"; @@ -48,4 +51,15 @@ message AwsRequestSigning { // to calculate the payload hash. Not all services support this option. See the `S3 // `_ policy for details. bool use_unsigned_payload = 4; + + // A list of request header string matchers that will be excluded from signing. The excluded header can be matched by + // any patterns defined in the StringMatcher proto (e.g. exact string, prefix, regex, etc). + // + // Example: + // match_excluded_headers: + // - prefix: x-envoy + // - exact: foo + // - exact: bar + // When applied, all headers that start with "x-envoy" and headers "foo" and "bar" will not be signed. + repeated type.matcher.v3.StringMatcher match_excluded_headers = 5; } diff --git a/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst b/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst index 4c31387c503a8..27d5f58a340d7 100644 --- a/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst +++ b/docs/root/configuration/http/http_filters/aws_request_signing_filter.rst @@ -25,6 +25,10 @@ When :ref:`use_unsigned_payload ` for details. +The :ref:`match_excluded_headers ` +option allows excluding certain request headers from being signed. This usually applies to headers that are likely to mutate or +are added later such as in retries. By default, the headers ``x-forwarded-for``, ``x-forwarded-proto``, and ``x-amzn-trace-id`` are always excluded. + Example configuration --------------------- @@ -38,6 +42,10 @@ Example filter configuration: service_name: s3 region: us-west-2 use_unsigned_payload: true + match_excluded_headers: + - prefix: x-envoy + - prefix: x-forwarded + - exact: x-amzn-trace-id Statistics diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index d04b950b3cc9f..506c48903a7ba 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -51,6 +51,7 @@ New Features ------------ * access log: added :ref:`grpc_stream_retry_policy ` to the gRPC logger to reconnect when a connection fails to be established. * api: added support for *xds.type.v3.TypedStruct* in addition to the now-deprecated *udpa.type.v1.TypedStruct* proto message, which is a wrapper proto used to encode typed JSON data in a *google.protobuf.Any* field. +* aws_request_signing_filter: added :ref:`match_excluded_headers ` to the signing filter to optionally exclude request headers from signing. * bootstrap: added :ref:`typed_dns_resolver_config ` in the bootstrap to support DNS resolver as an extension. * cluster: added :ref:`typed_dns_resolver_config ` in the cluster to support DNS resolver as an extension. * config: added :ref:`environment_variable ` to the :ref:`DataSource `. diff --git a/source/extensions/common/aws/BUILD b/source/extensions/common/aws/BUILD index f8857b60a5531..31bd0c8fa72a9 100644 --- a/source/extensions/common/aws/BUILD +++ b/source/extensions/common/aws/BUILD @@ -62,6 +62,7 @@ envoy_cc_library( external_deps = ["curl"], deps = [ "//source/common/common:empty_string", + "//source/common/common:matchers_lib", "//source/common/common:utility_lib", "//source/common/http:headers_lib", ], diff --git a/source/extensions/common/aws/signer_impl.cc b/source/extensions/common/aws/signer_impl.cc index a071e29149671..9f7bf92b4eb0f 100644 --- a/source/extensions/common/aws/signer_impl.cc +++ b/source/extensions/common/aws/signer_impl.cc @@ -57,7 +57,7 @@ void SignerImpl::sign(Http::RequestHeaderMap& headers, const std::string& conten const auto short_date = short_date_formatter_.now(time_source_); headers.addCopy(SignatureHeaders::get().Date, long_date); // Phase 1: Create a canonical request - const auto canonical_headers = Utility::canonicalizeHeaders(headers); + const auto canonical_headers = Utility::canonicalizeHeaders(headers, excluded_header_matchers_); const auto canonical_request = Utility::createCanonicalRequest( service_name_, method_header->value().getStringView(), path_header->value().getStringView(), canonical_headers, content_hash); diff --git a/source/extensions/common/aws/signer_impl.h b/source/extensions/common/aws/signer_impl.h index 301ead3e40eec..208133ce26798 100644 --- a/source/extensions/common/aws/signer_impl.h +++ b/source/extensions/common/aws/signer_impl.h @@ -1,7 +1,11 @@ #pragma once +#include + #include "source/common/common/logger.h" +#include "source/common/common/matchers.h" #include "source/common/common/utility.h" +#include "source/common/http/headers.h" #include "source/common/singleton/const_singleton.h" #include "source/extensions/common/aws/credentials_provider.h" #include "source/extensions/common/aws/signer.h" @@ -38,6 +42,8 @@ class SignatureConstantValues { using SignatureConstants = ConstSingleton; +using AwsSigV4HeaderExclusionVector = std::vector; + /** * Implementation of the Signature V4 signing process. * See https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html @@ -45,10 +51,17 @@ using SignatureConstants = ConstSingleton; 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) + const CredentialsProviderSharedPtr& credentials_provider, TimeSource& time_source, + const AwsSigV4HeaderExclusionVector& matcher_config) : service_name_(service_name), region_(region), credentials_provider_(credentials_provider), time_source_(time_source), long_date_formatter_(SignatureConstants::get().LongDateFormat), - short_date_formatter_(SignatureConstants::get().ShortDateFormat) {} + short_date_formatter_(SignatureConstants::get().ShortDateFormat) { + for (const auto& matcher : matcher_config) { + excluded_header_matchers_.emplace_back( + std::make_unique>( + matcher)); + } + } void sign(Http::RequestMessage& message, bool sign_body = false) override; void sign(Http::RequestHeaderMap& headers, const std::string& content_hash) override; @@ -71,9 +84,24 @@ class SignerImpl : public Signer, public Logger::Loggable { const std::map& canonical_headers, absl::string_view signature) const; + std::vector defaultMatchers() const { + std::vector matcher_ptrs{}; + for (const auto& header : default_excluded_headers_) { + envoy::type::matcher::v3::StringMatcher m; + m.set_exact(header); + matcher_ptrs.emplace_back( + std::make_unique>( + m)); + } + return matcher_ptrs; + } + const std::string service_name_; const std::string region_; - + const std::vector default_excluded_headers_ = { + Http::Headers::get().ForwardedFor.get(), Http::Headers::get().ForwardedProto.get(), + "x-amzn-trace-id"}; + std::vector excluded_header_matchers_ = defaultMatchers(); CredentialsProviderSharedPtr credentials_provider_; TimeSource& time_source_; DateFormatter long_date_formatter_; diff --git a/source/extensions/common/aws/utility.cc b/source/extensions/common/aws/utility.cc index a0ba42fe4eb89..b1929086ad6d9 100644 --- a/source/extensions/common/aws/utility.cc +++ b/source/extensions/common/aws/utility.cc @@ -24,36 +24,39 @@ const std::string URI_ENCODE = "%{:02X}"; const std::string URI_DOUBLE_ENCODE = "%25{:02X}"; std::map -Utility::canonicalizeHeaders(const Http::RequestHeaderMap& headers) { +Utility::canonicalizeHeaders(const Http::RequestHeaderMap& headers, + const std::vector& excluded_headers) { std::map out; - headers.iterate([&out](const Http::HeaderEntry& entry) -> Http::HeaderMap::Iterate { - // Skip empty headers - if (entry.key().empty() || entry.value().empty()) { - return Http::HeaderMap::Iterate::Continue; - } - // Pseudo-headers should not be canonicalized - if (!entry.key().getStringView().empty() && entry.key().getStringView()[0] == ':') { - return Http::HeaderMap::Iterate::Continue; - } - // Skip headers that are likely to mutate, when crossing proxies - const auto key = entry.key().getStringView(); - if (key == Http::Headers::get().ForwardedFor.get() || - key == Http::Headers::get().ForwardedProto.get() || key == "x-amzn-trace-id") { - return Http::HeaderMap::Iterate::Continue; - } + headers.iterate( + [&out, &excluded_headers](const Http::HeaderEntry& entry) -> Http::HeaderMap::Iterate { + // Skip empty headers + if (entry.key().empty() || entry.value().empty()) { + return Http::HeaderMap::Iterate::Continue; + } + // Pseudo-headers should not be canonicalized + if (!entry.key().getStringView().empty() && entry.key().getStringView()[0] == ':') { + return Http::HeaderMap::Iterate::Continue; + } + const auto key = entry.key().getStringView(); + if (std::any_of(excluded_headers.begin(), excluded_headers.end(), + [&key](const Matchers::StringMatcherPtr& matcher) { + return matcher->match(key); + })) { + return Http::HeaderMap::Iterate::Continue; + } - std::string value(entry.value().getStringView()); - // Remove leading, trailing, and deduplicate repeated ascii spaces - absl::RemoveExtraAsciiWhitespace(&value); - const auto iter = out.find(std::string(entry.key().getStringView())); - // If the entry already exists, append the new value to the end - if (iter != out.end()) { - iter->second += fmt::format(",{}", value); - } else { - out.emplace(std::string(entry.key().getStringView()), value); - } - return Http::HeaderMap::Iterate::Continue; - }); + std::string value(entry.value().getStringView()); + // Remove leading, trailing, and deduplicate repeated ascii spaces + absl::RemoveExtraAsciiWhitespace(&value); + const auto iter = out.find(std::string(entry.key().getStringView())); + // If the entry already exists, append the new value to the end + if (iter != out.end()) { + iter->second += fmt::format(",{}", value); + } else { + out.emplace(std::string(entry.key().getStringView()), value); + } + return Http::HeaderMap::Iterate::Continue; + }); // The AWS SDK has a quirk where it removes "default ports" (80, 443) from the host headers // Additionally, we canonicalize the :authority header as "host" // TODO(lavignes): This may need to be tweaked to canonicalize :authority for HTTP/2 requests diff --git a/source/extensions/common/aws/utility.h b/source/extensions/common/aws/utility.h index 36b34da02a9f2..e7cfdac2abb1c 100644 --- a/source/extensions/common/aws/utility.h +++ b/source/extensions/common/aws/utility.h @@ -1,5 +1,6 @@ #pragma once +#include "source/common/common/matchers.h" #include "source/common/http/headers.h" namespace Envoy { @@ -13,10 +14,12 @@ class Utility { * Creates a canonicalized header map used in creating a AWS Signature V4 canonical request. * See https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html * @param headers a header map to canonicalize. + * @param excluded_headers a list of string matchers to exclude a given header from signing. * @return a std::map of canonicalized headers to be used in building a canonical request. */ static std::map - canonicalizeHeaders(const Http::RequestHeaderMap& headers); + canonicalizeHeaders(const Http::RequestHeaderMap& headers, + const std::vector& excluded_headers); /** * Creates an AWS Signature V4 canonical request string. diff --git a/source/extensions/filters/http/aws_lambda/config.cc b/source/extensions/filters/http/aws_lambda/config.cc index a3b1838ff1949..45a9cfe75d826 100644 --- a/source/extensions/filters/http/aws_lambda/config.cc +++ b/source/extensions/filters/http/aws_lambda/config.cc @@ -48,7 +48,10 @@ Http::FilterFactoryCb AwsLambdaFilterFactory::createFilterFactoryFromProtoTyped( const std::string region = arn->region(); auto signer = std::make_shared( service_name, region, std::move(credentials_provider), - context.mainThreadDispatcher().timeSource()); + context.mainThreadDispatcher().timeSource(), + // TODO: extend API to allow specifying header exclusion. ref: + // https://github.com/envoyproxy/envoy/pull/18998 + Extensions::Common::Aws::AwsSigV4HeaderExclusionVector{}); FilterSettings filter_settings{*arn, getInvocationMode(proto_config), proto_config.payload_passthrough()}; diff --git a/source/extensions/filters/http/aws_request_signing/BUILD b/source/extensions/filters/http/aws_request_signing/BUILD index fd1ecabed3227..942ba57aff1f8 100644 --- a/source/extensions/filters/http/aws_request_signing/BUILD +++ b/source/extensions/filters/http/aws_request_signing/BUILD @@ -32,6 +32,7 @@ envoy_cc_extension( deps = [ ":aws_request_signing_filter_lib", "//envoy/registry", + "//source/common/common:matchers_lib", "//source/extensions/common/aws:credentials_provider_impl_lib", "//source/extensions/common/aws:signer_impl_lib", "//source/extensions/filters/http/common:factory_base_lib", diff --git a/source/extensions/filters/http/aws_request_signing/config.cc b/source/extensions/filters/http/aws_request_signing/config.cc index 0d2e9bc97c1f4..d9a1a5c74a7bd 100644 --- a/source/extensions/filters/http/aws_request_signing/config.cc +++ b/source/extensions/filters/http/aws_request_signing/config.cc @@ -21,10 +21,11 @@ Http::FilterFactoryCb AwsRequestSigningFilterFactory::createFilterFactoryFromPro auto credentials_provider = std::make_shared( context.api(), Extensions::Common::Aws::Utility::metadataFetcher); + const auto matcher_config = Extensions::Common::Aws::AwsSigV4HeaderExclusionVector( + config.match_excluded_headers().begin(), config.match_excluded_headers().end()); auto signer = std::make_unique( config.service_name(), config.region(), credentials_provider, - context.mainThreadDispatcher().timeSource()); - + context.mainThreadDispatcher().timeSource(), matcher_config); auto filter_config = std::make_shared(std::move(signer), stats_prefix, context.scope(), config.host_rewrite(), config.use_unsigned_payload()); diff --git a/source/extensions/grpc_credentials/aws_iam/config.cc b/source/extensions/grpc_credentials/aws_iam/config.cc index c70140a437cbf..6253e6cc5cbc5 100644 --- a/source/extensions/grpc_credentials/aws_iam/config.cc +++ b/source/extensions/grpc_credentials/aws_iam/config.cc @@ -47,7 +47,10 @@ std::shared_ptr AwsIamGrpcCredentialsFactory::getChann auto credentials_provider = std::make_shared( api, Common::Aws::Utility::metadataFetcher); auto signer = std::make_unique( - config.service_name(), getRegion(config), credentials_provider, api.timeSource()); + config.service_name(), getRegion(config), credentials_provider, api.timeSource(), + // TODO: extend API to allow specifying header exclusion. ref: + // https://github.com/envoyproxy/envoy/pull/18998 + Common::Aws::AwsSigV4HeaderExclusionVector{}); std::shared_ptr new_call_creds = grpc::MetadataCredentialsFromPlugin( std::make_unique(std::move(signer))); if (call_creds == nullptr) { diff --git a/test/extensions/common/aws/signer_impl_test.cc b/test/extensions/common/aws/signer_impl_test.cc index b3c060529ff85..ff5565def723b 100644 --- a/test/extensions/common/aws/signer_impl_test.cc +++ b/test/extensions/common/aws/signer_impl_test.cc @@ -22,7 +22,7 @@ class SignerImplTest : public testing::Test { : credentials_provider_(new NiceMock()), message_(new Http::RequestMessageImpl()), signer_("service", "region", CredentialsProviderSharedPtr{credentials_provider_}, - time_system_), + time_system_, Extensions::Common::Aws::AwsSigV4HeaderExclusionVector{}), credentials_("akid", "secret"), token_credentials_("akid", "secret", "token") { // 20180102T030405Z time_system_.setSystemTime(std::chrono::milliseconds(1514862245000)); @@ -48,7 +48,7 @@ class SignerImplTest : public testing::Test { headers.addCopy(Http::LowerCaseString("host"), "www.example.com"); SignerImpl signer(service_name, "region", CredentialsProviderSharedPtr{credentials_provider}, - time_system_); + time_system_, Extensions::Common::Aws::AwsSigV4HeaderExclusionVector{}); if (use_unsigned_payload) { signer.signUnsignedPayload(headers); } else { diff --git a/test/extensions/common/aws/utility_test.cc b/test/extensions/common/aws/utility_test.cc index d19f8e220356a..629f28c2c9577 100644 --- a/test/extensions/common/aws/utility_test.cc +++ b/test/extensions/common/aws/utility_test.cc @@ -19,7 +19,8 @@ TEST(UtilityTest, CanonicalizeHeadersInAlphabeticalOrder) { {"d", "d_value"}, {"f", "f_value"}, {"b", "b_value"}, {"e", "e_value"}, {"c", "c_value"}, {"a", "a_value"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("a", "a_value"), Pair("b", "b_value"), Pair("c", "c_value"), Pair("d", "d_value"), Pair("e", "e_value"), Pair("f", "f_value"))); } @@ -31,7 +32,8 @@ TEST(UtilityTest, CanonicalizeHeadersSkippingPseudoHeaders) { {":method", "GET"}, {"normal", "normal_value"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("normal", "normal_value"))); } @@ -42,7 +44,8 @@ TEST(UtilityTest, CanonicalizeHeadersJoiningDuplicatesWithCommas) { {"a", "a_value2"}, {"a", "a_value3"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("a", "a_value1,a_value2,a_value3"))); } @@ -51,7 +54,8 @@ TEST(UtilityTest, CanonicalizeHeadersAuthorityToHost) { Http::TestRequestHeaderMapImpl headers{ {":authority", "authority_value"}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("host", "authority_value"))); } @@ -60,13 +64,14 @@ TEST(UtilityTest, CanonicalizeHeadersRemovingDefaultPortsFromHost) { Http::TestRequestHeaderMapImpl headers_port80{ {":authority", "example.com:80"}, }; - const auto map_port80 = Utility::canonicalizeHeaders(headers_port80); + std::vector exclusion_list = {}; + const auto map_port80 = Utility::canonicalizeHeaders(headers_port80, exclusion_list); EXPECT_THAT(map_port80, ElementsAre(Pair("host", "example.com"))); Http::TestRequestHeaderMapImpl headers_port443{ {":authority", "example.com:443"}, }; - const auto map_port443 = Utility::canonicalizeHeaders(headers_port443); + const auto map_port443 = Utility::canonicalizeHeaders(headers_port443, exclusion_list); EXPECT_THAT(map_port443, ElementsAre(Pair("host", "example.com"))); } @@ -78,20 +83,39 @@ TEST(UtilityTest, CanonicalizeHeadersTrimmingWhitespace) { {"internal", "internal value"}, {"all", " all value "}, }; - const auto map = Utility::canonicalizeHeaders(headers); + std::vector exclusion_list = {}; + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("all", "all value"), Pair("internal", "internal value"), Pair("leading", "leading value"), Pair("trailing", "trailing value"))); } -// Headers that are likely to mutate are not considered canonical -TEST(UtilityTest, CanonicalizeHeadersDropMutatingHeaders) { +// Headers in the exclusion list are not canonicalized +TEST(UtilityTest, CanonicalizeHeadersDropExcludedMatchers) { Http::TestRequestHeaderMapImpl headers{ {":authority", "example.com"}, {"x-forwarded-for", "1.2.3.4"}, {"x-forwarded-proto", "https"}, {"x-amz-date", "20130708T220855Z"}, - {"x-amz-content-sha256", "e3b0c44..."}, - }; - const auto map = Utility::canonicalizeHeaders(headers); + {"x-amz-content-sha256", "e3b0c44..."}, {"x-envoy-retry-on", "5xx,reset"}, + {"x-envoy-max-retries", "3"}, {"x-amzn-trace-id", "0123456789"}}; + std::vector exclusion_list = {}; + std::vector exact_matches = {"x-amzn-trace-id", "x-forwarded-for", + "x-forwarded-proto"}; + for (auto& str : exact_matches) { + envoy::type::matcher::v3::StringMatcher config; + config.set_exact(str); + exclusion_list.emplace_back( + std::make_unique>( + config)); + } + std::vector prefixes = {"x-envoy"}; + for (auto& match_str : prefixes) { + envoy::type::matcher::v3::StringMatcher config; + config.set_prefix(match_str); + exclusion_list.emplace_back( + std::make_unique>( + config)); + } + const auto map = Utility::canonicalizeHeaders(headers, exclusion_list); EXPECT_THAT(map, ElementsAre(Pair("host", "example.com"), Pair("x-amz-content-sha256", "e3b0c44..."), Pair("x-amz-date", "20130708T220855Z"))); diff --git a/test/extensions/filters/http/aws_request_signing/config_test.cc b/test/extensions/filters/http/aws_request_signing/config_test.cc index 58842ca8fd4d6..27c9577a4b49b 100644 --- a/test/extensions/filters/http/aws_request_signing/config_test.cc +++ b/test/extensions/filters/http/aws_request_signing/config_test.cc @@ -21,11 +21,27 @@ TEST(AwsRequestSigningFilterConfigTest, SimpleConfig) { const std::string yaml = R"EOF( service_name: s3 region: us-west-2 +match_excluded_headers: + - prefix: x-envoy + - exact: foo + - exact: bar )EOF"; AwsRequestSigningProtoConfig proto_config; TestUtility::loadFromYamlAndValidate(yaml, proto_config); + AwsRequestSigningProtoConfig expected_config; + expected_config.set_service_name("s3"); + expected_config.set_region("us-west-2"); + expected_config.add_match_excluded_headers()->set_prefix("x-envoy"); + expected_config.add_match_excluded_headers()->set_exact("foo"); + expected_config.add_match_excluded_headers()->set_exact("bar"); + + Protobuf::util::MessageDifferencer differencer; + differencer.set_message_field_comparison(Protobuf::util::MessageDifferencer::EQUAL); + differencer.set_repeated_field_comparison(Protobuf::util::MessageDifferencer::AS_SET); + EXPECT_TRUE(differencer.Compare(expected_config, proto_config)); + testing::NiceMock context; AwsRequestSigningFilterFactory factory;