Skip to content

aws-config: make ProviderConfig::with_use_fips/with_use_dual_stack public#4551

Open
hligit wants to merge 1 commit intosmithy-lang:mainfrom
atlassian-forks:expose-provider-config-use-fips
Open

aws-config: make ProviderConfig::with_use_fips/with_use_dual_stack public#4551
hligit wants to merge 1 commit intosmithy-lang:mainfrom
atlassian-forks:expose-provider-config-use-fips

Conversation

@hligit
Copy link

@hligit hligit commented Feb 28, 2026

Motivation and Context

ProviderConfig::with_use_fips() and with_use_dual_stack() are currently pub(crate), which means external consumers that construct a ProviderConfig directly (via ProviderConfig::empty()) cannot propagate FIPS or dual-stack endpoint settings to credential providers.

This is a problem for applications that need FIPS-compliant STS endpoints when using DefaultCredentialsChain or AssumeRoleProvider with a custom ProviderConfig. The only workaround is setting AWS_ENDPOINT_URL_STS as a process-global environment variable, which is not thread-safe and affects all AWS clients in the process.

The gap:

The top-level ConfigLoader already exposes use_fips() and use_dual_stack() as public methods. ConfigLoader::load() reads AWS_USE_FIPS_ENDPOINT from the environment and calls ProviderConfig::with_use_fips() internally. But when constructing a ProviderConfig directly (e.g., to supply a custom HTTP client to DefaultCredentialsChain::builder().configure()), the use_fips field defaults to None and there is no public API to set it.

The consequence:

When a credential provider in the chain (e.g., WebIdentityTokenCredentialsProvider for EKS IRSA) needs to call STS, it builds an STS client from ProviderConfig::client_config(). If use_fips was never set, the STS client uses the non-FIPS endpoint — even if AWS_USE_FIPS_ENDPOINT=true is in the environment and all other AWS service clients use FIPS endpoints.

Real-world use case:

Applications that build their own HTTP client (e.g., one backed by rustls with aws-lc-rs for FIPS 140-3 compliance) and pass it to DefaultCredentialsChain via ProviderConfig currently have no way to ensure the internal STS calls also use FIPS endpoints:

let provider_config = ProviderConfig::empty()
    .with_region(Some(region))
    .with_http_client(my_fips_http_client)
    .with_use_fips(Some(true));       // <-- currently impossible (pub(crate))

let creds = DefaultCredentialsChain::builder()
    .configure(provider_config)
    .build()
    .await;

Description

  • Change ProviderConfig::with_use_fips() from pub(crate) to pub
  • Change ProviderConfig::with_use_dual_stack() from pub(crate) to pub
  • Add doc comments explaining the relationship to ConfigLoader and the use case

The diff is minimal (visibility change + doc comments only):

-    pub(crate) fn with_use_fips(mut self, use_fips: Option<bool>) -> Self {
+    pub fn with_use_fips(mut self, use_fips: Option<bool>) -> Self {

-    pub(crate) fn with_use_dual_stack(mut self, use_dual_stack: Option<bool>) -> Self {
+    pub fn with_use_dual_stack(mut self, use_dual_stack: Option<bool>) -> Self {

Testing

All 221 existing aws-config unit tests pass (run via cargo test --lib in aws/rust-runtime/aws-config). The one pre-existing failure (loader::test::provider_config_used) is an environment-dependent ANSI escape code matching issue unrelated to this change — it also fails on the unmodified parent commit.

The existing integration test e2e_fips_and_dual_stack_sso already exercises the FIPS endpoint resolution path through ConfigLoader.

The following standalone test demonstrates the gap this PR fills. It is included here for illustration rather than as an in-tree test, since once the methods are pub the test is trivially testing that a setter works:

#[test]
fn custom_provider_config_cannot_set_fips_without_public_api() {
    use aws_config::provider_config::ProviderConfig;
    use aws_types::region::Region;

    // Simulates what an application does when it needs a custom HTTP client:
    let config = ProviderConfig::empty()
        .with_region(Some(Region::new("us-east-1")));

    // use_fips is None — no public API to set it
    assert_eq!(config.use_fips(), None);

    // The SdkConfig used by internal STS clients defaults to non-FIPS
    let sdk_config = config.client_config();
    assert_eq!(sdk_config.use_fips(), Some(false));

    // ConfigLoader can set FIPS (it calls the pub(crate) method internally),
    // but ProviderConfig users cannot — this is the gap.
    //
    // The fix: make with_use_fips() and with_use_dual_stack() public:
    let config_with_fips = ProviderConfig::empty()
        .with_region(Some(Region::new("us-east-1")))
        .with_use_fips(Some(true));  // <-- requires pub

    let sdk_config = config_with_fips.client_config();
    assert_eq!(sdk_config.use_fips(), Some(true));
}

Checklist

  • For changes to the smithy-rs codegen or runtime crates, I have created a changelog entry Markdown file in the .changelog directory, specifying "client," "server," or both in the applies_to key.
  • For changes to the AWS SDK, generated SDK code, or SDK runtime crates, I have created a changelog entry Markdown file in the .changelog directory, specifying "aws-sdk-rust" in the applies_to key.

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

@hligit hligit requested a review from a team as a code owner February 28, 2026 12:03
…blic

`ProviderConfig::with_use_fips()` and `with_use_dual_stack()` are
currently `pub(crate)`, which means external consumers that construct
a `ProviderConfig` directly (via `ProviderConfig::empty()`) cannot
propagate FIPS or dual-stack endpoint settings to credential providers.

This is a problem for applications that need FIPS-compliant STS
endpoints when using `DefaultCredentialsChain` or
`AssumeRoleProvider` with a custom `ProviderConfig`. The only
workaround is setting `AWS_ENDPOINT_URL_STS` as a process-global
environment variable, which is not thread-safe and affects all AWS
clients in the process.

The top-level `ConfigLoader` already exposes `use_fips()` and
`use_dual_stack()` as public methods. `ConfigLoader::load()` reads
`AWS_USE_FIPS_ENDPOINT` from the environment and calls
`ProviderConfig::with_use_fips()` internally. But when constructing
a `ProviderConfig` directly (e.g., to supply a custom HTTP client to
`DefaultCredentialsChain::builder().configure()`), the `use_fips`
field defaults to `None` and there is no public API to set it.

When a credential provider in the chain (e.g.,
`WebIdentityTokenCredentialsProvider` for EKS IRSA) needs to call
STS, it builds an STS client from `ProviderConfig::client_config()`.
If `use_fips` was never set, the STS client uses the non-FIPS
endpoint — even if `AWS_USE_FIPS_ENDPOINT=true` is in the
environment and all other AWS service clients use FIPS endpoints.

Signed-off-by: Haitao Li <hli@atlassian.com>
@hligit hligit force-pushed the expose-provider-config-use-fips branch from cb4b605 to e141ffb Compare February 28, 2026 12:04
@hligit hligit requested a review from a team as a code owner February 28, 2026 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant