Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions core/src/services/s3/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use reqsign::AwsConfig;
use reqsign::AwsCredentialLoad;
use reqsign::AwsDefaultLoader;
use reqsign::AwsV4Signer;
use reqsign::ECSCredentialProvider;
use reqwest::Url;

use super::core::*;
Expand Down Expand Up @@ -402,6 +403,58 @@ impl S3Builder {
self.session_token(token)
}

/// Set container credentials relative URI for ECS Task IAM roles.
///
/// Used in ECS environments where the base metadata endpoint is known.
/// Example: "/v2/credentials/my-role-name"
pub fn container_credentials_relative_uri(mut self, uri: &str) -> Self {
if !uri.is_empty() {
self.config.container_credentials_relative_uri = Some(uri.to_string());
}
self
}

/// Set container credentials endpoint for Fargate or custom setups.
///
/// Complete URL for fetching credentials. Used in Fargate environments.
/// Example: "http://169.254.170.2/v2/credentials/my-role"
pub fn container_credentials_endpoint(mut self, endpoint: &str) -> Self {
if !endpoint.is_empty() {
self.config.container_credentials_endpoint = Some(endpoint.to_string());
}
self
}

/// Set authorization token for container credentials requests.
///
/// Token used for authenticating with the container credentials endpoint.
pub fn container_authorization_token(mut self, token: &str) -> Self {
if !token.is_empty() {
self.config.container_authorization_token = Some(token.to_string());
}
self
}

/// Set path to file containing authorization token for container credentials.
///
/// File should contain the authorization token for ECS credentials endpoint.
pub fn container_authorization_token_file(mut self, file_path: &str) -> Self {
if !file_path.is_empty() {
self.config.container_authorization_token_file = Some(file_path.to_string());
}
self
}

/// Set override for the container metadata URI base endpoint.
///
/// Used to override the default http://169.254.170.2 endpoint, typically for testing.
pub fn container_metadata_uri_override(mut self, uri: &str) -> Self {
if !uri.is_empty() {
self.config.container_metadata_uri_override = Some(uri.to_string());
}
self
}

/// Disable config load so that opendal will not load config from
/// environment.
///
Expand Down Expand Up @@ -495,6 +548,15 @@ impl S3Builder {
self
}

/// Check if ECS container credentials configuration is present
fn has_ecs_config(&self) -> bool {
self.config.container_credentials_endpoint.is_some()
|| self.config.container_credentials_relative_uri.is_some()
|| self.config.container_authorization_token.is_some()
|| self.config.container_authorization_token_file.is_some()
|| self.config.container_metadata_uri_override.is_some()
}

/// Check if `bucket` is valid
/// `bucket` must be not empty and if `enable_virtual_host_style` is true
/// it couldn't contain dot(.) character
Expand Down Expand Up @@ -844,6 +906,33 @@ impl Builder for S3Builder {
loader = Some(v);
}

// If ECS container credentials are configured, use ECSCredentialProvider
if loader.is_none() && self.has_ecs_config() {
let mut ecs_provider = ECSCredentialProvider::new();

if let Some(ref endpoint) = self.config.container_credentials_endpoint {
ecs_provider = ecs_provider.with_endpoint(endpoint);
}

if let Some(ref relative_uri) = self.config.container_credentials_relative_uri {
ecs_provider = ecs_provider.with_relative_uri(relative_uri);
}

if let Some(ref token) = self.config.container_authorization_token {
ecs_provider = ecs_provider.with_auth_token(token);
}

if let Some(ref token_file) = self.config.container_authorization_token_file {
ecs_provider = ecs_provider.with_auth_token_file(token_file);
}

if let Some(ref uri_override) = self.config.container_metadata_uri_override {
ecs_provider = ecs_provider.with_metadata_uri_override(uri_override);
}

loader = Some(Box::new(ecs_provider));
}

// If role_arn is set, we must use AssumeRoleLoad.
if let Some(role_arn) = self.config.role_arn {
// use current env as source credential loader.
Expand Down
39 changes: 39 additions & 0 deletions core/src/services/s3/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,45 @@ pub struct S3Config {

/// Indicates whether the client agrees to pay for the requests made to the S3 bucket.
pub enable_request_payer: bool,

/// Container credentials relative URI for ECS Task IAM roles.
///
/// Used in ECS environments where the base metadata endpoint is known.
/// Example: "/v2/credentials/my-role-name"
#[serde(alias = "aws_container_credentials_relative_uri")]
pub container_credentials_relative_uri: Option<String>,

/// Container credentials full endpoint for Fargate or custom setups.
///
/// Complete URL for fetching credentials. Used in Fargate environments.
/// Example: "http://169.254.170.2/v2/credentials/my-role"
#[serde(
alias = "container_credentials_full_uri",
alias = "aws_container_credentials_full_uri"
)]
pub container_credentials_endpoint: Option<String>,

/// Authorization token for container credentials requests.
///
/// Token used for authenticating with the container credentials endpoint.
#[serde(alias = "aws_container_authorization_token")]
pub container_authorization_token: Option<String>,

/// Path to file containing authorization token for container credentials.
///
/// File should contain the authorization token for ECS credentials endpoint.
#[serde(alias = "aws_container_authorization_token_file")]
pub container_authorization_token_file: Option<String>,

/// Override for the container metadata URI base endpoint.
///
/// Used to override the default http://169.254.170.2 endpoint, typically for testing.
#[serde(
alias = "aws_container_metadata_uri_override",
alias = "aws_metadata_endpoint",
alias = "metadata_endpoint"
)]
pub container_metadata_uri_override: Option<String>,
}

impl Debug for S3Config {
Expand Down
Loading