1
#pragma once
2

            
3
#include "source/common/common/logger.h"
4
#include "source/common/common/matchers.h"
5
#include "source/common/common/utility.h"
6
#include "source/common/http/headers.h"
7
#include "source/common/singleton/const_singleton.h"
8
#include "source/extensions/common/aws/credentials_provider.h"
9
#include "source/extensions/common/aws/signer.h"
10

            
11
namespace Envoy {
12
namespace Extensions {
13
namespace Common {
14
namespace Aws {
15

            
16
class SignatureHeaderValues {
17
public:
18
  const Http::LowerCaseString ContentSha256{"x-amz-content-sha256"};
19
  const Http::LowerCaseString Date{"x-amz-date"};
20
  const Http::LowerCaseString SecurityToken{"x-amz-security-token"};
21
};
22

            
23
using SignatureHeaders = ConstSingleton<SignatureHeaderValues>;
24

            
25
class SignatureQueryParameterValues {
26
public:
27
  // Query string parameters require camel case
28
  static constexpr absl::string_view AmzAlgorithm = "X-Amz-Algorithm";
29
  static constexpr absl::string_view AmzCredential = "X-Amz-Credential";
30
  static constexpr absl::string_view AmzDate = "X-Amz-Date";
31
  static constexpr absl::string_view AmzRegionSet = "X-Amz-Region-Set";
32
  static constexpr absl::string_view AmzSecurityToken = "X-Amz-Security-Token";
33
  static constexpr absl::string_view AmzSignature = "X-Amz-Signature";
34
  static constexpr absl::string_view AmzSignedHeaders = "X-Amz-SignedHeaders";
35
  static constexpr absl::string_view AmzExpires = "X-Amz-Expires";
36
  // Expiration time of query parameter request, in seconds
37
  static constexpr uint16_t DefaultExpiration = 5;
38
};
39

            
40
class SignatureConstants {
41
public:
42
  static constexpr absl::string_view Aws4Request = "aws4_request";
43
  static constexpr absl::string_view HashedEmptyString =
44
      "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
45

            
46
  static constexpr absl::string_view LongDateFormat = "%Y%m%dT%H%M%SZ";
47
  static constexpr absl::string_view ShortDateFormat = "%Y%m%d";
48
  static constexpr absl::string_view UnsignedPayload = "UNSIGNED-PAYLOAD";
49
  static constexpr absl::string_view AuthorizationCredentialFormat = "{}/{}";
50
};
51

            
52
using AwsSigningHeaderMatcherVector = std::vector<envoy::type::matcher::v3::StringMatcher>;
53

            
54
/**
55
 * Implementation of the Signature V4 signing process.
56
 * See https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
57
 *
58
 * Query parameter support is implemented as per:
59
 * https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
60
 */
61
class SignerBaseImpl : public Signer, public Logger::Loggable<Logger::Id::aws> {
62
public:
63
  SignerBaseImpl(absl::string_view service_name, absl::string_view region,
64
                 const CredentialsProviderChainSharedPtr& credentials_provider_chain,
65
                 Server::Configuration::CommonFactoryContext& context,
66
                 const AwsSigningHeaderMatcherVector& exclude_matcher_config,
67
                 const AwsSigningHeaderMatcherVector& include_matcher_config,
68
                 const bool query_string = false,
69
                 const uint16_t expiration_time = SignatureQueryParameterValues::DefaultExpiration)
70
446
      : service_name_(service_name), region_(region),
71
446
        excluded_header_matchers_(defaultMatchers(context)),
72
446
        credentials_provider_chain_(credentials_provider_chain), query_string_(query_string),
73
446
        expiration_time_(expiration_time), time_source_(context.timeSource()),
74
446
        long_date_formatter_(std::string(SignatureConstants::LongDateFormat)),
75
446
        short_date_formatter_(std::string(SignatureConstants::ShortDateFormat)) {
76
516
    for (const auto& matcher : exclude_matcher_config) {
77
114
      excluded_header_matchers_.emplace_back(
78
114
          std::make_unique<Matchers::StringMatcherImpl>(matcher, context));
79
114
    }
80
446
    for (const auto& matcher : include_matcher_config) {
81
4
      included_header_matchers_.emplace_back(
82
4
          std::make_unique<Matchers::StringMatcherImpl>(matcher, context));
83
4
    }
84
446
  }
85

            
86
  absl::Status sign(Http::RequestMessage& message, bool sign_body = false,
87
                    const absl::string_view override_region = "") override;
88
  absl::Status sign(Http::RequestHeaderMap& headers, const std::string& content_hash,
89
                    const absl::string_view override_region = "") override;
90
  absl::Status signEmptyPayload(Http::RequestHeaderMap& headers,
91
                                const absl::string_view override_region = "") override;
92
  absl::Status signUnsignedPayload(Http::RequestHeaderMap& headers,
93
                                   const absl::string_view override_region = "") override;
94

            
95
  // Used to request notification when credentials are available from a pending credentials provider
96
  bool addCallbackIfCredentialsPending(CredentialsPendingCallback&& cb) override;
97

            
98
protected:
99
  std::string getRegion() const;
100

            
101
  std::string createContentHash(Http::RequestMessage& message, bool sign_body) const;
102

            
103
  virtual void addRegionHeader(Http::RequestHeaderMap& headers,
104
                               const absl::string_view override_region) const;
105
  virtual void addRegionQueryParam(Envoy::Http::Utility::QueryParamsMulti& query_params,
106
                                   const absl::string_view override_region) const;
107

            
108
  virtual absl::string_view getAlgorithmString() const PURE;
109

            
110
  virtual std::string createCredentialScope(const absl::string_view short_date,
111
                                            const absl::string_view override_region) const PURE;
112

            
113
  virtual std::string createStringToSign(const absl::string_view canonical_request,
114
                                         const absl::string_view long_date,
115
                                         const absl::string_view credential_scope) const PURE;
116

            
117
  virtual std::string createSignature(const absl::string_view access_key_id,
118
                                      const absl::string_view secret_access_key,
119
                                      const absl::string_view short_date,
120
                                      const absl::string_view string_to_sign,
121
                                      const absl::string_view override_region) const PURE;
122

            
123
  virtual std::string
124
  createAuthorizationHeader(const absl::string_view access_key_id,
125
                            const absl::string_view credential_scope,
126
                            const std::map<std::string, std::string>& canonical_headers,
127
                            const absl::string_view signature) const PURE;
128

            
129
  std::string createAuthorizationCredential(absl::string_view access_key_id,
130
                                            absl::string_view credential_scope) const;
131

            
132
  void createQueryParams(Envoy::Http::Utility::QueryParamsMulti& query_params,
133
                         const absl::string_view authorization_credential,
134
                         const absl::string_view long_date,
135
                         const absl::optional<std::string> session_token,
136
                         const std::map<std::string, std::string>& signed_headers,
137
                         const uint16_t expiration_time) const;
138

            
139
  void addRequiredHeaders(Http::RequestHeaderMap& headers, const std::string long_date,
140
                          const absl::optional<std::string> session_token,
141
                          const absl::string_view override_region);
142

            
143
  std::vector<Matchers::StringMatcherPtr>
144
446
  defaultMatchers(Server::Configuration::CommonFactoryContext& context) const {
145
446
    std::vector<Matchers::StringMatcherPtr> matcher_ptrs{};
146
1338
    for (const auto& header : default_excluded_headers_) {
147
1338
      envoy::type::matcher::v3::StringMatcher m;
148
1338
      m.set_exact(header);
149
1338
      matcher_ptrs.emplace_back(std::make_unique<Matchers::StringMatcherImpl>(m, context));
150
1338
    }
151
446
    return matcher_ptrs;
152
446
  }
153

            
154
  const std::string service_name_;
155
  const std::string region_;
156
  const std::vector<std::string> default_excluded_headers_ = {
157
      Http::Headers::get().ForwardedFor.get(), Http::Headers::get().ForwardedProto.get(),
158
      "x-amzn-trace-id"};
159
  std::vector<Matchers::StringMatcherPtr> excluded_header_matchers_;
160
  std::vector<Matchers::StringMatcherPtr> included_header_matchers_;
161
  CredentialsProviderChainSharedPtr credentials_provider_chain_;
162
  const bool query_string_;
163
  const uint16_t expiration_time_;
164
  TimeSource& time_source_;
165
  DateFormatter long_date_formatter_;
166
  DateFormatter short_date_formatter_;
167
  const std::string invalid_signature_ = "invalidSignature";
168
};
169

            
170
} // namespace Aws
171
} // namespace Common
172
} // namespace Extensions
173
} // namespace Envoy