Line data Source code
1 : #include "source/extensions/common/aws/signer_base_impl.h" 2 : 3 : #include <openssl/ssl.h> 4 : 5 : #include <cstddef> 6 : 7 : #include "envoy/common/exception.h" 8 : 9 : #include "source/common/buffer/buffer_impl.h" 10 : #include "source/common/common/fmt.h" 11 : #include "source/common/common/hex.h" 12 : #include "source/common/crypto/utility.h" 13 : #include "source/common/http/headers.h" 14 : #include "source/extensions/common/aws/utility.h" 15 : 16 : #include "absl/strings/str_join.h" 17 : 18 : namespace Envoy { 19 : namespace Extensions { 20 : namespace Common { 21 : namespace Aws { 22 : 23 : void SignerBaseImpl::sign(Http::RequestMessage& message, bool sign_body, 24 0 : const absl::string_view override_region) { 25 : 26 0 : const auto content_hash = createContentHash(message, sign_body); 27 0 : auto& headers = message.headers(); 28 0 : sign(headers, content_hash, override_region); 29 0 : } 30 : 31 : void SignerBaseImpl::signEmptyPayload(Http::RequestHeaderMap& headers, 32 1 : const absl::string_view override_region) { 33 1 : headers.setReference(SignatureHeaders::get().ContentSha256, 34 1 : SignatureConstants::get().HashedEmptyString); 35 1 : sign(headers, SignatureConstants::get().HashedEmptyString, override_region); 36 1 : } 37 : 38 : void SignerBaseImpl::signUnsignedPayload(Http::RequestHeaderMap& headers, 39 0 : const absl::string_view override_region) { 40 0 : headers.setReference(SignatureHeaders::get().ContentSha256, 41 0 : SignatureConstants::get().UnsignedPayload); 42 0 : sign(headers, SignatureConstants::get().UnsignedPayload, override_region); 43 0 : } 44 : 45 : // Required only for sigv4a 46 : void SignerBaseImpl::addRegionHeader( 47 : ABSL_ATTRIBUTE_UNUSED Http::RequestHeaderMap& headers, 48 0 : ABSL_ATTRIBUTE_UNUSED const absl::string_view override_region) const {} 49 : 50 0 : std::string SignerBaseImpl::getRegion() const { return region_; } 51 : 52 : void SignerBaseImpl::sign(Http::RequestHeaderMap& headers, const std::string& content_hash, 53 5 : const absl::string_view override_region) { 54 : 55 5 : headers.setReferenceKey(SignatureHeaders::get().ContentSha256, content_hash); 56 5 : const auto& credentials = credentials_provider_->getCredentials(); 57 5 : if (!credentials.accessKeyId() || !credentials.secretAccessKey()) { 58 : // Empty or "anonymous" credentials are a valid use-case for non-production environments. 59 : // This behavior matches what the AWS SDK would do. 60 5 : return; 61 5 : } 62 0 : const auto* method_header = headers.Method(); 63 0 : if (method_header == nullptr) { 64 0 : throw EnvoyException("Message is missing :method header"); 65 0 : } 66 0 : const auto* path_header = headers.Path(); 67 0 : if (path_header == nullptr) { 68 0 : throw EnvoyException("Message is missing :path header"); 69 0 : } 70 : 71 0 : if (credentials.sessionToken()) { 72 0 : headers.addCopy(SignatureHeaders::get().SecurityToken, credentials.sessionToken().value()); 73 0 : } 74 0 : const auto long_date = long_date_formatter_.now(time_source_); 75 0 : const auto short_date = short_date_formatter_.now(time_source_); 76 0 : headers.addCopy(SignatureHeaders::get().Date, long_date); 77 : 78 0 : addRegionHeader(headers, override_region); 79 : 80 : // Phase 1: Create a canonical request 81 0 : const auto canonical_headers = Utility::canonicalizeHeaders(headers, excluded_header_matchers_); 82 0 : const auto canonical_request = Utility::createCanonicalRequest( 83 0 : service_name_, method_header->value().getStringView(), path_header->value().getStringView(), 84 0 : canonical_headers, content_hash); 85 0 : ENVOY_LOG(debug, "Canonical request:\n{}", canonical_request); 86 : 87 : // Phase 2: Create a string to sign 88 0 : const auto credential_scope = createCredentialScope(short_date, override_region); 89 0 : const auto string_to_sign = createStringToSign(canonical_request, long_date, credential_scope); 90 0 : ENVOY_LOG(debug, "String to sign:\n{}", string_to_sign); 91 : 92 : // Phase 3: Create a signature 93 0 : const auto signature = 94 0 : createSignature(credentials.accessKeyId().value(), credentials.secretAccessKey().value(), 95 0 : short_date, string_to_sign, override_region); 96 : // Phase 4: Sign request 97 0 : const auto authorization_header = createAuthorizationHeader( 98 0 : credentials.accessKeyId().value(), credential_scope, canonical_headers, signature); 99 0 : ENVOY_LOG(debug, "Signing request with: {}", authorization_header); 100 0 : headers.addCopy(Http::CustomHeaders::get().Authorization, authorization_header); 101 0 : } 102 : 103 0 : std::string SignerBaseImpl::createContentHash(Http::RequestMessage& message, bool sign_body) const { 104 0 : if (!sign_body) { 105 0 : return SignatureConstants::get().HashedEmptyString; 106 0 : } 107 0 : auto& crypto_util = Envoy::Common::Crypto::UtilitySingleton::get(); 108 0 : const auto content_hash = message.body().length() > 0 109 0 : ? Hex::encode(crypto_util.getSha256Digest(message.body())) 110 0 : : SignatureConstants::get().HashedEmptyString; 111 0 : return content_hash; 112 0 : } 113 : 114 : } // namespace Aws 115 : } // namespace Common 116 : } // namespace Extensions 117 : } // namespace Envoy