fix use_fips in provider config (#3007)

I'm not 100% that I fixed this in the right way. Feel free to set me
straight if that's the case.

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here -->
aws-sdk-rust#882

## Description
<!--- Describe your changes in detail -->
This change causes the`ProviderConfig` to respect both `use_fips` and
`use_dual_stack` when those settings are configured in a user's
environment or profile.

## Testing
<!--- Please describe in detail how you tested your changes -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
I wrote two tests

## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS
SDK, generated SDK code, or SDK runtime crates

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._

---------

Co-authored-by: John DiSanti <jdisanti@amazon.com>
This commit is contained in:
Zelda Hessler 2023-10-04 10:02:14 -05:00 committed by GitHub
parent 5129c1f5f0
commit bb35688696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 384 additions and 84 deletions

View File

@ -261,3 +261,9 @@ For more information, see the [Change Log Discussion](https://github.com/awslabs
meta = { "breaking" = true, "tada" = false, "bug" = false } meta = { "breaking" = true, "tada" = false, "bug" = false }
references = ["smithy-rs#3014"] references = ["smithy-rs#3014"]
author = "rcoh" author = "rcoh"
[[aws-sdk-rust]]
message = "STS and SSO-based credential providers will now respect both `use_fips` and `use_dual_stack` when those settings are configured in a user's environment or profile."
references = ["aws-sdk-rust#882", "smithy-rs#3007"]
meta = { "breaking" = true, "tada" = true, "bug" = true }
author = "Velfi"

View File

@ -306,6 +306,9 @@ mod test {
#[cfg(feature = "credentials-sso")] #[cfg(feature = "credentials-sso")]
make_test!(sso_no_token_file); make_test!(sso_no_token_file);
#[cfg(feature = "credentials-sso")]
make_test!(e2e_fips_and_dual_stack_sso);
#[tokio::test] #[tokio::test]
async fn profile_name_override() { async fn profile_name_override() {
let conf = let conf =

View File

@ -245,6 +245,6 @@ mod test {
fn real_environment() { fn real_environment() {
let provider = EnvironmentVariableCredentialsProvider::new(); let provider = EnvironmentVariableCredentialsProvider::new();
// we don't know what's in the env, just make sure it doesn't crash. // we don't know what's in the env, just make sure it doesn't crash.
let _ = provider.provide_credentials(); let _fut = provider.provide_credentials();
} }
} }

View File

@ -589,6 +589,23 @@ mod loader {
.with_http_connector(http_connector.clone()) .with_http_connector(http_connector.clone())
}) })
.with_profile_config(self.profile_files_override, self.profile_name_override); .with_profile_config(self.profile_files_override, self.profile_name_override);
let use_fips = if let Some(use_fips) = self.use_fips {
Some(use_fips)
} else {
use_fips_provider(&conf).await
};
let use_dual_stack = if let Some(use_dual_stack) = self.use_dual_stack {
Some(use_dual_stack)
} else {
use_dual_stack_provider(&conf).await
};
let conf = conf
.with_use_fips(use_fips)
.with_use_dual_stack(use_dual_stack);
let region = if let Some(provider) = self.region { let region = if let Some(provider) = self.region {
provider.region().await provider.region().await
} else { } else {
@ -648,18 +665,6 @@ mod loader {
None None
}; };
let use_fips = if let Some(use_fips) = self.use_fips {
Some(use_fips)
} else {
use_fips_provider(&conf).await
};
let use_dual_stack = if let Some(use_dual_stack) = self.use_dual_stack {
Some(use_dual_stack)
} else {
use_dual_stack_provider(&conf).await
};
let mut builder = SdkConfig::builder() let mut builder = SdkConfig::builder()
.region(region) .region(region)
.retry_config(retry_config) .retry_config(retry_config)

View File

@ -22,15 +22,13 @@
//! - `exec` which contains a chain representation of providers to implement passing bootstrapped credentials //! - `exec` which contains a chain representation of providers to implement passing bootstrapped credentials
//! through a series of providers. //! through a series of providers.
use crate::profile::credentials::exec::named::NamedProviderFactory;
use crate::profile::credentials::exec::ProviderChain;
use crate::profile::parser::ProfileFileLoadError; use crate::profile::parser::ProfileFileLoadError;
use crate::profile::profile_file::ProfileFiles; use crate::profile::profile_file::ProfileFiles;
use crate::profile::Profile; use crate::profile::Profile;
use crate::provider_config::ProviderConfig; use crate::provider_config::ProviderConfig;
use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials};
use aws_sdk_sts::config::Builder as StsConfigBuilder;
use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::error::display::DisplayErrorContext;
use aws_types::SdkConfig;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
@ -141,8 +139,8 @@ impl ProvideCredentials for ProfileFileCredentialsProvider {
#[doc = include_str!("location_of_profile_files.md")] #[doc = include_str!("location_of_profile_files.md")]
#[derive(Debug)] #[derive(Debug)]
pub struct ProfileFileCredentialsProvider { pub struct ProfileFileCredentialsProvider {
factory: NamedProviderFactory, factory: exec::named::NamedProviderFactory,
sts_config: StsConfigBuilder, sdk_config: SdkConfig,
provider_config: ProviderConfig, provider_config: ProviderConfig,
} }
@ -182,7 +180,7 @@ impl ProfileFileCredentialsProvider {
}; };
for provider in inner_provider.chain().iter() { for provider in inner_provider.chain().iter() {
let next_creds = provider let next_creds = provider
.credentials(creds, &self.sts_config) .credentials(creds, &self.sdk_config)
.instrument(tracing::debug_span!("load_assume_role", provider = ?provider)) .instrument(tracing::debug_span!("load_assume_role", provider = ?provider))
.await; .await;
match next_creds { match next_creds {
@ -444,7 +442,7 @@ impl Builder {
ProfileFileCredentialsProvider { ProfileFileCredentialsProvider {
factory, factory,
sts_config: conf.sts_client_config(), sdk_config: conf.client_config("profile file"),
provider_config: conf, provider_config: conf,
} }
} }
@ -452,8 +450,8 @@ impl Builder {
async fn build_provider_chain( async fn build_provider_chain(
provider_config: &ProviderConfig, provider_config: &ProviderConfig,
factory: &NamedProviderFactory, factory: &exec::named::NamedProviderFactory,
) -> Result<ProviderChain, ProfileFileError> { ) -> Result<exec::ProviderChain, ProfileFileError> {
let profile_set = provider_config let profile_set = provider_config
.try_profile() .try_profile()
.await .await
@ -485,6 +483,7 @@ mod test {
} }
make_test!(e2e_assume_role); make_test!(e2e_assume_role);
make_test!(e2e_fips_and_dual_stack_sts);
make_test!(empty_config); make_test!(empty_config);
make_test!(retry_on_error); make_test!(retry_on_error);
make_test!(invalid_config); make_test!(invalid_config);

View File

@ -11,10 +11,13 @@ use crate::provider_config::ProviderConfig;
use crate::sso::{SsoCredentialsProvider, SsoProviderConfig}; use crate::sso::{SsoCredentialsProvider, SsoProviderConfig};
use crate::sts; use crate::sts;
use crate::web_identity_token::{StaticConfiguration, WebIdentityTokenCredentialsProvider}; use crate::web_identity_token::{StaticConfiguration, WebIdentityTokenCredentialsProvider};
use aws_credential_types::provider::{self, error::CredentialsError, ProvideCredentials}; use aws_credential_types::provider::{
use aws_sdk_sts::config::{Builder as StsConfigBuilder, Credentials}; self, error::CredentialsError, ProvideCredentials, SharedCredentialsProvider,
};
use aws_sdk_sts::config::Credentials;
use aws_sdk_sts::Client as StsClient; use aws_sdk_sts::Client as StsClient;
use aws_smithy_async::time::SharedTimeSource; use aws_smithy_async::time::SharedTimeSource;
use aws_types::SdkConfig;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
@ -30,13 +33,13 @@ impl AssumeRoleProvider {
pub(super) async fn credentials( pub(super) async fn credentials(
&self, &self,
input_credentials: Credentials, input_credentials: Credentials,
sts_config: &StsConfigBuilder, sdk_config: &SdkConfig,
) -> provider::Result { ) -> provider::Result {
let config = sts_config let config = sdk_config
.clone() .to_builder()
.credentials_provider(input_credentials) .credentials_provider(SharedCredentialsProvider::new(input_credentials))
.build(); .build();
let client = StsClient::from_conf(config); let client = StsClient::new(&config);
let session_name = &self.session_name.as_ref().cloned().unwrap_or_else(|| { let session_name = &self.session_name.as_ref().cloned().unwrap_or_else(|| {
sts::util::default_session_name("assume-role-from-profile", self.time_source.now()) sts::util::default_session_name("assume-role-from-profile", self.time_source.now())
}); });
@ -143,8 +146,8 @@ impl ProviderChain {
tracing::info!(role_arn = ?role_arn, "which will be used to assume a role"); tracing::info!(role_arn = ?role_arn, "which will be used to assume a role");
AssumeRoleProvider { AssumeRoleProvider {
role_arn: role_arn.role_arn.into(), role_arn: role_arn.role_arn.into(),
external_id: role_arn.external_id.map(|id| id.into()), external_id: role_arn.external_id.map(Into::into),
session_name: role_arn.session_name.map(|id| id.into()), session_name: role_arn.session_name.map(Into::into),
time_source: provider_config.time_source(), time_source: provider_config.time_source(),
} }
}) })

View File

@ -5,27 +5,26 @@
//! Configuration Options for Credential Providers //! Configuration Options for Credential Providers
use crate::connector::{default_connector, expect_connector};
use crate::profile;
use crate::profile::profile_file::ProfileFiles;
use crate::profile::{ProfileFileLoadError, ProfileSet};
use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep}; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
use aws_smithy_async::time::SharedTimeSource; use aws_smithy_async::time::SharedTimeSource;
use aws_smithy_client::erase::DynConnector; use aws_smithy_client::erase::DynConnector;
use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::error::display::DisplayErrorContext;
use aws_smithy_types::retry::RetryConfig;
use aws_types::os_shim_internal::{Env, Fs}; use aws_types::os_shim_internal::{Env, Fs};
use aws_types::{ use aws_types::{
http_connector::{ConnectorSettings, HttpConnector}, http_connector::{ConnectorSettings, HttpConnector},
region::Region, region::Region,
SdkConfig,
}; };
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
use crate::connector::default_connector;
use crate::profile;
use crate::profile::profile_file::ProfileFiles;
use crate::profile::{ProfileFileLoadError, ProfileSet};
/// Configuration options for Credential Providers /// Configuration options for Credential Providers
/// ///
/// Most credential providers builders offer a `configure` method which applies general provider configuration /// Most credential providers builders offer a `configure` method which applies general provider configuration
@ -42,6 +41,8 @@ pub struct ProviderConfig {
connector: HttpConnector, connector: HttpConnector,
sleep: Option<SharedAsyncSleep>, sleep: Option<SharedAsyncSleep>,
region: Option<Region>, region: Option<Region>,
use_fips: Option<bool>,
use_dual_stack: Option<bool>,
/// An AWS profile created from `ProfileFiles` and a `profile_name` /// An AWS profile created from `ProfileFiles` and a `profile_name`
parsed_profile: Arc<OnceCell<Result<ProfileSet, ProfileFileLoadError>>>, parsed_profile: Arc<OnceCell<Result<ProfileSet, ProfileFileLoadError>>>,
/// A list of [std::path::Path]s to profile files /// A list of [std::path::Path]s to profile files
@ -57,6 +58,8 @@ impl Debug for ProviderConfig {
.field("fs", &self.fs) .field("fs", &self.fs)
.field("sleep", &self.sleep) .field("sleep", &self.sleep)
.field("region", &self.region) .field("region", &self.region)
.field("use_fips", &self.use_fips)
.field("use_dual_stack", &self.use_dual_stack)
.finish() .finish()
} }
} }
@ -76,6 +79,8 @@ impl Default for ProviderConfig {
connector, connector,
sleep: default_async_sleep(), sleep: default_async_sleep(),
region: None, region: None,
use_fips: None,
use_dual_stack: None,
parsed_profile: Default::default(), parsed_profile: Default::default(),
profile_files: ProfileFiles::default(), profile_files: ProfileFiles::default(),
profile_name_override: None, profile_name_override: None,
@ -104,6 +109,8 @@ impl ProviderConfig {
connector: HttpConnector::Prebuilt(None), connector: HttpConnector::Prebuilt(None),
sleep: None, sleep: None,
region: None, region: None,
use_fips: None,
use_dual_stack: None,
profile_name_override: None, profile_name_override: None,
} }
} }
@ -144,6 +151,8 @@ impl ProviderConfig {
connector: HttpConnector::Prebuilt(None), connector: HttpConnector::Prebuilt(None),
sleep: None, sleep: None,
region: None, region: None,
use_fips: None,
use_dual_stack: None,
parsed_profile: Default::default(), parsed_profile: Default::default(),
profile_files: ProfileFiles::default(), profile_files: ProfileFiles::default(),
profile_name_override: None, profile_name_override: None,
@ -161,6 +170,8 @@ impl ProviderConfig {
connector: HttpConnector::Prebuilt(None), connector: HttpConnector::Prebuilt(None),
sleep, sleep,
region: None, region: None,
use_fips: None,
use_dual_stack: None,
profile_name_override: None, profile_name_override: None,
} }
} }
@ -181,6 +192,21 @@ impl ProviderConfig {
Self::without_region().load_default_region().await Self::without_region().load_default_region().await
} }
pub(crate) fn client_config(&self, feature_name: &str) -> SdkConfig {
let mut builder = SdkConfig::builder()
.http_connector(expect_connector(
&format!("The {feature_name} features of aws-config"),
self.connector(&Default::default()),
))
.retry_config(RetryConfig::standard())
.region(self.region())
.time_source(self.time_source())
.use_fips(self.use_fips().unwrap_or_default())
.use_dual_stack(self.use_dual_stack().unwrap_or_default());
builder.set_sleep_impl(self.sleep());
builder.build()
}
// When all crate features are disabled, these accessors are unused // When all crate features are disabled, these accessors are unused
#[allow(dead_code)] #[allow(dead_code)]
@ -219,6 +245,16 @@ impl ProviderConfig {
self.region.clone() self.region.clone()
} }
#[allow(dead_code)]
pub(crate) fn use_fips(&self) -> Option<bool> {
self.use_fips
}
#[allow(dead_code)]
pub(crate) fn use_dual_stack(&self) -> Option<bool> {
self.use_dual_stack
}
pub(crate) async fn try_profile(&self) -> Result<&ProfileSet, &ProfileFileLoadError> { pub(crate) async fn try_profile(&self) -> Result<&ProfileSet, &ProfileFileLoadError> {
let parsed_profile = self let parsed_profile = self
.parsed_profile .parsed_profile
@ -249,6 +285,18 @@ impl ProviderConfig {
self self
} }
/// Override the `use_fips` setting.
pub(crate) fn with_use_fips(mut self, use_fips: Option<bool>) -> Self {
self.use_fips = use_fips;
self
}
/// Override the `use_dual_stack` setting.
pub(crate) fn with_use_dual_stack(mut self, use_dual_stack: Option<bool>) -> Self {
self.use_dual_stack = use_dual_stack;
self
}
pub(crate) fn with_profile_name(self, profile_name: String) -> Self { pub(crate) fn with_profile_name(self, profile_name: String) -> Self {
let profile_files = self.profile_files.clone(); let profile_files = self.profile_files.clone();
self.with_profile_config(Some(profile_files), Some(profile_name)) self.with_profile_config(Some(profile_files), Some(profile_name))

View File

@ -18,12 +18,13 @@ use aws_credential_types::cache::CredentialsCache;
use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials};
use aws_credential_types::Credentials; use aws_credential_types::Credentials;
use aws_sdk_sso::types::RoleCredentials; use aws_sdk_sso::types::RoleCredentials;
use aws_sdk_sso::{config::Builder as SsoConfigBuilder, Client as SsoClient, Config as SsoConfig}; use aws_sdk_sso::Client as SsoClient;
use aws_smithy_json::deserialize::Token; use aws_smithy_json::deserialize::Token;
use aws_smithy_types::date_time::Format; use aws_smithy_types::date_time::Format;
use aws_smithy_types::DateTime; use aws_smithy_types::DateTime;
use aws_types::os_shim_internal::{Env, Fs}; use aws_types::os_shim_internal::{Env, Fs};
use aws_types::region::Region; use aws_types::region::Region;
use aws_types::SdkConfig;
use std::convert::TryInto; use std::convert::TryInto;
use std::error::Error; use std::error::Error;
@ -31,8 +32,6 @@ use std::fmt::{Display, Formatter};
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use crate::connector::expect_connector;
use aws_smithy_types::retry::RetryConfig;
use ring::digest; use ring::digest;
use zeroize::Zeroizing; use zeroize::Zeroizing;
@ -47,7 +46,7 @@ pub struct SsoCredentialsProvider {
fs: Fs, fs: Fs,
env: Env, env: Env,
sso_provider_config: SsoProviderConfig, sso_provider_config: SsoProviderConfig,
sso_config: SsoConfigBuilder, sdk_config: SdkConfig,
} }
impl SsoCredentialsProvider { impl SsoCredentialsProvider {
@ -63,26 +62,18 @@ impl SsoCredentialsProvider {
let fs = provider_config.fs(); let fs = provider_config.fs();
let env = provider_config.env(); let env = provider_config.env();
let mut sso_config = SsoConfig::builder()
.http_connector(expect_connector(
"The SSO credentials provider",
provider_config.connector(&Default::default()),
))
.retry_config(RetryConfig::standard());
sso_config.set_sleep_impl(provider_config.sleep());
SsoCredentialsProvider { SsoCredentialsProvider {
fs, fs,
env, env,
sso_provider_config, sso_provider_config,
sso_config, sdk_config: provider_config.client_config("SSO"),
} }
} }
async fn credentials(&self) -> provider::Result { async fn credentials(&self) -> provider::Result {
load_sso_credentials( load_sso_credentials(
&self.sso_provider_config, &self.sso_provider_config,
&self.sso_config, &self.sdk_config,
&self.env, &self.env,
&self.fs, &self.fs,
) )
@ -206,20 +197,20 @@ pub(crate) struct SsoProviderConfig {
async fn load_sso_credentials( async fn load_sso_credentials(
sso_provider_config: &SsoProviderConfig, sso_provider_config: &SsoProviderConfig,
sso_config: &SsoConfigBuilder, sdk_config: &SdkConfig,
env: &Env, env: &Env,
fs: &Fs, fs: &Fs,
) -> provider::Result { ) -> provider::Result {
let token = load_token(&sso_provider_config.start_url, env, fs) let token = load_token(&sso_provider_config.start_url, env, fs)
.await .await
.map_err(CredentialsError::provider_error)?; .map_err(CredentialsError::provider_error)?;
let config = sso_config let config = sdk_config
.clone() .to_builder()
.region(sso_provider_config.region.clone()) .region(sso_provider_config.region.clone())
.credentials_cache(CredentialsCache::no_caching()) .credentials_cache(CredentialsCache::no_caching())
.build(); .build();
// TODO(enableNewSmithyRuntimeCleanup): Use `customize().config_override()` to set the region instead of creating a new client once middleware is removed // TODO(enableNewSmithyRuntimeCleanup): Use `customize().config_override()` to set the region instead of creating a new client once middleware is removed
let client = SsoClient::from_conf(config); let client = SsoClient::new(&config);
let resp = client let resp = client
.get_role_credentials() .get_role_credentials()
.role_name(&sso_provider_config.role_name) .role_name(&sso_provider_config.role_name)

View File

@ -10,22 +10,3 @@ pub(crate) mod util;
pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder}; pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder};
mod assume_role; mod assume_role;
use crate::connector::expect_connector;
use aws_sdk_sts::config::Builder as StsConfigBuilder;
use aws_smithy_types::retry::RetryConfig;
impl crate::provider_config::ProviderConfig {
pub(crate) fn sts_client_config(&self) -> StsConfigBuilder {
let mut builder = aws_sdk_sts::Config::builder()
.http_connector(expect_connector(
"The STS features of aws-config",
self.connector(&Default::default()),
))
.retry_config(RetryConfig::standard())
.region(self.region())
.time_source(self.time_source());
builder.set_sleep_impl(self.sleep());
builder
}
}

View File

@ -362,7 +362,7 @@ mod test {
#[tokio::test] #[tokio::test]
async fn configures_session_length() { async fn configures_session_length() {
let (server, request) = capture_request(None); let (server, request) = capture_request(None);
let provider_conf = SdkConfig::builder() let sdk_config = SdkConfig::builder()
.sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .sleep_impl(SharedAsyncSleep::new(TokioSleep::new()))
.time_source(StaticTimeSource::new( .time_source(StaticTimeSource::new(
UNIX_EPOCH + Duration::from_secs(1234567890 - 120), UNIX_EPOCH + Duration::from_secs(1234567890 - 120),
@ -371,7 +371,7 @@ mod test {
.region(Region::from_static("this-will-be-overridden")) .region(Region::from_static("this-will-be-overridden"))
.build(); .build();
let provider = AssumeRoleProvider::builder("myrole") let provider = AssumeRoleProvider::builder("myrole")
.configure(&provider_conf) .configure(&sdk_config)
.region(Region::new("us-east-1")) .region(Region::new("us-east-1"))
.session_length(Duration::from_secs(1234567)) .session_length(Duration::from_secs(1234567))
.build_from_provider(provide_credentials_fn(|| async { .build_from_provider(provide_credentials_fn(|| async {
@ -388,7 +388,7 @@ mod test {
#[tokio::test] #[tokio::test]
async fn loads_region_from_sdk_config() { async fn loads_region_from_sdk_config() {
let (server, request) = capture_request(None); let (server, request) = capture_request(None);
let provider_conf = SdkConfig::builder() let sdk_config = SdkConfig::builder()
.sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .sleep_impl(SharedAsyncSleep::new(TokioSleep::new()))
.time_source(StaticTimeSource::new( .time_source(StaticTimeSource::new(
UNIX_EPOCH + Duration::from_secs(1234567890 - 120), UNIX_EPOCH + Duration::from_secs(1234567890 - 120),
@ -397,14 +397,12 @@ mod test {
.credentials_provider(SharedCredentialsProvider::new(provide_credentials_fn( .credentials_provider(SharedCredentialsProvider::new(provide_credentials_fn(
|| async { || async {
panic!("don't call me — will be overridden"); panic!("don't call me — will be overridden");
#[allow(unreachable_code)]
Ok(Credentials::for_tests())
}, },
))) )))
.region(Region::from_static("us-west-2")) .region(Region::from_static("us-west-2"))
.build(); .build();
let provider = AssumeRoleProvider::builder("myrole") let provider = AssumeRoleProvider::builder("myrole")
.configure(&provider_conf) .configure(&sdk_config)
.session_length(Duration::from_secs(1234567)) .session_length(Duration::from_secs(1234567))
.build_from_provider(provide_credentials_fn(|| async { .build_from_provider(provide_credentials_fn(|| async {
Ok(Credentials::for_tests()) Ok(Credentials::for_tests())
@ -476,7 +474,7 @@ mod test {
UNIX_EPOCH + Duration::from_secs(1234567890 - 120), // 1234567890 since UNIX_EPOCH is 2009-02-13T23:31:30Z UNIX_EPOCH + Duration::from_secs(1234567890 - 120), // 1234567890 since UNIX_EPOCH is 2009-02-13T23:31:30Z
); );
let provider_conf = SdkConfig::builder() let sdk_config = SdkConfig::builder()
.sleep_impl(SharedAsyncSleep::new(sleep)) .sleep_impl(SharedAsyncSleep::new(sleep))
.time_source(testing_time_source.clone()) .time_source(testing_time_source.clone())
.http_connector(DynConnector::new(conn)) .http_connector(DynConnector::new(conn))
@ -499,7 +497,7 @@ mod test {
])); ]));
let credentials_list_cloned = credentials_list.clone(); let credentials_list_cloned = credentials_list.clone();
let provider = AssumeRoleProvider::builder("myrole") let provider = AssumeRoleProvider::builder("myrole")
.configure(&provider_conf) .configure(&sdk_config)
.region(Region::new("us-east-1")) .region(Region::new("us-east-1"))
.build_from_provider(provide_credentials_fn(move || { .build_from_provider(provide_credentials_fn(move || {
let list = credentials_list.clone(); let list = credentials_list.clone();

View File

@ -14,6 +14,8 @@ use aws_types::os_shim_internal::{Env, Fs};
use serde::Deserialize; use serde::Deserialize;
use crate::connector::default_connector; use crate::connector::default_connector;
use crate::default_provider::use_dual_stack::use_dual_stack_provider;
use crate::default_provider::use_fips::use_fips_provider;
use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::error::display::DisplayErrorContext;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
@ -236,6 +238,13 @@ impl TestEnvironment {
.with_sleep(TokioSleep::new()) .with_sleep(TokioSleep::new())
.load_default_region() .load_default_region()
.await; .await;
let use_dual_stack = use_dual_stack_provider(&provider_config).await;
let use_fips = use_fips_provider(&provider_config).await;
let provider_config = provider_config
.with_use_fips(use_fips)
.with_use_dual_stack(use_dual_stack);
Ok(TestEnvironment { Ok(TestEnvironment {
base_dir: dir.into(), base_dir: dir.into(),
metadata, metadata,

View File

@ -204,7 +204,7 @@ impl Builder {
WebIdentityTokenCredentialsProvider { WebIdentityTokenCredentialsProvider {
source, source,
fs: conf.fs(), fs: conf.fs(),
sts_client: StsClient::from_conf(conf.sts_client_config().build()), sts_client: StsClient::new(&conf.client_config("STS")),
time_source: conf.time_source(), time_source: conf.time_source(),
} }
} }

View File

@ -0,0 +1,7 @@
{
"HOME": "/home",
"AWS_REGION": "us-west-2",
"AWS_PROFILE": "sso-test",
"AWS_USE_FIPS_ENDPOINT": "true",
"AWS_USE_DUALSTACK_ENDPOINT": "true"
}

View File

@ -0,0 +1,6 @@
[profile sso-test]
sso_start_url = https://ssotest.awsapps.com/start
sso_region = us-east-2
sso_account_id = 123456789
sso_role_name = MySsoRole
region = us-east-2

View File

@ -0,0 +1,5 @@
{
"accessToken": "a-token",
"expiresAt": "2080-10-16T03:56:45Z",
"startUrl": "https://ssotest.awsapps.com/start"
}

View File

@ -0,0 +1,93 @@
{
"events": [
{
"connection_id": 0,
"action": {
"Request": {
"request": {
"uri": "https://portal.sso-fips.us-east-2.api.aws/federation/credentials?account_id=123456789&role_name=MySsoRole",
"headers": {
"x-amz-sso_bearer_token": [
"a-token"
],
"Host": [
"portal.sso-fips.us-east-2.api.aws"
]
},
"method": "GET"
}
}
}
},
{
"connection_id": 0,
"action": {
"Eof": {
"ok": true,
"direction": "Request"
}
}
},
{
"connection_id": 0,
"action": {
"Response": {
"response": {
"Ok": {
"status": 200,
"version": "HTTP/1.1",
"headers": {
"Date": [
"Mon, 03 Jan 2022 19:13:54 GMT"
],
"Content-Type": [
"application/json"
],
"Content-Length": [
"144"
],
"Connection": [
"keep-alive"
],
"Access-Control-Expose-Headers": [
"RequestId"
],
"Cache-Control": [
"no-cache"
],
"RequestId": [
"b339b807-25d1-474c-a476-b070e9f350e4"
],
"Server": [
"AWS SSO"
]
}
}
}
}
}
},
{
"connection_id": 0,
"action": {
"Data": {
"data": {
"Utf8": "{\"roleCredentials\":{\"accessKeyId\":\"ASIARCORRECT\",\"secretAccessKey\":\"secretkeycorrect\",\"sessionToken\":\"tokencorrect\",\"expiration\":1234567890000}}"
},
"direction": "Response"
}
}
},
{
"connection_id": 0,
"action": {
"Eof": {
"ok": true,
"direction": "Response"
}
}
}
],
"docs": "Load SSO credentials",
"version": "V0"
}

View File

@ -0,0 +1,12 @@
{
"name": "e2e_fips_and_dual_stack_sso",
"docs": "end to end SSO test with FIPS and dual stack enabled",
"result": {
"Ok": {
"access_key_id": "ASIARCORRECT",
"secret_access_key": "secretkeycorrect",
"session_token": "tokencorrect",
"expiry": 1234567890
}
}
}

View File

@ -0,0 +1,3 @@
{
"HOME": "/home"
}

View File

@ -0,0 +1,9 @@
[default]
region = us-east-1
role_arn = arn:aws:iam::123456789:role/integration-test
source_profile = base
use_fips_endpoint = true
use_dualstack_endpoint = true
[profile base]
region = us-east-1

View File

@ -0,0 +1,3 @@
[base]
aws_access_key_id = AKIAFAKE
aws_secret_access_key = FAKE

View File

@ -0,0 +1,107 @@
{
"events": [
{
"connection_id": 0,
"action": {
"Request": {
"request": {
"uri": "https://sts-fips.us-east-1.api.aws/",
"headers": {
"content-type": [
"application/x-www-form-urlencoded"
],
"authorization": [
"AWS4-HMAC-SHA256 Credential=AKIAFAKE/20210810/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-user-agent, Signature=cd5cb2aa1d20717ca17692bcbda711797ae9eb8bb1130690b021b3952b7ae56e"
],
"user-agent": [
"aws-sdk-rust/0.1.0 os/macos lang/rust/1.55.0-nightly"
],
"content-length": [
"146"
],
"x-amz-date": [
"20210810T003833Z"
],
"host": [
"sts-fips.us-east-1.api.aws"
],
"x-amz-user-agent": [
"aws-sdk-rust/0.1.0 api/sts/0.0.14-alpha os/macos lang/rust/1.55.0-nightly"
]
},
"method": "POST"
}
}
}
},
{
"connection_id": 0,
"action": {
"Data": {
"data": {
"Utf8": "Action=AssumeRole&Version=2011-06-15&RoleArn=arn%3Aaws%3Aiam%3A%3A123456789%3Arole%2Fintegration-test&RoleSessionName=assume-role-provider-session"
},
"direction": "Request"
}
}
},
{
"connection_id": 0,
"action": {
"Eof": {
"ok": true,
"direction": "Request"
}
}
},
{
"connection_id": 0,
"action": {
"Response": {
"response": {
"Ok": {
"status": 200,
"version": "HTTP/1.1",
"headers": {
"date": [
"Thu, 05 Aug 2021 18:58:02 GMT"
],
"content-length": [
"1491"
],
"content-type": [
"text/xml"
],
"x-amzn-requestid": [
"c2e971c2-702d-4124-9b1f-1670febbea18"
]
}
}
}
}
}
},
{
"connection_id": 0,
"action": {
"Data": {
"data": {
"Utf8": "<AssumeRoleResponse xmlns=\"https://sts.amazonaws.com/doc/2011-06-15/\">\n <AssumeRoleResult>\n <AssumedRoleUser>\n <AssumedRoleId>AROARABCDEFGHIJKLMNOP:assume-role-provider-session</AssumedRoleId>\n <Arn>arn:aws:sts::123456789012:assumed-role/integration-test/assume-role-provider-session</Arn>\n </AssumedRoleUser>\n <Credentials>\n <AccessKeyId>ASIARTESTID</AccessKeyId>\n <SecretAccessKey>TESTSECRETKEY</SecretAccessKey>\n <SessionToken>TESTSESSIONTOKEN</SessionToken>\n <Expiration>2021-08-05T19:58:02Z</Expiration>\n </Credentials>\n </AssumeRoleResult>\n <ResponseMetadata>\n <RequestId>c2e971c2-702d-4124-9b1f-1670febbea18</RequestId>\n </ResponseMetadata>\n</AssumeRoleResponse>\n"
},
"direction": "Response"
}
}
},
{
"connection_id": 0,
"action": {
"Eof": {
"ok": true,
"direction": "Response"
}
}
}
],
"docs": "standard request / response with STS",
"version": "V0"
}

View File

@ -0,0 +1,12 @@
{
"name": "e2e_fips_and_dual_stack_sts",
"docs": "end to end STS role assumption test with FIPS and dual stack enabled",
"result": {
"Ok": {
"access_key_id": "ASIARTESTID",
"secret_access_key": "TESTSECRETKEY",
"session_token": "TESTSESSIONTOKEN",
"expiry": 1628193482
}
}
}