aws-sdk-java-v2
aws-sdk-java-v2 copied to clipboard
HOWTO: Assume Role With Saml in java sdk 2.x
This may be a bug, or a misunderstanding of how to use the 2.0 sdk to assume a role with SAML. In short, I would like to use Secure Token Service to authenticate with my Identity Provider and then once authenticated, assume a role in AWS to use when calling different services.
Expected Behavior
Currently, I am using the StsAssumeRoleWithSamlCredentialProvider. Once I configure the credential provider and give it a mechanism to get a valid saml assertion from my Identity Provider, I should be able to give that credential provider to any other service client so that it will use those credentials when making API calls. Those other services should NOT attempt to get credentials from any other sources ( this includes the DefaultCredentialProvider which looks in a myriad of places).
Current Behavior
When I pass the StsAssumeRoleWithSamlCredentialProvider to another service client ( such as CloudWatchLogsClient ), and initiate a request, the request fails with multiple "Unable to load credentials from ***" errors. Here is one of the stack traces:
[http-nio-8080-exec-1] DEBUG software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain - Unable to load credentials from SystemPropertyCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId). software.amazon.awssdk.core.exception.SdkClientException: Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId). at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:97) at software.amazon.awssdk.auth.credentials.internal.SystemSettingsCredentialsProvider.resolveCredentials(SystemSettingsCredentialsProvider.java:58) at software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain.resolveCredentials(AwsCredentialsProviderChain.java:91) at software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider.resolveCredentials(DefaultCredentialsProvider.java:92) at software.amazon.awssdk.awscore.client.handler.AwsClientHandlerUtils.createExecutionContext(AwsClientHandlerUtils.java:70) at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.createExecutionContext(AwsSyncClientHandler.java:68) at software.amazon.awssdk.core.client.handler.BaseSyncClientHandler.execute(BaseSyncClientHandler.java:68) at software.amazon.awssdk.core.client.handler.SdkSyncClientHandler.execute(SdkSyncClientHandler.java:44) at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.execute(AwsSyncClientHandler.java:55) at software.amazon.awssdk.services.sts.DefaultStsClient.assumeRoleWithSAML(DefaultStsClient.java:370) at software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithSamlCredentialsProvider.getUpdatedCredentials(StsAssumeRoleWithSamlCredentialsProvider.java:69) at software.amazon.awssdk.services.sts.auth.StsCredentialsProvider.updateSessionCredentials(StsCredentialsProvider.java:68) at software.amazon.awssdk.utils.cache.CachedSupplier.refreshCache(CachedSupplier.java:132) at software.amazon.awssdk.utils.cache.CachedSupplier.get(CachedSupplier.java:89) at software.amazon.awssdk.services.sts.auth.StsCredentialsProvider.resolveCredentials(StsCredentialsProvider.java:78) at software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithSamlCredentialsProvider.resolveCredentials(StsAssumeRoleWithSamlCredentialsProvider.java:42) at software.amazon.awssdk.awscore.client.handler.AwsClientHandlerUtils.createExecutionContext(AwsClientHandlerUtils.java:70) at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.createExecutionContext(AwsSyncClientHandler.java:68) at software.amazon.awssdk.core.client.handler.BaseSyncClientHandler.execute(BaseSyncClientHandler.java:68) at software.amazon.awssdk.core.client.handler.SdkSyncClientHandler.execute(SdkSyncClientHandler.java:44) at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.execute(AwsSyncClientHandler.java:55) at software.amazon.awssdk.services.cloudwatchlogs.DefaultCloudWatchLogsClient.getLogEvents(DefaultCloudWatchLogsClient.java:1836) at logs.GetAWSLogs.getLogs(GetAWSLogs.java:167)
What I think may be the cause of this issue is that the StsAssumeRoleWithSamlCredentialsProvider uses the StsClient to make the request. The default StsClient uses the DefaultCredentialsProvider to authenticate with AWS in order to make the request. You can override the credentials provider in the StsClient with the overrideConfiguration
method, but since the StsAssumeRoleWithSamlCredentialsProvider requires the StsClient, it seems that its a "chicken and egg" scenario. You can't configure the Sts..CredentialsProvider without an StsClient, but the StsClient needs a credentials provider itself...
Guidance on how to correctly assume a role, or if this is truly a bug, then a quick fix would be very much appreciated.
Steps to Reproduce (for bugs)
` ProxyConfiguration proxyConfig = null; try { proxyConfig = ProxyConfiguration .builder() .endpoint(new URI("http_proxy")) .build(); } catch (URISyntaxException e) { e.printStackTrace(); }
AWSSAMLAuthenticationConfiguration config = AWSSAMLAuthenticationConfiguration
.builder()
.userName(ad_user)
.userPass(ad_pass)
.roleName(aws_role)
.region(Region.US_EAST_1)
.proxyConfig(proxyConfig)
.build();
SdkHttpClient httpClient = ApacheHttpClient
.builder()
.proxyConfiguration(proxyConfig)
.build();
AssumeRoleWithSamlRequest request = AssumeRoleWithSamlRequest
.builder()
.durationSeconds(900)
.roleArn(ROLE_ARN)
.principalArn("PRINCIPAL_ARN")
.build();
SamlIdentityProviderServiceFactory factory = new SamlIdentityProviderServiceFactory();
SamlIdentityProviderService service = factory.getService(config);
Supplier<AssumeRoleWithSamlRequest> supplier = () ->
request.toBuilder()
.samlAssertion(service.getValidAssertion())
.build();
cp = StsAssumeRoleWithSamlCredentialsProvider
.builder()
.stsClient(StsClient.builder().region(Region.US_EAST_1).httpClient(httpClient).build())
.asyncCredentialUpdateEnabled(true)
.refreshRequest(supplier)
.build();
cw = CloudWatchLogsClient.builder()
.httpClient(httpClient)
.region(Region.US_EAST_1)
.credentialsProvider(cp)
.build();
GetLogEventsResponse logEventsResponse = cw.getLogEvents(GetLogEventsRequest.builder()
.logGroupName(logGroup)
.logStreamName(logStream)
.startFromHead(true)
.build());
`
Context
Without the ability to correctly assume a role via saml, we cannot migrate to the 2.0 version of the SDK.
Your Environment
- AWS Java SDK version used: 2.3.1
@dagnir @millems
Bumping to hopefully get traction...
Can you try configuring the STS client with an AnonymousCredentialsProvider
and let me know if that works for you?
So, I changed the following
cp = StsAssumeRoleWithSamlCredentialsProvider .builder() .stsClient(StsClient.builder().region(Region.US_EAST_1).httpClient(httpClient).build()) .asyncCredentialUpdateEnabled(true) .refreshRequest(supplier) .build();
to
cp = StsAssumeRoleWithSamlCredentialsProvider .builder() .stsClient( StsClient .builder() .region(Region.US_EAST_1) .httpClient(httpClient) .credentialsProvider(AnonymousCredentialsProvider.create()).build() ) .asyncCredentialUpdateEnabled(true) .refreshRequest(supplier) .build();
And instead of getting the previously mentioned error, I'm just getting a 403 error from the STS service.
software.amazon.awssdk.services.sts.model.StsException: Access denied (Service: Sts, Status Code: 403 ...
I had this problem too.
However, I can confirm that I can make it work with the latest version of the SDK, in my case version 2.15.26
, and with the use of the AnonymousCredentialsProvider
like this:
StsClient stsClient = StsClient.builder()
.credentialsProvider(software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider.create())
.build();
Without the use of the AnonymousCredentialsProvider
I get :
"SdkClientException: Unable to load credentials from any of the providers in the chain"
I don't think the required use of the AnonymousCredentialsProvider
is well-documented, but perhaps I missed something? In fact the Javadoc says this:
Calling AssumeRoleWithSAML does not require the use of AWS security credentials. The identity of the caller is validated by using keys in the metadata document that is uploaded for the SAML provider entity for your identity provider.
This sentence doesn't make it obvious that in fact you MUST use AnonymousCredentialsProvider
. You can even use a bogus credentials provider, it just needs to be there and return a non-null AwsCredentials
object .. which then won't be used anyway. Weird.
I had similar issue in my code I followed @lbruun solution and worked.
stsClient = StsClient.builder()
.credentialsProvider(AnonymousCredentialsProvider.create())
.region(region)
.build();
@mscharp
Can you please tell me which AWS Package supports below classes
AWSSAMLAuthenticationConfiguration
, SamlIdentityProviderServiceFactory
and SamlIdentityProviderService
I am trying to use your example for getting saml assertions before pass it to StsAssumeRoleWithSamlCredentialsProvider
I have a suggestion to document it via code. IF possible, there should be a special STS client that takes in the settings for SAML/Web identities then return a "normal" client that is pre-configured for that identity.
An alternative is to have a helper dedicated to the anonymous credentials provider, so it comes up when I type in "cred" on the STS client builder. Not perfect, but I can extrapolate from there, after getting the error once.
The anonymous credentials provider is the solution, but it is not easy to find.