This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new d34f06b5da2 [fix](be): fix BE access to aws under eks pod identity 
(#67076)
d34f06b5da2 is described below

commit d34f06b5da25d50282f07fb801c1b426290ab980
Author: Owen Leung <[email protected]>
AuthorDate: Thu Sep 3 15:28:51 2026 +0800

    [fix](be): fix BE access to aws under eks pod identity (#67076)
    
    ### What problem does this PR solve?
    
    Issue Number: close #66554
    
    **Problem Summary**
    
    Fixes the issue above by forwarding
    `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE` alongside the inline token.
    Given a file path, the provider will re-read it before every fetch, so
    credentials survive token rotation. ECS, which sets only the inline
    variable `AWS_CONTAINER_AUTHORIZATION_TOKEN`, is unaffected.
    
    **Why the aws-sdp-cpp bump**
    
    The `GeneralHTTPCredentialsProvider` does not exist before 1.11.221. The
    only container provider available at the currently pinned version is
    `TaskRoleCredentialsProvider`, which takes the token as a string and has
    no notion of a token file. 1.11.221 is the first version whose
    `GeneralHTTPCredentialsProvider` re-reads the token file.
---
 be/src/runtime/aws_msk_iam_auth.cpp                |   5 +-
 be/test/io/s3_client_factory_test.cpp              | 207 ++++++++++++++++++++-
 be/test/runtime/aws_msk_iam_auth_test.cpp          |  54 ++++++
 be/test/testutil/container_credentials_endpoint.h  |  99 ++++++++++
 cloud/test/s3_accessor_mock_test.cpp               |  74 ++++++++
 common/cpp/aws_common.cpp                          |  58 ++++++
 common/cpp/aws_common.h                            |  28 +++
 .../cpp/custom_aws_credentials_provider_chain.cpp  |  50 ++---
 .../cpp/obj-client/auth/aws_credential_factory.cpp |   4 +-
 common/cpp/test/container_credentials_test_util.h  | 131 +++++++++++++
 gensrc/proto/cloud.proto                           |   2 +-
 gensrc/thrift/AgentService.thrift                  |   2 +-
 12 files changed, 657 insertions(+), 57 deletions(-)

diff --git a/be/src/runtime/aws_msk_iam_auth.cpp 
b/be/src/runtime/aws_msk_iam_auth.cpp
index 6d6292b7b01..863d2789a61 100644
--- a/be/src/runtime/aws_msk_iam_auth.cpp
+++ b/be/src/runtime/aws_msk_iam_auth.cpp
@@ -21,7 +21,6 @@
 #include <aws/core/auth/AWSCredentialsProvider.h>
 #include <aws/core/auth/AWSCredentialsProviderChain.h>
 #include <aws/core/auth/STSCredentialsProvider.h>
-#include <aws/core/platform/Environment.h>
 #include <aws/identity-management/auth/STSAssumeRoleCredentialsProvider.h>
 #include <aws/sts/STSClient.h>
 #include <aws/sts/model/AssumeRoleRequest.h>
@@ -34,6 +33,7 @@
 #include <sstream>
 
 #include "common/logging.h"
+#include "cpp/aws_common.h"
 
 namespace doris {
 
@@ -51,8 +51,7 @@ std::shared_ptr<Aws::Auth::AWSCredentialsProvider> 
AwsMskIamAuth::_create_provid
     } else if (provider_upper == "INSTANCE_PROFILE" || provider_upper == 
"INSTANCEPROFILE") {
         return 
std::make_shared<Aws::Auth::InstanceProfileCredentialsProvider>();
     } else if (provider_upper == "CONTAINER" || provider_upper == "ECS") {
-        return std::make_shared<Aws::Auth::TaskRoleCredentialsProvider>(
-                
Aws::Environment::GetEnv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI").c_str());
+        return create_container_credentials_provider();
     } else if (provider_upper == "SYSTEM_PROPERTIES" || provider_upper == 
"SYSTEMPROPERTIES") {
         return 
std::make_shared<Aws::Auth::ProfileConfigFileAWSCredentialsProvider>();
     } else if (provider_upper == "WEB_IDENTITY" || provider_upper == 
"WEBIDENTITY" ||
diff --git a/be/test/io/s3_client_factory_test.cpp 
b/be/test/io/s3_client_factory_test.cpp
index 466ca77415c..835b32bdaef 100644
--- a/be/test/io/s3_client_factory_test.cpp
+++ b/be/test/io/s3_client_factory_test.cpp
@@ -16,6 +16,7 @@
 // under the License.
 
 #include <aws/core/auth/AWSCredentialsProviderChain.h>
+#include <aws/core/auth/GeneralHTTPCredentialsProvider.h>
 #include <aws/core/auth/STSCredentialsProvider.h>
 #include <aws/core/client/ClientConfiguration.h>
 #include <aws/identity-management/auth/STSAssumeRoleCredentialsProvider.h>
@@ -23,9 +24,11 @@
 #include <gtest/gtest.h>
 #include <unistd.h>
 
+#include <cstdio>
 #include <cstdlib>
 #include <filesystem>
 #include <fstream>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -34,9 +37,11 @@
 #include "common/config.h"
 #include "cpp/aws_common.h"
 #include "cpp/custom_aws_credentials_provider_chain.h"
+#include "cpp/obj-client/auth/aws_credential_factory.h"
 #include "cpp/obj-client/s3_obj_storage_client.h"
 #include "cpp/sync_point.h"
 #include "io/fs/s3_file_system.h"
+#include "testutil/container_credentials_endpoint.h"
 #include "util/s3_rate_limiter_manager.h"
 #include "util/s3_uri.h"
 #include "util/s3_util.h"
@@ -573,16 +578,15 @@ TEST_F(S3ClientFactoryTest, 
AwsCredentialsProviderV2ProviderTypeWithoutRoleArn)
                       provider),
               nullptr);
 
-    const char* old_container_uri = 
std::getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI");
-    if (old_container_uri == nullptr) {
-        setenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", 
"/v2/credentials/mock", 1);
-    }
-    S3ClientConf container_conf;
-    container_conf.cred_provider_type = CredProviderType::Container;
-    provider = 
factory.create_aws_credentials_provider(container_conf).provider;
-    
ASSERT_NE(std::dynamic_pointer_cast<Aws::Auth::TaskRoleCredentialsProvider>(provider),
 nullptr);
-    if (old_container_uri == nullptr) {
-        unsetenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI");
+    {
+        // The environment is pinned rather than merely topped up, so that 
this case asserts the
+        // same thing whether or not the machine running it happens to be 
inside a container.
+        ContainerCredentialsEnvGuard env;
+        env.set_ecs_task_role("/v2/credentials/mock");
+        S3ClientConf container_conf;
+        container_conf.cred_provider_type = CredProviderType::Container;
+        provider = 
factory.create_aws_credentials_provider(container_conf).provider;
+        ASSERT_NE(as_valid_http_provider(provider), nullptr);
     }
 
     S3ClientConf instance_profile_conf;
@@ -689,4 +693,187 @@ TEST_F(S3ClientFactoryTest, 
AwsCredentialsProviderV1PartialCredentialsUseDefault
     config::aws_credentials_provider_version = "v2";
 }
 
+namespace {
+
+// The provider is looked up inside the chain rather than constructed directly,
+// because what is under test is how CustomAwsCredentialsProviderChain wires 
the
+// environment.
+Aws::Auth::GeneralHTTPCredentialsProvider* find_http_provider(
+        const CustomAwsCredentialsProviderChain& chain) {
+    for (const auto& provider : chain.GetProviders()) {
+        auto* http_provider =
+                
dynamic_cast<Aws::Auth::GeneralHTTPCredentialsProvider*>(provider.get());
+        if (http_provider != nullptr) {
+            return http_provider;
+        }
+    }
+    return nullptr;
+}
+
+} // namespace
+
+// EKS Pod Identity supplies the credential-endpoint token as a file that the
+// kubelet rotates in place, and never as a plain environment variable. The 
chain
+// must therefore hand the provider the token path, so that every refresh picks
+// up the current contents. Passing the value read at construction time works
+// until the first rotation and then fails with AccessDenied.
+TEST_F(S3ClientFactoryTest, CustomChainReadsRotatedTokenFileForPodIdentity) {
+    (void)S3ClientFactory::instance();
+
+    ContainerCredentialsEndpoint endpoint;
+    ASSERT_TRUE(endpoint.start());
+
+    ContainerCredentialsEnvGuard env;
+    const std::string token_path = env.token_file_path("custom_chain");
+    env.write_token_file(token_path, "token-one");
+    env.set_pod_identity(endpoint.url(), token_path);
+
+    CustomAwsCredentialsProviderChain chain;
+    auto* provider = find_http_provider(chain);
+    ASSERT_NE(provider, nullptr) << "no GeneralHTTPCredentialsProvider was 
added to the chain";
+
+    // Each GetAWSCredentials() drives one HTTP GET to the endpoint: the 
provider re-reads the token
+    // file, sends it as the Authorization header, and parses the credentials 
from the reply. The
+    // handler records the header it saw, so auth_headers() reports what 
actually went over the wire.
+    const auto first_credentials = provider->GetAWSCredentials();
+    EXPECT_EQ(first_credentials.GetAWSAccessKeyId(), "AKIDTEST");
+    EXPECT_EQ(first_credentials.GetSessionToken(), "SESSIONTEST");
+
+    // Rotate the file the way the kubelet does. The second call refetches 
rather than serving its
+    // cache only because the handler reports an already-expired Expiration.
+    env.write_token_file(token_path, "token-two");
+    provider->GetAWSCredentials();
+
+    // GE rather than EQ: the credentials client retries and the handler 
records every attempt, so an
+    // exact count would make a retry look like a bug. front/back keep the 
assertion at full strength
+    // because every request before the rewrite carries token-one and every 
one after carries token-two.
+    const auto auth_headers = endpoint.auth_headers();
+    ASSERT_GE(auth_headers.size(), 2u);
+    EXPECT_EQ(auth_headers.front(), "token-one");
+    EXPECT_EQ(auth_headers.back(), "token-two");
+}
+
+// The same wiring has to hold when CONTAINER is asked for by name rather than 
reached through the
+// default chain, which is the whole point of a public provider mode. Reading 
only
+// AWS_CONTAINER_CREDENTIALS_RELATIVE_URI leaves this provider with an empty 
URI, no HTTP client and
+// no credentials on every standard EKS deployment.
+TEST_F(S3ClientFactoryTest, 
ContainerProviderTypeReadsRotatedTokenFileForPodIdentity) {
+    S3ClientFactory& factory = S3ClientFactory::instance();
+    config::aws_credentials_provider_version = "v2";
+
+    ContainerCredentialsEndpoint endpoint;
+    ASSERT_TRUE(endpoint.start());
+
+    ContainerCredentialsEnvGuard env;
+    const std::string token_path = env.token_file_path("container_type");
+    env.write_token_file(token_path, "token-one");
+    env.set_pod_identity(endpoint.url(), token_path);
+
+    S3ClientConf conf;
+    conf.cred_provider_type = CredProviderType::Container;
+    auto provider = 
as_valid_http_provider(factory.create_aws_credentials_provider(conf).provider);
+    ASSERT_NE(provider, nullptr) << "CONTAINER did not yield a usable 
container credentials "
+                                    "provider for 
AWS_CONTAINER_CREDENTIALS_FULL_URI";
+
+    const auto first_credentials = provider->GetAWSCredentials();
+    EXPECT_EQ(first_credentials.GetAWSAccessKeyId(), "AKIDTEST");
+    EXPECT_EQ(first_credentials.GetSessionToken(), "SESSIONTEST");
+
+    env.write_token_file(token_path, "token-two");
+    provider->GetAWSCredentials();
+
+    const auto auth_headers = endpoint.auth_headers();
+    ASSERT_GE(auth_headers.size(), 2u);
+    EXPECT_EQ(auth_headers.front(), "token-one");
+    EXPECT_EQ(auth_headers.back(), "token-two");
+}
+
+TEST_F(S3ClientFactoryTest, 
ContainerProviderTypeForwardsInlineAuthorizationToken) {
+    S3ClientFactory& factory = S3ClientFactory::instance();
+    config::aws_credentials_provider_version = "v2";
+
+    ContainerCredentialsEndpoint endpoint;
+    ASSERT_TRUE(endpoint.start());
+
+    ContainerCredentialsEnvGuard env;
+    env.set_inline_token_endpoint(endpoint.url(), "inline-token");
+
+    S3ClientConf conf;
+    conf.cred_provider_type = CredProviderType::Container;
+    auto provider = 
as_valid_http_provider(factory.create_aws_credentials_provider(conf).provider);
+    ASSERT_NE(provider, nullptr) << "CONTAINER did not yield a usable 
container credentials "
+                                    "provider for an inline authorization 
token";
+
+    EXPECT_EQ(provider->GetAWSCredentials().GetAWSAccessKeyId(), "AKIDTEST");
+
+    const auto auth_headers = endpoint.auth_headers();
+    ASSERT_GE(auth_headers.size(), 1u);
+    EXPECT_EQ(auth_headers.front(), "inline-token");
+}
+
+TEST_F(S3ClientFactoryTest, ContainerProviderTypeStillHonoursEcsRelativeUri) {
+    S3ClientFactory& factory = S3ClientFactory::instance();
+    config::aws_credentials_provider_version = "v2";
+
+    ContainerCredentialsEnvGuard env;
+    env.set_ecs_task_role("/v2/credentials/mock");
+
+    S3ClientConf conf;
+    conf.cred_provider_type = CredProviderType::Container;
+    
EXPECT_NE(as_valid_http_provider(factory.create_aws_credentials_provider(conf).provider),
+              nullptr)
+            << "CONTAINER did not yield a usable container credentials 
provider for "
+               "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
+}
+
+// With neither URI exported there is nothing to talk to, and the provider 
says so. This pins the
+// discriminator the two tests above rely on: IsValid() really does 
distinguish a wired provider
+// from an inert one, so their assertions cannot pass vacuously.
+TEST_F(S3ClientFactoryTest, ContainerProviderTypeIsUnusableWithoutAnyUri) {
+    S3ClientFactory& factory = S3ClientFactory::instance();
+    config::aws_credentials_provider_version = "v2";
+
+    // Constructing the guard is the whole setup: it clears all four 
AWS_CONTAINER_* variables, so
+    // this runs as if on a host that is not a container at all.
+    ContainerCredentialsEnvGuard env;
+
+    S3ClientConf conf;
+    conf.cred_provider_type = CredProviderType::Container;
+    auto provider = factory.create_aws_credentials_provider(conf).provider;
+    
ASSERT_NE(std::dynamic_pointer_cast<Aws::Auth::GeneralHTTPCredentialsProvider>(provider),
+              nullptr);
+    EXPECT_EQ(as_valid_http_provider(provider), nullptr);
+}
+
+// With a role_arn the CONTAINER provider is not handed to the S3 client 
directly: it becomes the
+// base provider the STS client authenticates its AssumeRole call with. 
AwsCredentialFactory is
+// asked for that base provider here - a v2 request with no role_arn returns 
it unwrapped - so the
+// assertion is about the base itself rather than the STS wrapper around it.
+TEST_F(S3ClientFactoryTest, ContainerProviderTypeIsUsableAsStsBaseProvider) {
+    (void)S3ClientFactory::instance();
+
+    ContainerCredentialsEndpoint endpoint;
+    ASSERT_TRUE(endpoint.start());
+
+    ContainerCredentialsEnvGuard env;
+    const std::string token_path = env.token_file_path("sts_base");
+    env.write_token_file(token_path, "token-one");
+    env.set_pod_identity(endpoint.url(), token_path);
+
+    AwsCredentialOptions options;
+    options.version = AwsCredentialProviderVersion::V2;
+    options.provider_type = CredProviderType::Container;
+    // role_arn stays empty on purpose: that is what makes create() hand back 
the base provider
+    // itself instead of the STS wrapper built on top of it.
+    auto base_provider = 
as_valid_http_provider(AwsCredentialFactory::create(options).provider);
+    ASSERT_NE(base_provider, nullptr)
+            << "CONTAINER did not yield a usable STS base credentials 
provider";
+
+    EXPECT_EQ(base_provider->GetAWSCredentials().GetAWSAccessKeyId(), 
"AKIDTEST");
+    const auto auth_headers = endpoint.auth_headers();
+    ASSERT_FALSE(auth_headers.empty())
+            << "the STS base provider never contacted the credentials 
endpoint";
+    EXPECT_EQ(auth_headers.back(), "token-one");
+}
+
 } // namespace doris
diff --git a/be/test/runtime/aws_msk_iam_auth_test.cpp 
b/be/test/runtime/aws_msk_iam_auth_test.cpp
index f1eb1ec37ed..954621ece5e 100644
--- a/be/test/runtime/aws_msk_iam_auth_test.cpp
+++ b/be/test/runtime/aws_msk_iam_auth_test.cpp
@@ -17,6 +17,7 @@
 
 #include "runtime/aws_msk_iam_auth.h"
 
+#include <aws/core/auth/AWSCredentials.h>
 #include <gtest/gtest.h>
 
 #include <memory>
@@ -24,6 +25,8 @@
 #include <unordered_map>
 
 #include "common/status.h"
+#include "testutil/container_credentials_endpoint.h"
+#include "util/s3_util.h"
 
 namespace doris {
 
@@ -34,6 +37,14 @@ protected:
         config.region = "us-east-1";
     }
 
+    // Nothing in this binary initialises the AWS SDK on its own - neither 
BE-UT's main() nor this
+    // file - and without Aws::InitAPI there is no HTTP client factory, so a 
credentials provider
+    // quietly returns nothing instead of making a request. Constructing the 
S3 client factory is
+    // the cheapest in-tree way to get InitAPI called, exactly once per 
process, and it is the same
+    // trick the S3 client factory tests use. Only the tests that drive a real 
fetch need it, so it
+    // stays out of SetUp() where it would change what the older tests in this 
file do.
+    static void ensure_aws_sdk_initialized() { 
(void)S3ClientFactory::instance(); }
+
     AwsMskIamAuth::Config config;
 };
 
@@ -215,6 +226,49 @@ TEST_F(AwsMskIamAuthTest, 
TestOAuthCallbackCreationWithExternalIdWithoutRoleArn)
     ASSERT_EQ(callback, nullptr);
 }
 
+TEST_F(AwsMskIamAuthTest, ContainerProviderReadsTokenFileForPodIdentity) {
+    ensure_aws_sdk_initialized();
+
+    ContainerCredentialsEndpoint endpoint;
+    ASSERT_TRUE(endpoint.start());
+
+    ContainerCredentialsEnvGuard env;
+    const std::string token_path = env.token_file_path("msk_container");
+    env.write_token_file(token_path, "token-one");
+    env.set_pod_identity(endpoint.url(), token_path);
+
+    config.credentials_provider = "CONTAINER";
+    AwsMskIamAuth auth(config);
+
+    Aws::Auth::AWSCredentials credentials;
+    ASSERT_TRUE(auth.get_credentials(&credentials).ok());
+    EXPECT_EQ(credentials.GetAWSAccessKeyId(), "AKIDTEST");
+    EXPECT_EQ(credentials.GetSessionToken(), "SESSIONTEST");
+
+    const auto auth_headers = endpoint.auth_headers();
+    ASSERT_FALSE(auth_headers.empty());
+    EXPECT_EQ(auth_headers.back(), "token-one");
+}
+
+TEST_F(AwsMskIamAuthTest, EcsProviderAliasReachesContainerCredentialsEndpoint) 
{
+    ensure_aws_sdk_initialized();
+
+    ContainerCredentialsEndpoint endpoint;
+    ASSERT_TRUE(endpoint.start());
+
+    ContainerCredentialsEnvGuard env;
+    const std::string token_path = env.token_file_path("msk_ecs_alias");
+    env.write_token_file(token_path, "token-one");
+    env.set_pod_identity(endpoint.url(), token_path);
+
+    config.credentials_provider = "ECS";
+    AwsMskIamAuth auth(config);
+
+    Aws::Auth::AWSCredentials credentials;
+    ASSERT_TRUE(auth.get_credentials(&credentials).ok());
+    EXPECT_EQ(credentials.GetAWSAccessKeyId(), "AKIDTEST");
+}
+
 // Integration test - only runs if AWS credentials are available
 TEST_F(AwsMskIamAuthTest, DISABLED_IntegrationTestWithRealCredentials) {
     // This test is disabled by default
diff --git a/be/test/testutil/container_credentials_endpoint.h 
b/be/test/testutil/container_credentials_endpoint.h
new file mode 100644
index 00000000000..ac60189a69e
--- /dev/null
+++ b/be/test/testutil/container_credentials_endpoint.h
@@ -0,0 +1,99 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include "cpp/test/container_credentials_test_util.h"
+#include "service/http/ev_http_server.h"
+#include "service/http/http_channel.h"
+#include "service/http/http_handler.h"
+#include "service/http/http_headers.h"
+#include "service/http/http_method.h"
+#include "service/http/http_request.h"
+
+// Scaffolding for tests of the container credentials providers - the ECS 
task-role and EKS Pod
+// Identity endpoints. A credible test needs three awkward pieces at once: a 
real HTTP endpoint on an
+// address the AWS SDK is willing to talk to, a token file that can be 
rewritten mid-test, and the
+// four AWS_CONTAINER_* environment variables put back however they were found.
+//
+// Only the first of those lives here, because it is the only one the cloud 
test tree cannot use: it
+// needs EvHttpServer, which is BE-only. The other two - 
ContainerCredentialsEnvGuard and
+// as_valid_http_provider - come from 
cpp/test/container_credentials_test_util.h, included above and
+// re-exported to anything that includes this header, so BE tests keep getting 
all three from one
+// include.
+namespace doris {
+
+// A mock of the credentials endpoint that ECS and the EKS Pod Identity agent 
expose.
+class ContainerCredentialsHandler : public HttpHandler {
+public:
+    void handle(HttpRequest* req) override {
+        {
+            std::lock_guard<std::mutex> lock(_mutex);
+            _auth_headers.push_back(req->header(HttpHeaders::AUTHORIZATION));
+        }
+
+        req->add_output_header(HttpHeaders::CONTENT_TYPE, "application/json");
+        // Expiration in the past keeps ExpiresSoon() true, so the provider
+        // re-reads the token file on the next call instead of serving its 
cache.
+        HttpChannel::send_reply(req,
+                                
R"({"AccessKeyId":"AKIDTEST","SecretAccessKey":"SECRETTEST",)"
+                                
R"("Token":"SESSIONTEST","Expiration":"1970-01-01T00:00:00Z"})");
+    }
+
+    std::vector<std::string> auth_headers() {
+        std::lock_guard<std::mutex> lock(_mutex);
+        return _auth_headers;
+    }
+
+private:
+    std::mutex _mutex;
+    std::vector<std::string> _auth_headers;
+};
+
+// Starts the mock endpoint on an OS-assigned loopback port and hands out its 
URL.
+class ContainerCredentialsEndpoint {
+public:
+    bool start() {
+        if (!_server.register_handler(GET, "/creds", &_handler)) {
+            return false;
+        }
+        _server.start();
+        if (_server.get_real_port() == 0) {
+            return false;
+        }
+        _url = "http://127.0.0.1:"; + std::to_string(_server.get_real_port()) + 
"/creds";
+        return true;
+    }
+
+    const std::string& url() const { return _url; }
+
+    std::vector<std::string> auth_headers() { return _handler.auth_headers(); }
+
+private:
+    // _handler is declared before _server so that it is destroyed after it: 
~EvHttpServer() stops
+    // the server, and stop() only returns once every worker thread has left 
its event loop, so no
+    // request can still be inside _handler by then.
+    ContainerCredentialsHandler _handler;
+    EvHttpServer _server {0};
+    std::string _url;
+};
+
+} // namespace doris
diff --git a/cloud/test/s3_accessor_mock_test.cpp 
b/cloud/test/s3_accessor_mock_test.cpp
index 30624e82649..5e9d985777e 100644
--- a/cloud/test/s3_accessor_mock_test.cpp
+++ b/cloud/test/s3_accessor_mock_test.cpp
@@ -16,6 +16,7 @@
 // under the License.
 
 #include <aws/core/Aws.h>
+#include <aws/core/auth/GeneralHTTPCredentialsProvider.h>
 #include <aws/s3/S3Client.h>
 #include <aws/s3/model/ListObjectsV2Request.h>
 #include <aws/s3/model/ListObjectsV2Result.h>
@@ -23,10 +24,17 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <cstdio>
+#include <memory>
+#include <string>
+
 #include "common/config.h"
 #include "common/logging.h"
+#include "cpp/aws_common.h"
 #include "cpp/obj-client/s3_obj_storage_client.h"
 #include "cpp/sync_point.h"
+#include "cpp/test/container_credentials_test_util.h"
+#include "recycler/s3_accessor.h"
 
 using namespace doris;
 using namespace Aws::S3::Model;
@@ -86,4 +94,70 @@ TEST_F(S3AccessorMockTest, list_objects_compatibility) {
     EXPECT_TRUE(objects.empty());
 }
 
+namespace {
+
+class ProviderProbe : public S3Accessor {
+public:
+    ProviderProbe() : S3Accessor(S3Conf {}) {}
+
+    // The recycler builds its provider through 
create_aws_credentials_provider(), so the probe goes
+    // through the same call rather than reaching past it. An S3Conf with no 
ak/sk and no role_arn is
+    // what a vault configured for container credentials looks like, and it 
makes the factory return
+    // the CONTAINER base provider unwrapped.
+    std::shared_ptr<Aws::Auth::AWSCredentialsProvider> container_provider() {
+        S3Conf conf;
+        conf.cred_provider_type = CredProviderType::Container;
+        return create_aws_credentials_provider(conf).provider;
+    }
+};
+
+} // namespace
+
+// The recycler reaches CredProviderType::Container through a storage vault's 
persisted
+// cred_provider_type, and on EKS the pod's credentials live behind
+// AWS_CONTAINER_CREDENTIALS_FULL_URI with the token in a kubelet-rotated 
file. Reading only
+// AWS_CONTAINER_CREDENTIALS_RELATIVE_URI leaves this provider with no 
endpoint, so recycling silently
+// reclaims nothing.
+TEST_F(S3AccessorMockTest, 
container_provider_uses_pod_identity_full_uri_and_token_file) {
+    ContainerCredentialsEnvGuard env;
+    const std::string token_path = env.token_file_path("cloud_pod_identity");
+    env.write_token_file(token_path, "token-one");
+    env.set_pod_identity("http://127.0.0.1:65000/creds";, token_path);
+
+    ProviderProbe probe;
+    EXPECT_NE(as_valid_http_provider(probe.container_provider()), nullptr)
+            << "CONTAINER did not yield a usable container credentials 
provider for "
+               "AWS_CONTAINER_CREDENTIALS_FULL_URI";
+
+    ASSERT_EQ(std::remove(token_path.c_str()), 0);
+    EXPECT_EQ(as_valid_http_provider(probe.container_provider()), nullptr)
+            << "CONTAINER ignored AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE: the 
provider stayed "
+               "valid "
+               "with the token file removed, so the path was never forwarded";
+}
+
+// ECS is the other half of the same provider mode: forwarding the full URI 
must not cost the
+// relative one, which the provider resolves against the ECS agent's own 
address.
+TEST_F(S3AccessorMockTest, container_provider_still_honours_ecs_relative_uri) {
+    ContainerCredentialsEnvGuard env;
+    env.set_ecs_task_role("/v2/credentials/mock");
+
+    ProviderProbe probe;
+    EXPECT_NE(as_valid_http_provider(probe.container_provider()), nullptr)
+            << "CONTAINER did not yield a usable container credentials 
provider for "
+               "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
+}
+
+// Pins the discriminator the two tests above rely on: with neither URI 
exported the provider really
+// is unusable, so their assertions cannot be passing vacuously.
+TEST_F(S3AccessorMockTest, container_provider_is_unusable_without_any_uri) {
+    ContainerCredentialsEnvGuard env;
+
+    ProviderProbe probe;
+    auto provider = probe.container_provider();
+    
ASSERT_NE(std::dynamic_pointer_cast<Aws::Auth::GeneralHTTPCredentialsProvider>(provider),
+              nullptr);
+    EXPECT_EQ(as_valid_http_provider(provider), nullptr);
+}
+
 } // namespace doris::cloud
diff --git a/common/cpp/aws_common.cpp b/common/cpp/aws_common.cpp
index 7f11d8cb95e..41dc9debaca 100644
--- a/common/cpp/aws_common.cpp
+++ b/common/cpp/aws_common.cpp
@@ -17,11 +17,18 @@
 
 #include "aws_common.h"
 
+#include <aws/core/auth/GeneralHTTPCredentialsProvider.h>
 #include <aws/core/client/ClientConfiguration.h>
+#include <aws/core/platform/Environment.h>
+#include <aws/core/utils/memory/AWSMemory.h>
 #include <glog/logging.h>
 
 namespace doris {
 
+namespace {
+const char CONTAINER_CREDENTIALS_PROVIDER_TAG[] = 
"ContainerCredentialsProvider";
+} // namespace
+
 CredProviderType cred_provider_type_from_pb(cloud::CredProviderTypePB 
cred_provider_type) {
     switch (cred_provider_type) {
     case cloud::CredProviderTypePB::DEFAULT:
@@ -77,6 +84,57 @@ CredProviderType cred_provider_type_from_string(const 
std::string& type) {
     return CredProviderType::Default;
 }
 
+bool container_credentials_available() {
+    return 
!Aws::Environment::GetEnv(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI).empty() ||
+           
!Aws::Environment::GetEnv(AWS_CONTAINER_CREDENTIALS_FULL_URI).empty();
+}
+
+std::shared_ptr<Aws::Auth::AWSCredentialsProvider> 
create_container_credentials_provider() {
+    const auto relative_uri = 
Aws::Environment::GetEnv(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI);
+    const auto absolute_uri = 
Aws::Environment::GetEnv(AWS_CONTAINER_CREDENTIALS_FULL_URI);
+    const auto token = 
Aws::Environment::GetEnv(AWS_CONTAINER_AUTHORIZATION_TOKEN);
+    const auto token_path = 
Aws::Environment::GetEnv(AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE);
+
+    // Both URIs are forwarded and the provider decides between them: a 
non-empty relative URI wins
+    // and is resolved against the ECS agent's address, otherwise the full URI 
is used as-is. This
+    // is the same precedence the AWS SDK's own default chain applies.
+    //
+    // Both token forms are forwarded for the same reason. The endpoint 
authenticates every fetch
+    // with a bearer token, which the provider takes either inline or as a 
file path, and given a
+    // path it re-reads the file before each fetch. ECS sets only the inline 
variable, EKS Pod
+    // Identity sets only the file one - so forwarding the path is what makes 
the Authorization header
+    // non-empty under Pod Identity, and what keeps it valid once the kubelet 
rotates the file.
+    //
+    // NOTE: The header file names its third parameter authTokenFilePath and 
its fourth authToken,
+    // but the implementation binds them the other way round. The header is 
the side that is wrong,
+    // not the definition. This is reported as aws/aws-sdk-cpp#3143, fixed by
+    // aws/aws-sdk-cpp#3162.
+    auto provider = Aws::MakeShared<Aws::Auth::GeneralHTTPCredentialsProvider>(
+            CONTAINER_CREDENTIALS_PROVIDER_TAG, relative_uri, absolute_uri, 
token, token_path);
+
+    const bool uses_relative_uri = !relative_uri.empty();
+    const char* const uri_var = uses_relative_uri ? 
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
+                                                  : 
AWS_CONTAINER_CREDENTIALS_FULL_URI;
+    const auto& uri = uses_relative_uri ? relative_uri : absolute_uri;
+
+    if (relative_uri.empty() && absolute_uri.empty()) {
+        LOG(WARNING) << "Container credentials provider has no endpoint to 
call and will return no "
+                        "credentials: neither "
+                     << AWS_CONTAINER_CREDENTIALS_RELATIVE_URI << " nor "
+                     << AWS_CONTAINER_CREDENTIALS_FULL_URI << " is set.";
+    } else {
+        LOG(INFO)
+                << "Created container credentials provider from " << uri_var 
<< ": [" << uri
+                << "] with a" << (token.empty() ? "n empty" : " non-empty")
+                << " inline authorization token and a"
+                << (token_path.empty() ? "n empty" : " non-empty")
+                << " authorization token file path: [" << token_path
+                << "]. If credentials come back empty, raise aws_log_level to 
3 or higher for the "
+                   "SDK's own reason.";
+    }
+    return provider;
+}
+
 std::string get_valid_ca_cert_path(const std::vector<std::string>& 
ca_cert_file_paths) {
     for (const auto& path : ca_cert_file_paths) {
         if (std::filesystem::exists(path)) {
diff --git a/common/cpp/aws_common.h b/common/cpp/aws_common.h
index d977dd245b0..9c48ca5133e 100644
--- a/common/cpp/aws_common.h
+++ b/common/cpp/aws_common.h
@@ -20,6 +20,11 @@
 #include <gen_cpp/cloud.pb.h>
 
 #include <filesystem>
+#include <memory>
+
+namespace Aws::Auth {
+class AWSCredentialsProvider;
+}
 
 namespace Aws::Client {
 struct ClientConfiguration;
@@ -38,10 +43,33 @@ enum class CredProviderType {
     Anonymous = 7
 };
 
+// The environment variables through which a container runtime advertises its 
credentials
+// endpoint. ECS exports a relative URI, which is a path resolved against the 
ECS agent's fixed
+// link-local address, and optionally an inline authorization token. EKS Pod 
Identity exports a
+// full URI, which is a complete URL, and an authorization token file that the 
kubelet rotates in
+// place. A runtime never sets both URIs, so code that reads only one of them 
silently gets
+// nothing on the other platform.
+inline constexpr char AWS_CONTAINER_CREDENTIALS_RELATIVE_URI[] =
+        "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
+inline constexpr char AWS_CONTAINER_CREDENTIALS_FULL_URI[] = 
"AWS_CONTAINER_CREDENTIALS_FULL_URI";
+inline constexpr char AWS_CONTAINER_AUTHORIZATION_TOKEN[] = 
"AWS_CONTAINER_AUTHORIZATION_TOKEN";
+inline constexpr char AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE[] =
+        "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE";
+
 CredProviderType cred_provider_type_from_pb(cloud::CredProviderTypePB 
cred_provider_type);
 
 CredProviderType cred_provider_type_from_string(const std::string& type);
 
+// Builds the credentials provider that talks to a container runtime's 
credentials endpoint, from
+// the four AWS_CONTAINER_* environment variables above. Every caller that 
asks for container credentials
+// - the default provider chain, and the explicit CONTAINER/ECS provider modes 
of the BE S3 client
+// factory, the cloud recycler and the Kafka MSK IAM signer - has to support 
both ECS and EKS Pod
+// Identity, so none of them can afford to read a subset of the variables.
+std::shared_ptr<Aws::Auth::AWSCredentialsProvider> 
create_container_credentials_provider();
+
+// True when the runtime advertises a container credentials endpoint through 
either URI variable.
+bool container_credentials_available();
+
 std::string get_valid_ca_cert_path(const std::vector<std::string>& 
ca_cert_file_paths);
 
 // Configures the default S3 client transport scheme for endpoints without an 
explicit scheme.
diff --git a/common/cpp/custom_aws_credentials_provider_chain.cpp 
b/common/cpp/custom_aws_credentials_provider_chain.cpp
index 5b8ae485bd8..6727136d751 100644
--- a/common/cpp/custom_aws_credentials_provider_chain.cpp
+++ b/common/cpp/custom_aws_credentials_provider_chain.cpp
@@ -18,72 +18,44 @@
 #include "custom_aws_credentials_provider_chain.h"
 
 #include <aws/core/auth/AWSCredentialsProviderChain.h>
-#include <aws/core/auth/STSCredentialsProvider.h>
 #include <aws/core/auth/SSOCredentialsProvider.h>
+#include <aws/core/auth/STSCredentialsProvider.h>
 #include <aws/core/platform/Environment.h>
-#include <aws/core/utils/memory/AWSMemory.h>
 #include <aws/core/utils/StringUtils.h>
 #include <aws/core/utils/logging/LogMacros.h>
+#include <aws/core/utils/memory/AWSMemory.h>
+
+#include "aws_common.h"
 
 namespace doris {
 
 using namespace Aws::Auth;
 using namespace Aws::Utils::Threading;
 
-static const char AWS_ECS_CONTAINER_CREDENTIALS_RELATIVE_URI[] =
-        "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
-static const char AWS_ECS_CONTAINER_CREDENTIALS_FULL_URI[] = 
"AWS_CONTAINER_CREDENTIALS_FULL_URI";
-static const char AWS_ECS_CONTAINER_AUTHORIZATION_TOKEN[] = 
"AWS_CONTAINER_AUTHORIZATION_TOKEN";
 static const char AWS_EC2_METADATA_DISABLED[] = "AWS_EC2_METADATA_DISABLED";
 static const char DefaultCredentialsProviderChainTag[] = 
"DefaultAWSCredentialsProviderChain";
 
 CustomAwsCredentialsProviderChain::CustomAwsCredentialsProviderChain()
         : AWSCredentialsProviderChain() {
-
     AddProvider(Aws::MakeShared<STSAssumeRoleWebIdentityCredentialsProvider>(
             DefaultCredentialsProviderChainTag));
 
-    //ECS TaskRole Credentials only available when ENVIRONMENT VARIABLE is set
-    const auto relativeUri = 
Aws::Environment::GetEnv(AWS_ECS_CONTAINER_CREDENTIALS_RELATIVE_URI);
-    AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag,
-                        "The environment variable value "
-                                << AWS_ECS_CONTAINER_CREDENTIALS_RELATIVE_URI 
<< " is "
-                                << relativeUri);
-
-    const auto absoluteUri = 
Aws::Environment::GetEnv(AWS_ECS_CONTAINER_CREDENTIALS_FULL_URI);
-    AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag,
-                        "The environment variable value " << 
AWS_ECS_CONTAINER_CREDENTIALS_FULL_URI
-                                                          << " is " << 
absoluteUri);
-
     const auto ec2MetadataDisabled = 
Aws::Environment::GetEnv(AWS_EC2_METADATA_DISABLED);
     AWS_LOGSTREAM_DEBUG(DefaultCredentialsProviderChainTag,
                         "The environment variable value " << 
AWS_EC2_METADATA_DISABLED << " is "
                                                           << 
ec2MetadataDisabled);
 
-    if (!relativeUri.empty()) {
-        
AddProvider(Aws::MakeShared<TaskRoleCredentialsProvider>(DefaultCredentialsProviderChainTag,
-                                                                 
relativeUri.c_str()));
-        AWS_LOGSTREAM_INFO(DefaultCredentialsProviderChainTag,
-                           "Added ECS metadata service credentials provider 
with relative path: ["
-                                   << relativeUri << "] to the provider 
chain.");
-    } else if (!absoluteUri.empty()) {
-        const auto token = 
Aws::Environment::GetEnv(AWS_ECS_CONTAINER_AUTHORIZATION_TOKEN);
-        AddProvider(Aws::MakeShared<TaskRoleCredentialsProvider>(
-                DefaultCredentialsProviderChainTag, absoluteUri.c_str(), 
token.c_str()));
-
-        //DO NOT log the value of the authorization token for security 
purposes.
-        AWS_LOGSTREAM_INFO(DefaultCredentialsProviderChainTag,
-                           "Added ECS credentials provider with URI: ["
-                                   << absoluteUri << "] to the provider chain 
with a"
-                                   << (token.empty() ? "n empty " : " 
non-empty ")
-                                   << "authorization token.");
+    // One provider serves both container platforms: 
create_container_credentials_provider() reads
+    // all four AWS_CONTAINER_* variables, applies the provider's own 
relative-then-absolute
+    // precedence, and logs which variable supplied the endpoint.
+    if (container_credentials_available()) {
+        AddProvider(create_container_credentials_provider());
     }
 
     AddProvider(Aws::MakeShared<InstanceProfileCredentialsProvider>(
             DefaultCredentialsProviderChainTag));
-    AWS_LOGSTREAM_INFO(
-            DefaultCredentialsProviderChainTag,
-            "Added EC2 metadata service credentials provider to the provider 
chain.");
+    AWS_LOGSTREAM_INFO(DefaultCredentialsProviderChainTag,
+                       "Added EC2 metadata service credentials provider to the 
provider chain.");
 
     AddProvider(
             
Aws::MakeShared<EnvironmentAWSCredentialsProvider>(DefaultCredentialsProviderChainTag));
diff --git a/common/cpp/obj-client/auth/aws_credential_factory.cpp 
b/common/cpp/obj-client/auth/aws_credential_factory.cpp
index f8d47411b34..8c49b4bd728 100644
--- a/common/cpp/obj-client/auth/aws_credential_factory.cpp
+++ b/common/cpp/obj-client/auth/aws_credential_factory.cpp
@@ -21,7 +21,6 @@
 #include <aws/core/auth/AWSCredentialsProvider.h>
 #include <aws/core/auth/AWSCredentialsProviderChain.h>
 #include <aws/core/auth/STSCredentialsProvider.h>
-#include <aws/core/platform/Environment.h>
 #include <aws/identity-management/auth/STSAssumeRoleCredentialsProvider.h>
 #include <aws/sts/STSClient.h>
 
@@ -41,8 +40,7 @@ std::shared_ptr<Provider> 
create_v2_base_provider(CredProviderType type) {
     case CredProviderType::WebIdentity:
         return 
std::make_shared<Aws::Auth::STSAssumeRoleWebIdentityCredentialsProvider>();
     case CredProviderType::Container:
-        return std::make_shared<Aws::Auth::TaskRoleCredentialsProvider>(
-                
Aws::Environment::GetEnv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI").c_str());
+        return create_container_credentials_provider();
     case CredProviderType::Anonymous:
         return std::make_shared<Aws::Auth::AnonymousAWSCredentialsProvider>();
     case CredProviderType::Default:
diff --git a/common/cpp/test/container_credentials_test_util.h 
b/common/cpp/test/container_credentials_test_util.h
new file mode 100644
index 00000000000..e76d038bc6a
--- /dev/null
+++ b/common/cpp/test/container_credentials_test_util.h
@@ -0,0 +1,131 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <aws/core/auth/AWSCredentialsProvider.h>
+#include <aws/core/auth/GeneralHTTPCredentialsProvider.h>
+#include <unistd.h>
+
+#include <array>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "cpp/aws_common.h"
+
+// Test support for the container credentials provider built by
+// create_container_credentials_provider(). It lives in common/cpp/test 
because both test trees need
+// it and neither can include the other's: the BE unit tests cover the S3 
client factory and the
+// Kafka MSK IAM signer, the cloud unit tests cover the recycler, and all 
three reach the same
+// factory.
+namespace doris {
+
+// Clears the four AWS_CONTAINER_* variables on construction and restores them 
on destruction.
+class ContainerCredentialsEnvGuard {
+public:
+    ContainerCredentialsEnvGuard() {
+        for (const char* name : kNames) {
+            const char* value = std::getenv(name);
+            _saved.emplace_back(value == nullptr ? std::nullopt
+                                                 : 
std::optional<std::string>(value));
+            unsetenv(name);
+        }
+    }
+
+    ~ContainerCredentialsEnvGuard() {
+        for (size_t i = 0; i < kNames.size(); ++i) {
+            if (_saved[i].has_value()) {
+                setenv(kNames[i], _saved[i]->c_str(), 1);
+            } else {
+                unsetenv(kNames[i]);
+            }
+        }
+        for (const auto& path : _token_files) {
+            std::remove(path.c_str());
+        }
+    }
+
+    ContainerCredentialsEnvGuard(const ContainerCredentialsEnvGuard&) = delete;
+    ContainerCredentialsEnvGuard& operator=(const 
ContainerCredentialsEnvGuard&) = delete;
+
+    // Presents the environment the way EKS Pod Identity does: a full URL, a 
token that exists only
+    // as a file, and no relative URI or inline token at all.
+    void set_pod_identity(const std::string& full_uri, const std::string& 
token_file) {
+        unsetenv(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI);
+        unsetenv(AWS_CONTAINER_AUTHORIZATION_TOKEN);
+        setenv(AWS_CONTAINER_CREDENTIALS_FULL_URI, full_uri.c_str(), 1);
+        setenv(AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, token_file.c_str(), 1);
+    }
+
+    // Presents the environment the way ECS does: a relative path against the 
agent's own address,
+    // and no full URI or token file.
+    void set_ecs_task_role(const std::string& relative_uri) {
+        unsetenv(AWS_CONTAINER_CREDENTIALS_FULL_URI);
+        unsetenv(AWS_CONTAINER_AUTHORIZATION_TOKEN);
+        unsetenv(AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE);
+        setenv(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, relative_uri.c_str(), 
1);
+    }
+
+    void set_inline_token_endpoint(const std::string& full_uri, const 
std::string& token) {
+        unsetenv(AWS_CONTAINER_CREDENTIALS_RELATIVE_URI);
+        unsetenv(AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE);
+        setenv(AWS_CONTAINER_CREDENTIALS_FULL_URI, full_uri.c_str(), 1);
+        setenv(AWS_CONTAINER_AUTHORIZATION_TOKEN, token.c_str(), 1);
+    }
+
+    // Writes a token file and takes responsibility for deleting it. Call it 
again with the same path
+    // to rotate the token the way the kubelet does.
+    void write_token_file(const std::string& path, const std::string& 
contents) {
+        std::ofstream out(path, std::ios::trunc);
+        out << contents;
+        out.flush();
+        _token_files.push_back(path);
+    }
+
+    // A token file path unique to this test and this process
+    std::string token_file_path(const std::string& name) const {
+        return "/tmp/doris_container_credentials_token_" + name + "_" + 
std::to_string(getpid());
+    }
+
+private:
+    static constexpr std::array<const char*, 4> kNames {
+            AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, 
AWS_CONTAINER_CREDENTIALS_FULL_URI,
+            AWS_CONTAINER_AUTHORIZATION_TOKEN, 
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE};
+
+    std::vector<std::optional<std::string>> _saved;
+    std::vector<std::string> _token_files;
+};
+
+// Returns the provider only if it is a container credentials provider that 
can actually reach an
+// endpoint, and nullptr otherwise.
+inline std::shared_ptr<Aws::Auth::GeneralHTTPCredentialsProvider> 
as_valid_http_provider(
+        const std::shared_ptr<Aws::Auth::AWSCredentialsProvider>& provider) {
+    auto http_provider =
+            
std::dynamic_pointer_cast<Aws::Auth::GeneralHTTPCredentialsProvider>(provider);
+    if (http_provider != nullptr && !http_provider->IsValid()) {
+        return nullptr;
+    }
+    return http_provider;
+}
+
+} // namespace doris
diff --git a/gensrc/proto/cloud.proto b/gensrc/proto/cloud.proto
index 4addf9385b6..a63ac2d5181 100644
--- a/gensrc/proto/cloud.proto
+++ b/gensrc/proto/cloud.proto
@@ -283,7 +283,7 @@ enum CredProviderTypePB {
     ENV = 4;  // EnvironmentAWSCredentialsProvider
     SYSTEM_PROPERTIES = 5;  // SystemPropertiesCredentialsProvider
     WEB_IDENTITY = 6;  // STSAssumeRoleWebIdentityCredentialsProvider
-    CONTAINER = 7;  // TaskRoleCredentialsProvider
+    CONTAINER = 7;  // GeneralHTTPCredentialsProvider, for ECS task roles and 
EKS Pod Identity
     ANONYMOUS = 8;  // AnonymousAWSCredentialsProvider
 }
 
diff --git a/gensrc/thrift/AgentService.thrift 
b/gensrc/thrift/AgentService.thrift
index fb5c1a54931..5ca0f531c06 100644
--- a/gensrc/thrift/AgentService.thrift
+++ b/gensrc/thrift/AgentService.thrift
@@ -106,7 +106,7 @@ enum TCredProviderType {
     ENV = 3,  // EnvironmentAWSCredentialsProvider
     SYSTEM_PROPERTIES = 4,  // SystemPropertiesCredentialsProvider
     WEB_IDENTITY = 5,  // STSAssumeRoleWebIdentityCredentialsProvider
-    CONTAINER = 6,  // TaskRoleCredentialsProvider
+    CONTAINER = 6,  // GeneralHTTPCredentialsProvider, for ECS task roles and 
EKS Pod Identity
     ANONYMOUS = 7  // AnonymousAWSCredentialsProvider
 }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to