1
#include "source/extensions/common/aws/credential_providers/credentials_file_credentials_provider.h"
2

            
3
#include "envoy/server/factory_context.h"
4

            
5
#include "source/extensions/common/aws/utility.h"
6

            
7
namespace Envoy {
8
namespace Extensions {
9
namespace Common {
10
namespace Aws {
11

            
12
CredentialsFileCredentialsProvider::CredentialsFileCredentialsProvider(
13
    Server::Configuration::ServerFactoryContext& context,
14
    const envoy::extensions::common::aws::v3::CredentialsFileCredentialProvider&
15
        credential_file_config)
16
109
    : context_(context), profile_("") {
17

            
18
109
  if (credential_file_config.has_credentials_data_source()) {
19
5
    auto provider_or_error_ = Config::DataSource::DataSourceProvider<std::string>::create(
20
5
        credential_file_config.credentials_data_source(), context.mainThreadDispatcher(),
21
5
        context.threadLocal(), context.api(), false,
22
5
        [](absl::string_view data) { return std::make_shared<std::string>(data); }, 4096);
23
5
    if (provider_or_error_.ok()) {
24
3
      credential_file_data_source_provider_ = std::move(provider_or_error_.value());
25
3
      if (credential_file_config.credentials_data_source().has_watched_directory()) {
26
1
        has_watched_directory_ = true;
27
1
      }
28
5
    } else {
29
2
      ENVOY_LOG(info, "Invalid credential file data source");
30
2
      credential_file_data_source_provider_.reset();
31
2
    }
32
5
  }
33
109
  if (!credential_file_config.profile().empty()) {
34
7
    profile_ = credential_file_config.profile();
35
7
  }
36
109
}
37

            
38
23
bool CredentialsFileCredentialsProvider::needsRefresh() {
39
23
  return has_watched_directory_
40
23
             ? true
41
23
             : context_.api().timeSource().systemTime() - last_updated_ > REFRESH_INTERVAL;
42
23
}
43

            
44
21
void CredentialsFileCredentialsProvider::refresh() {
45
21
  auto profile = profile_.empty() ? Utility::getCredentialProfileName() : profile_;
46

            
47
21
  ENVOY_LOG(debug, "Getting AWS credentials from the credentials file");
48

            
49
21
  std::string credential_file_data, credential_file_path;
50

            
51
  // Use data source if provided, otherwise read from default AWS credential file path
52
21
  if (credential_file_data_source_provider_.has_value() &&
53
21
      credential_file_data_source_provider_.value()->data() != nullptr) {
54
3
    credential_file_data = *credential_file_data_source_provider_.value()->data();
55
3
    credential_file_path = "<config datasource>";
56
18
  } else {
57
18
    credential_file_path = Utility::getCredentialFilePath();
58
18
    auto credential_file = context_.api().fileSystem().fileReadToEnd(credential_file_path);
59
18
    if (credential_file.ok()) {
60
13
      credential_file_data = credential_file.value();
61
16
    } else {
62
5
      ENVOY_LOG(debug, "Unable to read from credential file {}", credential_file_path);
63
      // Update last_updated_ now so that even if this function returns before successfully
64
      // extracting credentials, this function won't be called again until after the
65
      // REFRESH_INTERVAL. This prevents envoy from attempting and failing to read the credentials
66
      // file on every request if there are errors extracting credentials from it (e.g. if the
67
      // credentials file doesn't exist).
68
5
      last_updated_ = context_.api().timeSource().systemTime();
69
5
      return;
70
5
    }
71
18
  }
72
16
  ENVOY_LOG(debug, "Credentials file path = {}, profile name = {}", credential_file_path, profile);
73

            
74
16
  extractCredentials(credential_file_data, profile);
75
16
}
76

            
77
void CredentialsFileCredentialsProvider::extractCredentials(absl::string_view credentials_string,
78
16
                                                            absl::string_view profile) {
79

            
80
16
  std::string access_key_id, secret_access_key, session_token;
81

            
82
  // TODO: @nbaws optimize out this flat hash map creation
83

            
84
16
  absl::flat_hash_map<std::string, std::string> elements = {
85
16
      {AWS_ACCESS_KEY_ID, ""}, {AWS_SECRET_ACCESS_KEY, ""}, {AWS_SESSION_TOKEN, ""}};
86
16
  absl::flat_hash_map<std::string, std::string>::iterator it;
87
16
  Utility::resolveProfileElementsFromString(credentials_string.data(), profile.data(), elements);
88
  // if profile file fails to load, or these elements are not found in the profile, their values
89
  // will remain blank when retrieving them from the hash map
90
16
  access_key_id = elements.find(AWS_ACCESS_KEY_ID)->second;
91
16
  secret_access_key = elements.find(AWS_SECRET_ACCESS_KEY)->second;
92
16
  session_token = elements.find(AWS_SESSION_TOKEN)->second;
93

            
94
16
  if (access_key_id.empty() || secret_access_key.empty()) {
95
    // Return empty credentials if we're unable to retrieve from profile
96
4
    cached_credentials_ = Credentials();
97
12
  } else {
98
12
    ENVOY_LOG(debug, "Found following AWS credentials for profile '{}': {}={}, {}={}, {}={}",
99
12
              profile, AWS_ACCESS_KEY_ID, access_key_id, AWS_SECRET_ACCESS_KEY,
100
12
              secret_access_key.empty() ? "" : "*****", AWS_SESSION_TOKEN,
101
12
              session_token.empty() ? "" : "*****");
102

            
103
12
    cached_credentials_ = Credentials(access_key_id, secret_access_key, session_token);
104
12
  }
105
16
  last_updated_ = context_.api().timeSource().systemTime();
106
16
}
107

            
108
} // namespace Aws
109
} // namespace Common
110
} // namespace Extensions
111
} // namespace Envoy