1
#pragma once
2

            
3
#include "envoy/common/matchers.h"
4
#include "envoy/config/cluster/v3/cluster.pb.h"
5
#include "envoy/json/json_object.h"
6

            
7
#include "source/common/common/logger.h"
8

            
9
namespace Envoy {
10
namespace Extensions {
11
namespace Common {
12
namespace Aws {
13

            
14
class Utility : public Logger::Loggable<Logger::Id::aws> {
15
public:
16
  /**
17
   * Creates a canonicalized header map used in creating a AWS Signature V4 canonical request.
18
   * See https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
19
   * @param headers a header map to canonicalize.
20
   * @param excluded_headers a list of string matchers to exclude a given header from signing.
21
   * @param included_headers a list of string matchers to include a given header from signing.
22
   * included_headers will take precedence over excluded_headers and if included_headers is
23
   * non-empty, only headers that match included_headers will be signed.
24
   * @return a std::map of canonicalized headers to be used in building a canonical request.
25
   */
26
  static std::map<std::string, std::string>
27
  canonicalizeHeaders(const Http::RequestHeaderMap& headers,
28
                      const std::vector<Matchers::StringMatcherPtr>& excluded_headers,
29
                      const std::vector<Matchers::StringMatcherPtr>& included_headers);
30

            
31
  /**
32
   * Creates an AWS Signature V4 canonical request string.
33
   * See https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
34
   * @param method the HTTP request method.
35
   * @param path the request path.
36
   * @param canonical_headers the pre-canonicalized request headers.
37
   * @param content_hash the hashed request body.
38
   * @param should_normalize_uri_path whether we should normalize the path string
39
   * @param use_double_uri_uncode whether we should perform an additional uri encode
40
   * @return the canonicalized request string.
41
   */
42
  static std::string
43
  createCanonicalRequest(absl::string_view method, absl::string_view path,
44
                         const std::map<std::string, std::string>& canonical_headers,
45
                         absl::string_view content_hash, bool should_normalize_uri_path,
46
                         bool use_double_uri_uncode);
47

            
48
  /**
49
   * Normalizes the path string based on AWS requirements.
50
   * See step 2 in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
51
   * @param headers header map
52
   */
53
  static std::string normalizePath(absl::string_view original_path);
54

            
55
  /**
56
   * URI encodes the path string based on AWS requirements.
57
   * See step 2 in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
58
   * @param headers header map
59
   */
60
  static std::string uriEncodePath(absl::string_view original_path);
61

            
62
  /**
63
   * Normalizes the query string based on AWS requirements.
64
   * See step 3 in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
65
   * @param query_string the query string from the HTTP request.
66
   * @return the canonicalized query string.
67
   */
68
  static std::string canonicalizeQueryString(absl::string_view query_string);
69

            
70
  /**
71
   * URI encodes query component while preserving %2B semantics.
72
   * %2B remains as literal +, raw + becomes %20 (space).
73
   * @param original the original string (may contain percent-encoded sequences).
74
   * @return the properly encoded string.
75
   */
76
  static std::string encodeQueryComponentPreservingPlus(absl::string_view original);
77

            
78
  /**
79
   * Get the semicolon-delimited string of canonical header names.
80
   * @param canonical_headers the pre-canonicalized request headers.
81
   * @return the header names as a semicolon-delimited string.
82
   */
83
  static std::string
84
  joinCanonicalHeaderNames(const std::map<std::string, std::string>& canonical_headers);
85

            
86
  /**
87
   * Get the IAM Roles Anywhere Service endpoint for a given region:
88
   * rolesanywhere.<region>.amazonaws.com See:
89
   * https://docs.aws.amazon.com/rolesanywhere/latest/userguide/authentication-sign-process.html#authentication-task1
90
   * @param trust_anchor_arn The configured roles anywhere trust anchor arn for the region to be
91
   * extracted from
92
   * @return an sts endpoint url.
93
   */
94

            
95
  static std::string getRolesAnywhereEndpoint(const std::string& trust_anchor_arn);
96

            
97
  /**
98
   * Get the Security Token Service endpoint for a given region: sts.<region>.amazonaws.com
99
   * See: https://docs.aws.amazon.com/general/latest/gr/rande.html#sts_region
100
   * @param region An AWS region.
101
   * @return an sts endpoint url.
102
   */
103
  static std::string getSTSEndpoint(absl::string_view region);
104

            
105
  /**
106
   * @brief Creates the prototype for a static cluster towards a credentials provider
107
   *        to fetch the credentials using http async client.
108
   *
109
   * @param cluster_name a name for credentials provider cluster
110
   * @param cluster_type STATIC or STRICT_DNS or LOGICAL_DNS etc
111
   * @param uri provider's IP (STATIC cluster) or URL (STRICT_DNS). Will use port 80 if the port is
112
   * not specified in the uri or no matching cluster is found.
113
   * @return the created cluster prototype
114
   * @return false if failed to add the cluster
115
   */
116
  static envoy::config::cluster::v3::Cluster
117
  createInternalClusterStatic(absl::string_view cluster_name,
118
                              const envoy::config::cluster::v3::Cluster::DiscoveryType cluster_type,
119
                              absl::string_view uri);
120

            
121
  /**
122
   * @brief Retrieve an environment variable if set, otherwise return default_value
123
   *
124
   * @param variable_name Environment variable.
125
   * @param default_value Value to be returned if environment variable is not set.
126
   * @return The evaluation result.
127
   */
128
  static std::string getEnvironmentVariableOrDefault(const std::string& variable_name,
129
                                                     const std::string& default_value);
130

            
131
  /**
132
   * @brief Given a profile name and a file containing profile elements, such as config or
133
   * credentials, retrieve all elements in the elements map
134
   *
135
   * @param profile_file path to the file to search for elements.
136
   * @param profile_name the profile section to search.
137
   * @param elements a hash map of elements to search for. values will be replaced if found.
138
   * @return true if profile file could be read and searched.
139
   * @return false if profile file could not be read.
140
   */
141

            
142
  static bool
143
  resolveProfileElementsFromString(const std::string& string_data, const std::string& profile_name,
144
                                   absl::flat_hash_map<std::string, std::string>& elements);
145

            
146
  static bool
147
  resolveProfileElementsFromFile(const std::string& profile_file, const std::string& profile_name,
148
                                 absl::flat_hash_map<std::string, std::string>& elements);
149

            
150
  static bool
151
  resolveProfileElementsFromStream(std::istream& stream, const std::string& profile_name,
152
                                   absl::flat_hash_map<std::string, std::string>& elements);
153

            
154
  /**
155
   * @brief Return the path of AWS credential file, following environment variable expansions
156
   *
157
   * @return File path of the AWS credential file.
158
   */
159
  static std::string getCredentialFilePath();
160

            
161
  /**
162
   * @brief Return the path of AWS config file, following environment variable expansions
163
   *
164
   * @return File path of the AWS config file.
165
   */
166
  static std::string getConfigFilePath();
167

            
168
  /**
169
   * @brief Return the AWS profile string within a config file, following environment variable
170
   * expansions
171
   *
172
   * @return Name of the profile string.
173
   */
174
  static std::string getConfigProfileName();
175

            
176
  /**
177
   * @brief Return the AWS profile string within a credentials file, following environment variable
178
   * expansions
179
   *
180
   * @return Name of the profile string.
181
   */
182
  static std::string getCredentialProfileName();
183

            
184
  /**
185
   * @brief Return a string value from a json object key, or a default it not found. Does not throw
186
   * exceptions.
187
   *
188
   * @return String value or default
189
   */
190
  static std::string getStringFromJsonOrDefault(Json::ObjectSharedPtr json_object,
191
                                                const std::string& string_value,
192
                                                const std::string& string_default);
193

            
194
  /**
195
   * @brief Return an integer value from a json object key, or a default it not found. Does not
196
   * throw exceptions. Will correctly handle json exponent formatted integers as well as standard
197
   * format.
198
   *
199
   * @return Integer value or default
200
   */
201
  static int64_t getIntegerFromJsonOrDefault(Json::ObjectSharedPtr json_object,
202
                                             const std::string& integer_value,
203
                                             const int64_t integer_default);
204

            
205
  /**
206
   * @brief Given a service name, should we uri encode the path
207
   * Logic for this is based on inspection of smithy definitions and currently only includes s3
208
   * services
209
   * @return boolean
210
   */
211

            
212
  static bool useDoubleUriEncode(const std::string service_name);
213

            
214
  /**
215
   * @brief Given a service name, should we normalize the path
216
   * Logic for this is based on inspection of smithy definitions and currently only includes s3
217
   * services
218
   * @return boolean
219
   */
220
  static bool shouldNormalizeUriPath(const std::string service_name);
221

            
222
  /**
223
   * Checks if a URI path is already percent-encoded according to RFC 3986.
224
   * Returns false if any character that should be percent-encoded is found unencoded.
225
   * @param path the URI path to check.
226
   * @return true if the path is already properly encoded, false otherwise.
227
   */
228
  static bool isUriPathEncoded(absl::string_view path);
229

            
230
private:
231
  /**
232
   * Helper method to encode a character based on reserved character rules.
233
   * @param c the character to encode.
234
   * @param result the string to append the encoded result to.
235
   */
236
  static void encodeCharacter(unsigned char c, std::string& result);
237
};
238

            
239
} // namespace Aws
240
} // namespace Common
241
} // namespace Extensions
242
} // namespace Envoy