LCOV - code coverage report
Current view: top level - source/extensions/common/aws - credentials_provider_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 132 584 22.6 %
Date: 2024-01-05 06:35:25 Functions: 18 44 40.9 %

          Line data    Source code
       1             : #include "source/extensions/common/aws/credentials_provider_impl.h"
       2             : 
       3             : #include <fstream>
       4             : 
       5             : #include "envoy/common/exception.h"
       6             : 
       7             : #include "source/common/common/lock_guard.h"
       8             : #include "source/common/http/message_impl.h"
       9             : #include "source/common/http/utility.h"
      10             : #include "source/common/json/json_loader.h"
      11             : #include "source/common/runtime/runtime_features.h"
      12             : #include "source/common/tracing/http_tracer_impl.h"
      13             : #include "source/extensions/common/aws/utility.h"
      14             : 
      15             : #include "absl/strings/str_format.h"
      16             : #include "absl/strings/str_split.h"
      17             : 
      18             : namespace Envoy {
      19             : namespace Extensions {
      20             : namespace Common {
      21             : namespace Aws {
      22             : 
      23             : namespace {
      24             : 
      25             : constexpr char AWS_ACCESS_KEY_ID[] = "AWS_ACCESS_KEY_ID";
      26             : constexpr char AWS_SECRET_ACCESS_KEY[] = "AWS_SECRET_ACCESS_KEY";
      27             : constexpr char AWS_SESSION_TOKEN[] = "AWS_SESSION_TOKEN";
      28             : constexpr char AWS_ROLE_ARN[] = "AWS_ROLE_ARN";
      29             : constexpr char AWS_WEB_IDENTITY_TOKEN_FILE[] = "AWS_WEB_IDENTITY_TOKEN_FILE";
      30             : constexpr char AWS_ROLE_SESSION_NAME[] = "AWS_ROLE_SESSION_NAME";
      31             : 
      32             : constexpr char CREDENTIALS[] = "Credentials";
      33             : constexpr char ACCESS_KEY_ID[] = "AccessKeyId";
      34             : constexpr char SECRET_ACCESS_KEY[] = "SecretAccessKey";
      35             : constexpr char TOKEN[] = "Token";
      36             : constexpr char EXPIRATION[] = "Expiration";
      37             : constexpr char EXPIRATION_FORMAT[] = "%E4Y-%m-%dT%H:%M:%S%z";
      38             : constexpr char TRUE[] = "true";
      39             : constexpr char SESSION_TOKEN[] = "SessionToken";
      40             : constexpr char WEB_IDENTITY_RESPONSE_ELEMENT[] = "AssumeRoleWithWebIdentityResponse";
      41             : constexpr char WEB_IDENTITY_RESULT_ELEMENT[] = "AssumeRoleWithWebIdentityResult";
      42             : 
      43             : constexpr char AWS_SHARED_CREDENTIALS_FILE[] = "AWS_SHARED_CREDENTIALS_FILE";
      44             : constexpr char AWS_PROFILE[] = "AWS_PROFILE";
      45             : constexpr char DEFAULT_AWS_SHARED_CREDENTIALS_FILE[] = "~/.aws/credentials";
      46             : constexpr char DEFAULT_AWS_PROFILE[] = "default";
      47             : 
      48             : constexpr char AWS_CONTAINER_CREDENTIALS_RELATIVE_URI[] = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
      49             : constexpr char AWS_CONTAINER_CREDENTIALS_FULL_URI[] = "AWS_CONTAINER_CREDENTIALS_FULL_URI";
      50             : constexpr char AWS_CONTAINER_AUTHORIZATION_TOKEN[] = "AWS_CONTAINER_AUTHORIZATION_TOKEN";
      51             : constexpr char AWS_EC2_METADATA_DISABLED[] = "AWS_EC2_METADATA_DISABLED";
      52             : 
      53             : constexpr std::chrono::hours REFRESH_INTERVAL{1};
      54             : constexpr std::chrono::seconds REFRESH_GRACE_PERIOD{5};
      55             : constexpr char EC2_METADATA_HOST[] = "169.254.169.254:80";
      56             : constexpr char CONTAINER_METADATA_HOST[] = "169.254.170.2:80";
      57             : constexpr char EC2_IMDS_TOKEN_RESOURCE[] = "/latest/api/token";
      58             : constexpr char EC2_IMDS_TOKEN_HEADER[] = "X-aws-ec2-metadata-token";
      59             : constexpr char EC2_IMDS_TOKEN_TTL_HEADER[] = "X-aws-ec2-metadata-token-ttl-seconds";
      60             : constexpr char EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE[] = "21600";
      61             : constexpr char SECURITY_CREDENTIALS_PATH[] = "/latest/meta-data/iam/security-credentials";
      62             : 
      63             : constexpr char EC2_METADATA_CLUSTER[] = "ec2_instance_metadata_server_internal";
      64             : constexpr char CONTAINER_METADATA_CLUSTER[] = "ecs_task_metadata_server_internal";
      65             : constexpr char STS_TOKEN_CLUSTER[] = "sts_token_service_internal";
      66             : 
      67             : } // namespace
      68             : 
      69           5 : Credentials EnvironmentCredentialsProvider::getCredentials() {
      70           5 :   ENVOY_LOG(debug, "Getting AWS credentials from the environment");
      71             : 
      72           5 :   const auto access_key_id = absl::NullSafeStringView(std::getenv(AWS_ACCESS_KEY_ID));
      73           5 :   if (access_key_id.empty()) {
      74           5 :     return Credentials();
      75           5 :   }
      76             : 
      77           0 :   const auto secret_access_key = absl::NullSafeStringView(std::getenv(AWS_SECRET_ACCESS_KEY));
      78           0 :   const auto session_token = absl::NullSafeStringView(std::getenv(AWS_SESSION_TOKEN));
      79             : 
      80           0 :   ENVOY_LOG(debug, "Found following AWS credentials in the environment: {}={}, {}={}, {}={}",
      81           0 :             AWS_ACCESS_KEY_ID, access_key_id, AWS_SECRET_ACCESS_KEY,
      82           0 :             secret_access_key.empty() ? "" : "*****", AWS_SESSION_TOKEN,
      83           0 :             session_token.empty() ? "" : "*****");
      84             : 
      85           0 :   return Credentials(access_key_id, secret_access_key, session_token);
      86           5 : }
      87             : 
      88          10 : void CachedCredentialsProviderBase::refreshIfNeeded() {
      89          10 :   const Thread::LockGuard lock(lock_);
      90          10 :   if (needsRefresh()) {
      91          10 :     refresh();
      92          10 :   }
      93          10 : }
      94             : 
      95             : // TODO(suniltheta): The field context is of type ServerFactoryContextOptRef so that an
      96             : // optional empty value can be set. Especially in aws iam plugin the cluster manager
      97             : // obtained from server factory context object is not fully initialized due to the
      98             : // reasons explained in https://github.com/envoyproxy/envoy/issues/27586 which cannot
      99             : // utilize http async client here to fetch AWS credentials. For time being if context
     100             : // is empty then will use libcurl to fetch the credentials.
     101             : MetadataCredentialsProviderBase::MetadataCredentialsProviderBase(
     102             :     Api::Api& api, ServerFactoryContextOptRef context,
     103             :     const CurlMetadataFetcher& fetch_metadata_using_curl,
     104             :     CreateMetadataFetcherCb create_metadata_fetcher_cb, absl::string_view cluster_name,
     105             :     const envoy::config::cluster::v3::Cluster::DiscoveryType cluster_type, absl::string_view uri)
     106             :     : api_(api), context_(context), fetch_metadata_using_curl_(fetch_metadata_using_curl),
     107             :       create_metadata_fetcher_cb_(create_metadata_fetcher_cb),
     108             :       cluster_name_(std::string(cluster_name)), cluster_type_(cluster_type), uri_(std::string(uri)),
     109             :       cache_duration_(getCacheDuration()),
     110           6 :       debug_name_(absl::StrCat("Fetching aws credentials from cluster=", cluster_name)) {
     111           6 :   if (context_) {
     112           6 :     context_->mainThreadDispatcher().post([this]() {
     113           6 :       if (!Utility::addInternalClusterStatic(context_->clusterManager(), cluster_name_,
     114           6 :                                              cluster_type_, uri_)) {
     115           0 :         ENVOY_LOG(critical,
     116           0 :                   "Failed to add [STATIC cluster = {} with address = {}] or cluster not found",
     117           0 :                   cluster_name_, uri_);
     118           0 :         return;
     119           0 :       }
     120           6 :     });
     121             : 
     122           6 :     tls_ = ThreadLocal::TypedSlot<ThreadLocalCredentialsCache>::makeUnique(context_->threadLocal());
     123           6 :     tls_->set(
     124           6 :         [](Envoy::Event::Dispatcher&) { return std::make_shared<ThreadLocalCredentialsCache>(); });
     125             : 
     126           6 :     cache_duration_timer_ = context_->mainThreadDispatcher().createTimer([this]() -> void {
     127           0 :       if (useHttpAsyncClient()) {
     128           0 :         const Thread::LockGuard lock(lock_);
     129           0 :         refresh();
     130           0 :       }
     131           0 :     });
     132             : 
     133           6 :     if (useHttpAsyncClient()) {
     134             :       // Register with init_manager, force the listener to wait for fetching (refresh).
     135           0 :       init_target_ =
     136           0 :           std::make_unique<Init::TargetImpl>(debug_name_, [this]() -> void { refresh(); });
     137           0 :       context_->initManager().add(*init_target_);
     138           0 :     }
     139           6 :   }
     140           6 : }
     141             : 
     142           5 : Credentials MetadataCredentialsProviderBase::getCredentials() {
     143           5 :   refreshIfNeeded();
     144           5 :   if (useHttpAsyncClient() && context_ && tls_) {
     145             :     // If server factor context was supplied then we would have thread local slot initialized.
     146           0 :     return *(*tls_)->credentials_.get();
     147           5 :   } else {
     148           5 :     return cached_credentials_;
     149           5 :   }
     150           5 : }
     151             : 
     152           6 : std::chrono::seconds MetadataCredentialsProviderBase::getCacheDuration() {
     153           6 :   return std::chrono::seconds(
     154           6 :       REFRESH_INTERVAL * 60 * 60 -
     155           6 :       REFRESH_GRACE_PERIOD /*TODO: Add jitter from context.api().randomGenerator()*/);
     156           6 : }
     157             : 
     158           0 : void MetadataCredentialsProviderBase::handleFetchDone() {
     159           0 :   if (useHttpAsyncClient() && context_) {
     160           0 :     if (init_target_) {
     161           0 :       init_target_->ready();
     162           0 :       init_target_.reset();
     163           0 :     }
     164           0 :     if (cache_duration_timer_ && !cache_duration_timer_->enabled()) {
     165           0 :       cache_duration_timer_->enableTimer(cache_duration_);
     166           0 :     }
     167           0 :   }
     168           0 : }
     169             : 
     170             : void MetadataCredentialsProviderBase::setCredentialsToAllThreads(
     171           0 :     CredentialsConstUniquePtr&& creds) {
     172           0 :   CredentialsConstSharedPtr shared_credentials = std::move(creds);
     173           0 :   if (tls_) {
     174           0 :     tls_->runOnAllThreads([shared_credentials](OptRef<ThreadLocalCredentialsCache> obj) {
     175           0 :       obj->credentials_ = shared_credentials;
     176           0 :     });
     177           0 :   }
     178           0 : }
     179             : 
     180          16 : bool MetadataCredentialsProviderBase::useHttpAsyncClient() {
     181          16 :   return Runtime::runtimeFeatureEnabled(
     182          16 :       "envoy.reloadable_features.use_http_client_to_fetch_aws_credentials");
     183          16 : }
     184             : 
     185           5 : bool CredentialsFileCredentialsProvider::needsRefresh() {
     186           5 :   return api_.timeSource().systemTime() - last_updated_ > REFRESH_INTERVAL;
     187           5 : }
     188             : 
     189             : std::string getEnvironmentVariableOrDefault(const std::string& variable_name,
     190          10 :                                             const std::string& default_value) {
     191          10 :   const char* value = getenv(variable_name.c_str());
     192          10 :   return (value != nullptr) && (value[0] != '\0') ? value : default_value;
     193          10 : }
     194             : 
     195           5 : void CredentialsFileCredentialsProvider::refresh() {
     196           5 :   ENVOY_LOG(debug, "Getting AWS credentials from the credentials file");
     197             : 
     198           5 :   const auto credentials_file = getEnvironmentVariableOrDefault(
     199           5 :       AWS_SHARED_CREDENTIALS_FILE, DEFAULT_AWS_SHARED_CREDENTIALS_FILE);
     200           5 :   const auto profile = getEnvironmentVariableOrDefault(AWS_PROFILE, DEFAULT_AWS_PROFILE);
     201             : 
     202           5 :   extractCredentials(credentials_file, profile);
     203           5 : }
     204             : 
     205             : void CredentialsFileCredentialsProvider::extractCredentials(const std::string& credentials_file,
     206           5 :                                                             const std::string& profile) {
     207             :   // Update last_updated_ now so that even if this function returns before successfully
     208             :   // extracting credentials, this function won't be called again until after the REFRESH_INTERVAL.
     209             :   // This prevents envoy from attempting and failing to read the credentials file on every request
     210             :   // if there are errors extracting credentials from it (e.g. if the credentials file doesn't
     211             :   // exist).
     212           5 :   last_updated_ = api_.timeSource().systemTime();
     213             : 
     214           5 :   std::ifstream file(credentials_file);
     215           5 :   if (!file) {
     216           5 :     ENVOY_LOG(debug, "Error opening credentials file {}", credentials_file);
     217           5 :     return;
     218           5 :   }
     219             : 
     220           0 :   std::string access_key_id, secret_access_key, session_token;
     221           0 :   const auto profile_start = absl::StrFormat("[%s]", profile);
     222             : 
     223           0 :   bool found_profile = false;
     224           0 :   std::string line;
     225           0 :   while (std::getline(file, line)) {
     226           0 :     line = std::string(StringUtil::trim(line));
     227           0 :     if (line.empty()) {
     228           0 :       continue;
     229           0 :     }
     230             : 
     231           0 :     if (line == profile_start) {
     232           0 :       found_profile = true;
     233           0 :       continue;
     234           0 :     }
     235             : 
     236           0 :     if (found_profile) {
     237             :       // Stop reading once we find the start of the next profile.
     238           0 :       if (absl::StartsWith(line, "[")) {
     239           0 :         break;
     240           0 :       }
     241             : 
     242           0 :       std::vector<std::string> parts = absl::StrSplit(line, absl::MaxSplits('=', 1));
     243           0 :       if (parts.size() == 2) {
     244           0 :         const auto key = StringUtil::toUpper(StringUtil::trim(parts[0]));
     245           0 :         const auto val = StringUtil::trim(parts[1]);
     246           0 :         if (key == AWS_ACCESS_KEY_ID) {
     247           0 :           access_key_id = val;
     248           0 :         } else if (key == AWS_SECRET_ACCESS_KEY) {
     249           0 :           secret_access_key = val;
     250           0 :         } else if (key == AWS_SESSION_TOKEN) {
     251           0 :           session_token = val;
     252           0 :         }
     253           0 :       }
     254           0 :     }
     255           0 :   }
     256             : 
     257           0 :   ENVOY_LOG(debug, "Found following AWS credentials for profile '{}' in {}: {}={}, {}={}, {}={}",
     258           0 :             profile, credentials_file, AWS_ACCESS_KEY_ID, access_key_id, AWS_SECRET_ACCESS_KEY,
     259           0 :             secret_access_key.empty() ? "" : "*****", AWS_SESSION_TOKEN,
     260           0 :             session_token.empty() ? "" : "*****");
     261             : 
     262           0 :   cached_credentials_ = Credentials(access_key_id, secret_access_key, session_token);
     263           0 :   last_updated_ = api_.timeSource().systemTime();
     264           0 : }
     265             : 
     266             : InstanceProfileCredentialsProvider::InstanceProfileCredentialsProvider(
     267             :     Api::Api& api, ServerFactoryContextOptRef context,
     268             :     const CurlMetadataFetcher& fetch_metadata_using_curl,
     269             :     CreateMetadataFetcherCb create_metadata_fetcher_cb, absl::string_view cluster_name)
     270             :     : MetadataCredentialsProviderBase(
     271             :           api, context, fetch_metadata_using_curl, create_metadata_fetcher_cb, cluster_name,
     272           6 :           envoy::config::cluster::v3::Cluster::STATIC /*cluster_type*/, EC2_METADATA_HOST) {}
     273             : 
     274           5 : bool InstanceProfileCredentialsProvider::needsRefresh() {
     275           5 :   return api_.timeSource().systemTime() - last_updated_ > REFRESH_INTERVAL;
     276           5 : }
     277             : 
     278           5 : void InstanceProfileCredentialsProvider::refresh() {
     279           5 :   ENVOY_LOG(debug, "Getting AWS credentials from the EC2MetadataService");
     280             : 
     281             :   // First request for a session TOKEN so that we can call EC2MetadataService securely.
     282             :   // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
     283           5 :   Http::RequestMessageImpl token_req_message;
     284           5 :   token_req_message.headers().setScheme(Http::Headers::get().SchemeValues.Http);
     285           5 :   token_req_message.headers().setMethod(Http::Headers::get().MethodValues.Put);
     286           5 :   token_req_message.headers().setHost(EC2_METADATA_HOST);
     287           5 :   token_req_message.headers().setPath(EC2_IMDS_TOKEN_RESOURCE);
     288           5 :   token_req_message.headers().setCopy(Http::LowerCaseString(EC2_IMDS_TOKEN_TTL_HEADER),
     289           5 :                                       EC2_IMDS_TOKEN_TTL_DEFAULT_VALUE);
     290             : 
     291           5 :   if (!useHttpAsyncClient() || !context_) {
     292             :     // Using curl to fetch the AWS credentials where we first get the token.
     293           5 :     const auto token_string = fetch_metadata_using_curl_(token_req_message);
     294           5 :     if (token_string) {
     295           0 :       ENVOY_LOG(debug, "Obtained token to make secure call to EC2MetadataService");
     296           0 :       fetchInstanceRole(std::move(token_string.value()));
     297           5 :     } else {
     298           5 :       ENVOY_LOG(warn,
     299           5 :                 "Failed to get token from EC2MetadataService, falling back to less secure way");
     300           5 :       fetchInstanceRole(std::move(""));
     301           5 :     }
     302           5 :   } else {
     303             :     // Stop any existing timer.
     304           0 :     if (cache_duration_timer_ && cache_duration_timer_->enabled()) {
     305           0 :       cache_duration_timer_->disableTimer();
     306           0 :     }
     307             :     // Using Http async client to fetch the AWS credentials where we first get the token.
     308           0 :     if (!metadata_fetcher_) {
     309           0 :       metadata_fetcher_ = create_metadata_fetcher_cb_(context_->clusterManager(), clusterName());
     310           0 :     } else {
     311           0 :       metadata_fetcher_->cancel(); // Cancel if there is any inflight request.
     312           0 :     }
     313           0 :     on_async_fetch_cb_ = [this](const std::string&& arg) {
     314           0 :       return this->fetchInstanceRoleAsync(std::move(arg));
     315           0 :     };
     316           0 :     continue_on_async_fetch_failure_ = true;
     317           0 :     continue_on_async_fetch_failure_reason_ = "Token fetch failed so fall back to less secure way";
     318           0 :     metadata_fetcher_->fetch(token_req_message, Tracing::NullSpan::instance(), *this);
     319           0 :   }
     320           5 : }
     321             : 
     322             : void InstanceProfileCredentialsProvider::fetchInstanceRole(const std::string&& token_string,
     323           5 :                                                            bool async /*default = false*/) {
     324             :   // Discover the Role of this instance.
     325           5 :   Http::RequestMessageImpl message;
     326           5 :   message.headers().setScheme(Http::Headers::get().SchemeValues.Http);
     327           5 :   message.headers().setMethod(Http::Headers::get().MethodValues.Get);
     328           5 :   message.headers().setHost(EC2_METADATA_HOST);
     329           5 :   message.headers().setPath(SECURITY_CREDENTIALS_PATH);
     330           5 :   if (!token_string.empty()) {
     331           0 :     message.headers().setCopy(Http::LowerCaseString(EC2_IMDS_TOKEN_HEADER),
     332           0 :                               StringUtil::trim(token_string));
     333           0 :   }
     334             : 
     335           5 :   if (!async) {
     336             :     // Using curl to fetch the Instance Role.
     337           5 :     const auto instance_role_string = fetch_metadata_using_curl_(message);
     338           5 :     if (!instance_role_string) {
     339           5 :       ENVOY_LOG(error, "Could not retrieve credentials listing from the EC2MetadataService");
     340           5 :       return;
     341           5 :     }
     342           0 :     fetchCredentialFromInstanceRole(std::move(instance_role_string.value()),
     343           0 :                                     std::move(token_string));
     344           0 :   } else {
     345             :     // Using Http async client to fetch the Instance Role.
     346           0 :     metadata_fetcher_->cancel(); // Cancel if there is any inflight request.
     347           0 :     on_async_fetch_cb_ = [this, token_string = std::move(token_string)](const std::string&& arg) {
     348           0 :       return this->fetchCredentialFromInstanceRoleAsync(std::move(arg), std::move(token_string));
     349           0 :     };
     350           0 :     metadata_fetcher_->fetch(message, Tracing::NullSpan::instance(), *this);
     351           0 :   }
     352           5 : }
     353             : 
     354             : void InstanceProfileCredentialsProvider::fetchCredentialFromInstanceRole(
     355             :     const std::string&& instance_role, const std::string&& token_string,
     356           0 :     bool async /*default = false*/) {
     357             : 
     358           0 :   if (instance_role.empty()) {
     359           0 :     ENVOY_LOG(error, "No roles found to fetch AWS credentials from the EC2MetadataService");
     360           0 :     if (async) {
     361           0 :       handleFetchDone();
     362           0 :     }
     363           0 :     return;
     364           0 :   }
     365           0 :   const auto instance_role_list = StringUtil::splitToken(StringUtil::trim(instance_role), "\n");
     366           0 :   if (instance_role_list.empty()) {
     367           0 :     ENVOY_LOG(error, "No roles found to fetch AWS credentials from the EC2MetadataService");
     368           0 :     if (async) {
     369           0 :       handleFetchDone();
     370           0 :     }
     371           0 :     return;
     372           0 :   }
     373           0 :   ENVOY_LOG(debug, "AWS credentials list:\n{}", instance_role);
     374             : 
     375             :   // Only one Role can be associated with an instance:
     376             :   // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
     377           0 :   const auto credential_path =
     378           0 :       std::string(SECURITY_CREDENTIALS_PATH) + "/" +
     379           0 :       std::string(instance_role_list[0].data(), instance_role_list[0].size());
     380           0 :   ENVOY_LOG(debug, "AWS credentials path: {}", credential_path);
     381             : 
     382           0 :   Http::RequestMessageImpl message;
     383           0 :   message.headers().setScheme(Http::Headers::get().SchemeValues.Http);
     384           0 :   message.headers().setMethod(Http::Headers::get().MethodValues.Get);
     385           0 :   message.headers().setHost(EC2_METADATA_HOST);
     386           0 :   message.headers().setPath(credential_path);
     387           0 :   if (!token_string.empty()) {
     388           0 :     message.headers().setCopy(Http::LowerCaseString(EC2_IMDS_TOKEN_HEADER),
     389           0 :                               StringUtil::trim(token_string));
     390           0 :   }
     391             : 
     392           0 :   if (!async) {
     393             :     // Fetch and parse the credentials.
     394           0 :     const auto credential_document = fetch_metadata_using_curl_(message);
     395           0 :     if (!credential_document) {
     396           0 :       ENVOY_LOG(error, "Could not load AWS credentials document from the EC2MetadataService");
     397           0 :       return;
     398           0 :     }
     399           0 :     extractCredentials(std::move(credential_document.value()));
     400           0 :   } else {
     401             :     // Using Http async client to fetch and parse the AWS credentials.
     402           0 :     metadata_fetcher_->cancel(); // Cancel if there is any inflight request.
     403           0 :     on_async_fetch_cb_ = [this](const std::string&& arg) {
     404           0 :       return this->extractCredentialsAsync(std::move(arg));
     405           0 :     };
     406           0 :     metadata_fetcher_->fetch(message, Tracing::NullSpan::instance(), *this);
     407           0 :   }
     408           0 : }
     409             : 
     410             : void InstanceProfileCredentialsProvider::extractCredentials(
     411           0 :     const std::string&& credential_document_value, bool async /*default = false*/) {
     412           0 :   if (credential_document_value.empty()) {
     413           0 :     if (async) {
     414           0 :       handleFetchDone();
     415           0 :     }
     416           0 :     return;
     417           0 :   }
     418           0 :   Json::ObjectSharedPtr document_json;
     419           0 :   TRY_NEEDS_AUDIT { document_json = Json::Factory::loadFromString(credential_document_value); }
     420           0 :   END_TRY catch (EnvoyException& e) {
     421           0 :     ENVOY_LOG(error, "Could not parse AWS credentials document: {}", e.what());
     422           0 :     if (async) {
     423           0 :       handleFetchDone();
     424           0 :     }
     425           0 :     return;
     426           0 :   }
     427             : 
     428           0 :   const auto access_key_id = document_json->getString(ACCESS_KEY_ID, "");
     429           0 :   const auto secret_access_key = document_json->getString(SECRET_ACCESS_KEY, "");
     430           0 :   const auto session_token = document_json->getString(TOKEN, "");
     431             : 
     432           0 :   ENVOY_LOG(debug,
     433           0 :             "Obtained following AWS credentials from the EC2MetadataService: {}={}, {}={}, {}={}",
     434           0 :             AWS_ACCESS_KEY_ID, access_key_id, AWS_SECRET_ACCESS_KEY,
     435           0 :             secret_access_key.empty() ? "" : "*****", AWS_SESSION_TOKEN,
     436           0 :             session_token.empty() ? "" : "*****");
     437             : 
     438           0 :   last_updated_ = api_.timeSource().systemTime();
     439           0 :   if (useHttpAsyncClient() && context_) {
     440           0 :     setCredentialsToAllThreads(
     441           0 :         std::make_unique<Credentials>(access_key_id, secret_access_key, session_token));
     442           0 :   } else {
     443           0 :     cached_credentials_ = Credentials(access_key_id, secret_access_key, session_token);
     444           0 :   }
     445           0 :   handleFetchDone();
     446           0 : }
     447             : 
     448           0 : void InstanceProfileCredentialsProvider::onMetadataSuccess(const std::string&& body) {
     449             :   // TODO(suniltheta): increment fetch success stats
     450           0 :   ENVOY_LOG(debug, "AWS Instance metadata fetch success, calling callback func");
     451           0 :   on_async_fetch_cb_(std::move(body));
     452           0 : }
     453             : 
     454           0 : void InstanceProfileCredentialsProvider::onMetadataError(Failure reason) {
     455             :   // TODO(suniltheta): increment fetch failed stats
     456           0 :   if (continue_on_async_fetch_failure_) {
     457           0 :     ENVOY_LOG(warn, "{}. Reason: {}", continue_on_async_fetch_failure_reason_,
     458           0 :               metadata_fetcher_->failureToString(reason));
     459           0 :     continue_on_async_fetch_failure_ = false;
     460           0 :     continue_on_async_fetch_failure_reason_ = "";
     461           0 :     on_async_fetch_cb_(std::move(""));
     462           0 :   } else {
     463           0 :     ENVOY_LOG(error, "AWS Instance metadata fetch failure: {}",
     464           0 :               metadata_fetcher_->failureToString(reason));
     465           0 :     handleFetchDone();
     466           0 :   }
     467           0 : }
     468             : 
     469             : TaskRoleCredentialsProvider::TaskRoleCredentialsProvider(
     470             :     Api::Api& api, ServerFactoryContextOptRef context,
     471             :     const CurlMetadataFetcher& fetch_metadata_using_curl,
     472             :     CreateMetadataFetcherCb create_metadata_fetcher_cb, absl::string_view credential_uri,
     473             :     absl::string_view authorization_token = {}, absl::string_view cluster_name = {})
     474             :     : MetadataCredentialsProviderBase(
     475             :           api, context, fetch_metadata_using_curl, create_metadata_fetcher_cb, cluster_name,
     476             :           envoy::config::cluster::v3::Cluster::STATIC /*cluster_type*/, credential_uri),
     477           0 :       credential_uri_(credential_uri), authorization_token_(authorization_token) {}
     478             : 
     479           0 : bool TaskRoleCredentialsProvider::needsRefresh() {
     480           0 :   const auto now = api_.timeSource().systemTime();
     481           0 :   return (now - last_updated_ > REFRESH_INTERVAL) ||
     482           0 :          (expiration_time_ - now < REFRESH_GRACE_PERIOD);
     483           0 : }
     484             : 
     485           0 : void TaskRoleCredentialsProvider::refresh() {
     486           0 :   ENVOY_LOG(debug, "Getting AWS credentials from the task role at URI: {}", credential_uri_);
     487             : 
     488           0 :   absl::string_view host;
     489           0 :   absl::string_view path;
     490           0 :   Http::Utility::extractHostPathFromUri(credential_uri_, host, path);
     491             : 
     492           0 :   Http::RequestMessageImpl message;
     493           0 :   message.headers().setScheme(Http::Headers::get().SchemeValues.Http);
     494           0 :   message.headers().setMethod(Http::Headers::get().MethodValues.Get);
     495           0 :   message.headers().setHost(host);
     496           0 :   message.headers().setPath(path);
     497           0 :   message.headers().setCopy(Http::CustomHeaders::get().Authorization, authorization_token_);
     498           0 :   if (!useHttpAsyncClient() || !context_) {
     499             :     // Using curl to fetch the AWS credentials.
     500           0 :     const auto credential_document = fetch_metadata_using_curl_(message);
     501           0 :     if (!credential_document) {
     502           0 :       ENVOY_LOG(error, "Could not load AWS credentials document from the task role");
     503           0 :       return;
     504           0 :     }
     505           0 :     extractCredentials(std::move(credential_document.value()));
     506           0 :   } else {
     507             :     // Stop any existing timer.
     508           0 :     if (cache_duration_timer_ && cache_duration_timer_->enabled()) {
     509           0 :       cache_duration_timer_->disableTimer();
     510           0 :     }
     511             :     // Using Http async client to fetch the AWS credentials.
     512           0 :     if (!metadata_fetcher_) {
     513           0 :       metadata_fetcher_ = create_metadata_fetcher_cb_(context_->clusterManager(), clusterName());
     514           0 :     } else {
     515           0 :       metadata_fetcher_->cancel(); // Cancel if there is any inflight request.
     516           0 :     }
     517           0 :     on_async_fetch_cb_ = [this](const std::string&& arg) {
     518           0 :       return this->extractCredentials(std::move(arg));
     519           0 :     };
     520           0 :     metadata_fetcher_->fetch(message, Tracing::NullSpan::instance(), *this);
     521           0 :   }
     522           0 : }
     523             : 
     524             : void TaskRoleCredentialsProvider::extractCredentials(
     525           0 :     const std::string&& credential_document_value) {
     526           0 :   if (credential_document_value.empty()) {
     527           0 :     handleFetchDone();
     528           0 :     return;
     529           0 :   }
     530           0 :   Json::ObjectSharedPtr document_json;
     531           0 :   TRY_NEEDS_AUDIT { document_json = Json::Factory::loadFromString(credential_document_value); }
     532           0 :   END_TRY catch (EnvoyException& e) {
     533           0 :     ENVOY_LOG(error, "Could not parse AWS credentials document from the task role: {}", e.what());
     534           0 :     handleFetchDone();
     535           0 :     return;
     536           0 :   }
     537             : 
     538           0 :   const auto access_key_id = document_json->getString(ACCESS_KEY_ID, "");
     539           0 :   const auto secret_access_key = document_json->getString(SECRET_ACCESS_KEY, "");
     540           0 :   const auto session_token = document_json->getString(TOKEN, "");
     541             : 
     542           0 :   ENVOY_LOG(debug, "Found following AWS credentials in the task role: {}={}, {}={}, {}={}",
     543           0 :             AWS_ACCESS_KEY_ID, access_key_id, AWS_SECRET_ACCESS_KEY,
     544           0 :             secret_access_key.empty() ? "" : "*****", AWS_SESSION_TOKEN,
     545           0 :             session_token.empty() ? "" : "*****");
     546             : 
     547           0 :   const auto expiration_str = document_json->getString(EXPIRATION, "");
     548           0 :   if (!expiration_str.empty()) {
     549           0 :     absl::Time expiration_time;
     550           0 :     if (absl::ParseTime(EXPIRATION_FORMAT, expiration_str, &expiration_time, nullptr)) {
     551           0 :       ENVOY_LOG(debug, "Task role AWS credentials expiration time: {}", expiration_str);
     552           0 :       expiration_time_ = absl::ToChronoTime(expiration_time);
     553           0 :     }
     554           0 :   }
     555             : 
     556           0 :   last_updated_ = api_.timeSource().systemTime();
     557           0 :   if (useHttpAsyncClient() && context_) {
     558           0 :     setCredentialsToAllThreads(
     559           0 :         std::make_unique<Credentials>(access_key_id, secret_access_key, session_token));
     560           0 :   } else {
     561           0 :     cached_credentials_ = Credentials(access_key_id, secret_access_key, session_token);
     562           0 :   }
     563           0 :   handleFetchDone();
     564           0 : }
     565             : 
     566           0 : void TaskRoleCredentialsProvider::onMetadataSuccess(const std::string&& body) {
     567             :   // TODO(suniltheta): increment fetch success stats
     568           0 :   ENVOY_LOG(debug, "AWS Task metadata fetch success, calling callback func");
     569           0 :   on_async_fetch_cb_(std::move(body));
     570           0 : }
     571             : 
     572           0 : void TaskRoleCredentialsProvider::onMetadataError(Failure reason) {
     573             :   // TODO(suniltheta): increment fetch failed stats
     574           0 :   ENVOY_LOG(error, "AWS metadata fetch failure: {}", metadata_fetcher_->failureToString(reason));
     575           0 :   handleFetchDone();
     576           0 : }
     577             : 
     578             : WebIdentityCredentialsProvider::WebIdentityCredentialsProvider(
     579             :     Api::Api& api, ServerFactoryContextOptRef context,
     580             :     const CurlMetadataFetcher& fetch_metadata_using_curl,
     581             :     CreateMetadataFetcherCb create_metadata_fetcher_cb, absl::string_view token_file_path,
     582             :     absl::string_view sts_endpoint, absl::string_view role_arn, absl::string_view role_session_name,
     583             :     absl::string_view cluster_name = {})
     584             :     : MetadataCredentialsProviderBase(
     585             :           api, context, fetch_metadata_using_curl, create_metadata_fetcher_cb, cluster_name,
     586             :           envoy::config::cluster::v3::Cluster::LOGICAL_DNS /*cluster_type*/, sts_endpoint),
     587             :       token_file_path_(token_file_path), sts_endpoint_(sts_endpoint), role_arn_(role_arn),
     588           0 :       role_session_name_(role_session_name) {}
     589             : 
     590           0 : bool WebIdentityCredentialsProvider::needsRefresh() {
     591           0 :   const auto now = api_.timeSource().systemTime();
     592           0 :   return (now - last_updated_ > REFRESH_INTERVAL) ||
     593           0 :          (expiration_time_ - now < REFRESH_GRACE_PERIOD);
     594           0 : }
     595             : 
     596           0 : void WebIdentityCredentialsProvider::refresh() {
     597             :   // If http async client is not enabled then just set empty credentials and return.
     598           0 :   if (!useHttpAsyncClient()) {
     599           0 :     cached_credentials_ = Credentials();
     600           0 :     return;
     601           0 :   }
     602             : 
     603           0 :   ENVOY_LOG(debug, "Getting AWS web identity credentials from STS: {}", sts_endpoint_);
     604             : 
     605           0 :   const auto web_token_file_or_error = api_.fileSystem().fileReadToEnd(token_file_path_);
     606           0 :   THROW_IF_STATUS_NOT_OK(web_token_file_or_error, throw);
     607           0 :   Http::RequestMessageImpl message;
     608           0 :   message.headers().setScheme(Http::Headers::get().SchemeValues.Https);
     609           0 :   message.headers().setMethod(Http::Headers::get().MethodValues.Get);
     610           0 :   message.headers().setHost(Http::Utility::parseAuthority(sts_endpoint_).host_);
     611           0 :   message.headers().setPath(
     612           0 :       fmt::format("/?Action=AssumeRoleWithWebIdentity"
     613           0 :                   "&Version=2011-06-15"
     614           0 :                   "&RoleSessionName={}"
     615           0 :                   "&RoleArn={}"
     616           0 :                   "&WebIdentityToken={}",
     617           0 :                   Envoy::Http::Utility::PercentEncoding::encode(role_session_name_),
     618           0 :                   Envoy::Http::Utility::PercentEncoding::encode(role_arn_),
     619           0 :                   Envoy::Http::Utility::PercentEncoding::encode(web_token_file_or_error.value())));
     620             :   // Use the Accept header to ensure that AssumeRoleWithWebIdentityResponse is returned as JSON.
     621           0 :   message.headers().setReference(Http::CustomHeaders::get().Accept,
     622           0 :                                  Http::Headers::get().ContentTypeValues.Json);
     623             :   // Stop any existing timer.
     624           0 :   if (cache_duration_timer_ && cache_duration_timer_->enabled()) {
     625           0 :     cache_duration_timer_->disableTimer();
     626           0 :   }
     627             :   // Using Http async client to fetch the AWS credentials.
     628           0 :   if (!metadata_fetcher_) {
     629           0 :     metadata_fetcher_ = create_metadata_fetcher_cb_(context_->clusterManager(), clusterName());
     630           0 :   } else {
     631           0 :     metadata_fetcher_->cancel(); // Cancel if there is any inflight request.
     632           0 :   }
     633           0 :   on_async_fetch_cb_ = [this](const std::string&& arg) {
     634           0 :     return this->extractCredentials(std::move(arg));
     635           0 :   };
     636           0 :   metadata_fetcher_->fetch(message, Tracing::NullSpan::instance(), *this);
     637           0 : }
     638             : 
     639             : void WebIdentityCredentialsProvider::extractCredentials(
     640           0 :     const std::string&& credential_document_value) {
     641           0 :   if (credential_document_value.empty()) {
     642           0 :     handleFetchDone();
     643           0 :     ENVOY_LOG(error, "Could not load AWS credentials document from STS");
     644           0 :     return;
     645           0 :   }
     646             : 
     647           0 :   Json::ObjectSharedPtr document_json;
     648           0 :   TRY_NEEDS_AUDIT { document_json = Json::Factory::loadFromString(credential_document_value); }
     649           0 :   END_TRY catch (EnvoyException& e) {
     650           0 :     ENVOY_LOG(error, "Could not parse AWS credentials document from STS: {}", e.what());
     651           0 :     handleFetchDone();
     652           0 :     return;
     653           0 :   }
     654             : 
     655           0 :   absl::StatusOr<Json::ObjectSharedPtr> root_node =
     656           0 :       document_json->getObjectNoThrow(WEB_IDENTITY_RESPONSE_ELEMENT);
     657           0 :   if (!root_node.ok()) {
     658           0 :     ENVOY_LOG(error, "AWS STS credentials document is empty");
     659           0 :     handleFetchDone();
     660           0 :     return;
     661           0 :   }
     662           0 :   absl::StatusOr<Json::ObjectSharedPtr> result_node =
     663           0 :       root_node.value()->getObjectNoThrow(WEB_IDENTITY_RESULT_ELEMENT);
     664           0 :   if (!result_node.ok()) {
     665           0 :     ENVOY_LOG(error, "AWS STS returned an unexpected result");
     666           0 :     handleFetchDone();
     667           0 :     return;
     668           0 :   }
     669           0 :   absl::StatusOr<Json::ObjectSharedPtr> credentials =
     670           0 :       result_node.value()->getObjectNoThrow(CREDENTIALS);
     671           0 :   if (!credentials.ok()) {
     672           0 :     ENVOY_LOG(error, "AWS STS credentials document does not contain any credentials");
     673           0 :     handleFetchDone();
     674           0 :     return;
     675           0 :   }
     676             : 
     677           0 :   TRY_NEEDS_AUDIT {
     678           0 :     const auto access_key_id = credentials.value()->getString(ACCESS_KEY_ID, "");
     679           0 :     const auto secret_access_key = credentials.value()->getString(SECRET_ACCESS_KEY, "");
     680           0 :     const auto session_token = credentials.value()->getString(SESSION_TOKEN, "");
     681             : 
     682           0 :     ENVOY_LOG(debug, "Received the following AWS credentials from STS: {}={}, {}={}, {}={}",
     683           0 :               AWS_ACCESS_KEY_ID, access_key_id, AWS_SECRET_ACCESS_KEY,
     684           0 :               secret_access_key.empty() ? "" : "*****", AWS_SESSION_TOKEN,
     685           0 :               session_token.empty() ? "" : "*****");
     686           0 :     setCredentialsToAllThreads(
     687           0 :         std::make_unique<Credentials>(access_key_id, secret_access_key, session_token));
     688           0 :   }
     689           0 :   END_TRY catch (EnvoyException& e) {
     690           0 :     ENVOY_LOG(error, "Bad format, could not parse AWS credentials document from STS: {}", e.what());
     691           0 :     handleFetchDone();
     692           0 :     return;
     693           0 :   }
     694             : 
     695           0 :   TRY_NEEDS_AUDIT {
     696           0 :     const auto expiration = credentials.value()->getInteger(EXPIRATION, 0);
     697           0 :     if (expiration != 0) {
     698           0 :       expiration_time_ =
     699           0 :           std::chrono::time_point<std::chrono::system_clock>(std::chrono::seconds(expiration));
     700           0 :       ENVOY_LOG(debug, "AWS STS credentials expiration time (unix timestamp): {}", expiration);
     701           0 :     } else {
     702           0 :       expiration_time_ = api_.timeSource().systemTime() + REFRESH_INTERVAL;
     703           0 :       ENVOY_LOG(warn, "Could not get Expiration value of AWS credentials document from STS, so "
     704           0 :                       "setting expiration to 1 hour in future");
     705           0 :     }
     706           0 :   }
     707           0 :   END_TRY catch (EnvoyException& e) {
     708           0 :     expiration_time_ = api_.timeSource().systemTime() + REFRESH_INTERVAL;
     709           0 :     ENVOY_LOG(warn,
     710           0 :               "Could not parse Expiration value of AWS credentials document from STS: {}, so "
     711           0 :               "setting expiration to 1 hour in future",
     712           0 :               e.what());
     713           0 :   }
     714             : 
     715           0 :   last_updated_ = api_.timeSource().systemTime();
     716           0 :   handleFetchDone();
     717           0 : }
     718             : 
     719           0 : void WebIdentityCredentialsProvider::onMetadataSuccess(const std::string&& body) {
     720             :   // TODO(suniltheta): increment fetch success stats
     721           0 :   ENVOY_LOG(debug, "AWS metadata fetch from STS success, calling callback func");
     722           0 :   on_async_fetch_cb_(std::move(body));
     723           0 : }
     724             : 
     725           0 : void WebIdentityCredentialsProvider::onMetadataError(Failure reason) {
     726             :   // TODO(suniltheta): increment fetch failed stats
     727           0 :   ENVOY_LOG(error, "AWS metadata fetch failure: {}", metadata_fetcher_->failureToString(reason));
     728           0 :   handleFetchDone();
     729           0 : }
     730             : 
     731           5 : Credentials CredentialsProviderChain::getCredentials() {
     732          15 :   for (auto& provider : providers_) {
     733          15 :     const auto credentials = provider->getCredentials();
     734          15 :     if (credentials.accessKeyId() && credentials.secretAccessKey()) {
     735           0 :       return credentials;
     736           0 :     }
     737          15 :   }
     738             : 
     739           5 :   ENVOY_LOG(debug, "No AWS credentials found, using anonymous credentials");
     740           5 :   return Credentials();
     741           5 : }
     742             : 
     743             : DefaultCredentialsProviderChain::DefaultCredentialsProviderChain(
     744             :     Api::Api& api, ServerFactoryContextOptRef context, absl::string_view region,
     745             :     const MetadataCredentialsProviderBase::CurlMetadataFetcher& fetch_metadata_using_curl,
     746           6 :     const CredentialsProviderChainFactories& factories) {
     747           6 :   ENVOY_LOG(debug, "Using environment credentials provider");
     748           6 :   add(factories.createEnvironmentCredentialsProvider());
     749             : 
     750           6 :   if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_aws_credentials_file")) {
     751           6 :     ENVOY_LOG(debug, "Using credentials file credentials provider");
     752           6 :     add(factories.createCredentialsFileCredentialsProvider(api));
     753           6 :   } else {
     754           0 :     ENVOY_LOG(debug, "Not using credential file credentials provider because it is not enabled");
     755           0 :   }
     756             : 
     757             :   // WebIdentityCredentialsProvider can be used only if `context` is supplied which is required to
     758             :   // use http async http client to make http calls to fetch the credentials.
     759           6 :   if (context) {
     760           6 :     const auto web_token_path = absl::NullSafeStringView(std::getenv(AWS_WEB_IDENTITY_TOKEN_FILE));
     761           6 :     const auto role_arn = absl::NullSafeStringView(std::getenv(AWS_ROLE_ARN));
     762           6 :     if (!web_token_path.empty() && !role_arn.empty()) {
     763           0 :       const auto role_session_name = absl::NullSafeStringView(std::getenv(AWS_ROLE_SESSION_NAME));
     764           0 :       std::string actual_session_name;
     765           0 :       if (!role_session_name.empty()) {
     766           0 :         actual_session_name = std::string(role_session_name);
     767           0 :       } else {
     768             :         // In practice, this value will be provided by the environment, so the placeholder value is
     769             :         // not important. Some AWS SDKs use time in nanoseconds, so we'll just use that.
     770           0 :         const auto now_nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(
     771           0 :                                    api.timeSource().systemTime().time_since_epoch())
     772           0 :                                    .count();
     773           0 :         actual_session_name = fmt::format("{}", now_nanos);
     774           0 :       }
     775           0 :       const auto sts_endpoint = Utility::getSTSEndpoint(region) + ":443";
     776           0 :       ENVOY_LOG(
     777           0 :           debug,
     778           0 :           "Using web identity credentials provider with STS endpoint: {} and session name: {}",
     779           0 :           sts_endpoint, actual_session_name);
     780           0 :       add(factories.createWebIdentityCredentialsProvider(
     781           0 :           api, context, fetch_metadata_using_curl, MetadataFetcher::create, STS_TOKEN_CLUSTER,
     782           0 :           web_token_path, sts_endpoint, role_arn, actual_session_name));
     783           0 :     }
     784           6 :   }
     785             : 
     786             :   // Even if WebIdentity is supported keep the fallback option open so that
     787             :   // Envoy can use other credentials provider if available.
     788           6 :   const auto relative_uri =
     789           6 :       absl::NullSafeStringView(std::getenv(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI));
     790           6 :   const auto full_uri = absl::NullSafeStringView(std::getenv(AWS_CONTAINER_CREDENTIALS_FULL_URI));
     791           6 :   const auto metadata_disabled = absl::NullSafeStringView(std::getenv(AWS_EC2_METADATA_DISABLED));
     792             : 
     793           6 :   if (!relative_uri.empty()) {
     794           0 :     const auto uri = absl::StrCat(CONTAINER_METADATA_HOST, relative_uri);
     795           0 :     ENVOY_LOG(debug, "Using task role credentials provider with URI: {}", uri);
     796           0 :     add(factories.createTaskRoleCredentialsProvider(api, context, fetch_metadata_using_curl,
     797           0 :                                                     MetadataFetcher::create,
     798           0 :                                                     CONTAINER_METADATA_CLUSTER, uri));
     799           6 :   } else if (!full_uri.empty()) {
     800           0 :     const auto authorization_token =
     801           0 :         absl::NullSafeStringView(std::getenv(AWS_CONTAINER_AUTHORIZATION_TOKEN));
     802           0 :     if (!authorization_token.empty()) {
     803           0 :       ENVOY_LOG(debug,
     804           0 :                 "Using task role credentials provider with URI: "
     805           0 :                 "{} and authorization token",
     806           0 :                 full_uri);
     807           0 :       add(factories.createTaskRoleCredentialsProvider(
     808           0 :           api, context, fetch_metadata_using_curl, MetadataFetcher::create,
     809           0 :           CONTAINER_METADATA_CLUSTER, full_uri, authorization_token));
     810           0 :     } else {
     811           0 :       ENVOY_LOG(debug, "Using task role credentials provider with URI: {}", full_uri);
     812           0 :       add(factories.createTaskRoleCredentialsProvider(api, context, fetch_metadata_using_curl,
     813           0 :                                                       MetadataFetcher::create,
     814           0 :                                                       CONTAINER_METADATA_CLUSTER, full_uri));
     815           0 :     }
     816           6 :   } else if (metadata_disabled != TRUE) {
     817           6 :     ENVOY_LOG(debug, "Using instance profile credentials provider");
     818           6 :     add(factories.createInstanceProfileCredentialsProvider(
     819           6 :         api, context, fetch_metadata_using_curl, MetadataFetcher::create, EC2_METADATA_CLUSTER));
     820           6 :   }
     821           6 : }
     822             : 
     823             : } // namespace Aws
     824             : } // namespace Common
     825             : } // namespace Extensions
     826             : } // namespace Envoy

Generated by: LCOV version 1.15